master 9f9435307de4 cached
174 files
146.8 KB
45.3k tokens
131 symbols
1 requests
Download .txt
Repository: Armour/express-webpack-react-redux-typescript-boilerplate
Branch: master
Commit: 9f9435307de4
Files: 174
Total size: 146.8 KB

Directory structure:
gitextract_w64eli_i/

├── .appveyor.yml
├── .babelrc
├── .circleci/
│   └── config.yml
├── .commitlintrc.yml
├── .dockerignore
├── .editorconfig
├── .eslintignore
├── .eslintrc
├── .gitattributes
├── .github/
│   ├── CODE_OF_CONDUCT.md
│   ├── COMMIT_CONVENTION.md
│   ├── CONTRIBUTING.md
│   ├── ISSUE_TEMPLATE.md
│   ├── PULL_REQUEST_TEMPLATE.md
│   └── main.workflow
├── .gitignore
├── .stylelintignore
├── .stylelintrc
├── Dockerfile
├── LICENSE
├── README.md
├── __mocks__/
│   ├── fileMock.tsx
│   └── react-i18next.tsx
├── backend/
│   ├── config.json
│   ├── controllers/
│   │   └── notes.js
│   ├── db/
│   │   └── index.js
│   ├── jsconfig.json
│   ├── redis-data/
│   │   └── .gitkeep
│   ├── routes/
│   │   ├── index.js
│   │   └── notes.js
│   ├── server.js
│   └── sql/
│       ├── data.sql
│       └── schema.sql
├── docker-compose.yml
├── frontend/
│   ├── public/
│   │   ├── browserconfig.xml
│   │   ├── index.ejs
│   │   ├── locales/
│   │   │   ├── en/
│   │   │   │   ├── common.json
│   │   │   │   ├── homePage.json
│   │   │   │   ├── notFoundPage.json
│   │   │   │   ├── parallaxPage.json
│   │   │   │   └── reactPage.json
│   │   │   ├── jp/
│   │   │   │   ├── common.json
│   │   │   │   ├── homePage.json
│   │   │   │   ├── notFoundPage.json
│   │   │   │   ├── parallaxPage.json
│   │   │   │   └── reactPage.json
│   │   │   └── zh/
│   │   │       ├── common.json
│   │   │       ├── homePage.json
│   │   │       ├── notFoundPage.json
│   │   │       ├── parallaxPage.json
│   │   │       └── reactPage.json
│   │   ├── manifest.webmanifest
│   │   └── robots.txt
│   └── src/
│       ├── App.tsx
│       ├── components/
│       │   ├── ContentLoader/
│       │   │   ├── __tests__/
│       │   │   │   ├── __snapshots__/
│       │   │   │   │   └── contentLoader.spec.tsx.snap
│       │   │   │   └── contentLoader.spec.tsx
│       │   │   ├── contentLoader.scss
│       │   │   ├── contentLoader.tsx
│       │   │   └── index.tsx
│       │   ├── Dropdown/
│       │   │   ├── __tests__/
│       │   │   │   ├── __snapshots__/
│       │   │   │   │   └── dropdown.spec.tsx.snap
│       │   │   │   └── dropdown.spec.tsx
│       │   │   ├── dropdown.tsx
│       │   │   └── index.tsx
│       │   ├── Footer/
│       │   │   ├── __tests__/
│       │   │   │   ├── __snapshots__/
│       │   │   │   │   └── footer.spec.tsx.snap
│       │   │   │   └── footer.spec.tsx
│       │   │   ├── footer.tsx
│       │   │   └── index.tsx
│       │   └── Header/
│       │       ├── __tests__/
│       │       │   ├── __snapshots__/
│       │       │   │   └── header.spec.tsx.snap
│       │       │   └── header.spec.tsx
│       │       ├── header.scss
│       │       ├── header.tsx
│       │       └── index.tsx
│       ├── i18n/
│       │   └── index.tsx
│       ├── index.tsx
│       ├── pages/
│       │   ├── HomePage/
│       │   │   ├── __tests__/
│       │   │   │   ├── __snapshots__/
│       │   │   │   │   └── homePage.spec.tsx.snap
│       │   │   │   └── homePage.spec.tsx
│       │   │   ├── components/
│       │   │   │   ├── Carousel/
│       │   │   │   │   ├── __tests__/
│       │   │   │   │   │   ├── __snapshots__/
│       │   │   │   │   │   │   └── carousel.spec.tsx.snap
│       │   │   │   │   │   └── carousel.spec.tsx
│       │   │   │   │   ├── carousel.tsx
│       │   │   │   │   ├── constants/
│       │   │   │   │   │   └── carousel.tsx
│       │   │   │   │   └── index.tsx
│       │   │   │   ├── Pushpin/
│       │   │   │   │   ├── __tests__/
│       │   │   │   │   │   ├── __snapshots__/
│       │   │   │   │   │   │   └── pushpin.spec.tsx.snap
│       │   │   │   │   │   └── pushpin.spec.tsx
│       │   │   │   │   ├── index.tsx
│       │   │   │   │   ├── pushpin.scss
│       │   │   │   │   └── pushpin.tsx
│       │   │   │   └── TranslationButton/
│       │   │   │       ├── __tests__/
│       │   │   │       │   ├── __snapshots__/
│       │   │   │       │   │   └── translationButton.spec.tsx.snap
│       │   │   │       │   └── translationButton.spec.tsx
│       │   │   │       ├── index.tsx
│       │   │   │       └── translationButton.tsx
│       │   │   ├── homePage.scss
│       │   │   ├── homePage.tsx
│       │   │   └── index.tsx
│       │   ├── NotFoundPage/
│       │   │   ├── __tests__/
│       │   │   │   ├── __snapshots__/
│       │   │   │   │   └── notFoundPage.spec.tsx.snap
│       │   │   │   └── notFoundPage.spec.tsx
│       │   │   ├── index.tsx
│       │   │   ├── notFoundPage.scss
│       │   │   └── notFoundPage.tsx
│       │   ├── ParallaxPage/
│       │   │   ├── __tests__/
│       │   │   │   ├── __snapshots__/
│       │   │   │   │   └── parallaxPage.spec.tsx.snap
│       │   │   │   └── parallaxPage.spec.tsx
│       │   │   ├── components/
│       │   │   │   └── PrismCodes/
│       │   │   │       ├── __tests__/
│       │   │   │       │   ├── __snapshots__/
│       │   │   │       │   │   └── prismCodes.spec.tsx.snap
│       │   │   │       │   └── prismCodes.spec.tsx
│       │   │   │       ├── constants/
│       │   │   │       │   └── prismCodes.tsx
│       │   │   │       ├── index.tsx
│       │   │   │       ├── prismCodes.scss
│       │   │   │       └── prismCodes.tsx
│       │   │   ├── index.tsx
│       │   │   ├── parallaxPage.scss
│       │   │   └── parallaxPage.tsx
│       │   └── ReactPage/
│       │       ├── __tests__/
│       │       │   ├── __snapshots__/
│       │       │   │   └── reactPage.spec.tsx.snap
│       │       │   └── reactPage.spec.tsx
│       │       ├── components/
│       │       │   ├── FetchNote/
│       │       │   │   ├── __tests__/
│       │       │   │   │   ├── __snapshots__/
│       │       │   │   │   │   └── fetchNote.spec.tsx.snap
│       │       │   │   │   └── fetchNote.spec.tsx
│       │       │   │   ├── fetchNote.scss
│       │       │   │   ├── fetchNote.tsx
│       │       │   │   └── index.tsx
│       │       │   └── TodoLayout/
│       │       │       ├── __tests__/
│       │       │       │   ├── __snapshots__/
│       │       │       │   │   └── todoLayout.spec.tsx.snap
│       │       │       │   └── todoLayout.spec.tsx
│       │       │       ├── components/
│       │       │       │   ├── TodoFooter/
│       │       │       │   │   ├── __tests__/
│       │       │       │   │   │   ├── __snapshots__/
│       │       │       │   │   │   │   └── todoFooter.spec.tsx.snap
│       │       │       │   │   │   └── todoFooter.spec.tsx
│       │       │       │   │   ├── components/
│       │       │       │   │   │   └── TodoFilter/
│       │       │       │   │   │       ├── __tests__/
│       │       │       │   │   │       │   ├── __snapshots__/
│       │       │       │   │   │       │   │   └── todoFilter.spec.tsx.snap
│       │       │       │   │   │       │   └── todoFilter.spec.tsx
│       │       │       │   │   │       ├── index.tsx
│       │       │       │   │   │       ├── todoFilter.scss
│       │       │       │   │   │       └── todoFilter.tsx
│       │       │       │   │   ├── index.tsx
│       │       │       │   │   └── todoFooter.tsx
│       │       │       │   ├── TodoInput/
│       │       │       │   │   ├── __tests__/
│       │       │       │   │   │   ├── __snapshots__/
│       │       │       │   │   │   │   └── todoInput.spec.tsx.snap
│       │       │       │   │   │   └── todoInput.spec.tsx
│       │       │       │   │   ├── index.tsx
│       │       │       │   │   └── todoInput.tsx
│       │       │       │   └── TodoList/
│       │       │       │       ├── __tests__/
│       │       │       │       │   ├── __snapshots__/
│       │       │       │       │   │   └── todoList.spec.tsx.snap
│       │       │       │       │   └── todoList.spec.tsx
│       │       │       │       ├── components/
│       │       │       │       │   └── Todo/
│       │       │       │       │       ├── __tests__/
│       │       │       │       │       │   ├── __snapshots__/
│       │       │       │       │       │   │   └── todo.spec.tsx.snap
│       │       │       │       │       │   └── todo.spec.tsx
│       │       │       │       │       ├── index.tsx
│       │       │       │       │       ├── todo.scss
│       │       │       │       │       └── todo.tsx
│       │       │       │       ├── index.tsx
│       │       │       │       └── todoList.tsx
│       │       │       ├── index.tsx
│       │       │       ├── todoLayout.scss
│       │       │       └── todoLayout.tsx
│       │       ├── index.tsx
│       │       ├── reactPage.scss
│       │       └── reactPage.tsx
│       ├── reducers/
│       │   └── index.tsx
│       ├── router.tsx
│       ├── sagas/
│       │   └── index.tsx
│       ├── sass/
│       │   ├── global.scss
│       │   └── variables.scss
│       ├── services/
│       │   ├── notes/
│       │   │   ├── actions.tsx
│       │   │   ├── apis.tsx
│       │   │   ├── constants.tsx
│       │   │   ├── reducer.tsx
│       │   │   ├── sagas.tsx
│       │   │   └── types.d.ts
│       │   └── todos/
│       │       ├── __test__/
│       │       │   ├── actions.spec.tsx
│       │       │   └── reducer.spec.tsx
│       │       ├── actions.tsx
│       │       ├── constants.tsx
│       │       ├── reducer.tsx
│       │       └── types.d.ts
│       ├── store/
│       │   └── index.tsx
│       ├── types/
│       │   └── global.d.ts
│       └── utils/
│           └── index.tsx
├── package.json
├── tsconfig.json
├── tslint.json
├── webpack.config.base.babel.js
├── webpack.config.dev.babel.js
├── webpack.config.dll.babel.js
├── webpack.config.prod.babel.js
└── webpack.config.profile.babel.js

================================================
FILE CONTENTS
================================================

================================================
FILE: .appveyor.yml
================================================
environment:
  nodejs_version: "11"

install:
  - ps: Install-Product node $env:nodejs_version
  - yarn install

build_script:
  - yarn build

test_script:
  - yarn test

cache:
  - "%LOCALAPPDATA%\\Yarn"


================================================
FILE: .babelrc
================================================
{
  "presets": [
    "@babel/preset-env",
    "@babel/preset-react",
    "@babel/preset-typescript"
  ],
  "plugins": [
    [
      "@babel/plugin-proposal-class-properties",
      {
        "loose": true,
      }
    ],
    "@babel/plugin-syntax-dynamic-import",
    "@babel/plugin-transform-runtime",
    "react-hot-loader/babel",
    [
      "prismjs",
      {
        "languages": [
          "javascript",
          "css",
          "markup",
          "cpp",
          "bash",
          "docker",
          "go",
          "json",
          "markdown",
          "python",
          "jsx",
          "tsx",
          "scss",
          "typescript"
        ],
        "plugins": [
          "autolinker",
          "command-line"
        ],
        "theme": "default",
        "css": true
      }
    ]
  ]
}


================================================
FILE: .circleci/config.yml
================================================
# Javascript Node CircleCI 2.0 configuration file
#
# Check https://circleci.com/docs/2.0/language-javascript/ for more details
#

version: 2

jobs:
  build:
    working_directory: ~
    docker:
      - image: circleci/node:11
    steps:
      - checkout
      - run:
          name: Install Docker client
          command: |
            set -x
            VER="18.09.1"
            curl -L -o /tmp/docker.tgz https://download.docker.com/linux/static/stable/x86_64/docker-$VER.tgz
            tar -xz -C /tmp -f /tmp/docker.tgz
            sudo cp -r /tmp/docker/* /usr/bin
      - run:
          name: Install Docker Compose
          command: |
            set -x
            VER="1.23.2"
            curl -L https://github.com/docker/compose/releases/download/$VER/docker-compose-`uname -s`-`uname -m` > /tmp/docker-compose
            sudo cp /tmp/docker-compose /usr/local/bin/docker-compose
            sudo chmod +x /usr/local/bin/docker-compose
      - setup_remote_docker
      - run:
          name: Run Docker Compose
          command: docker-compose up --build -d
      - run:
          name: Install dependencies
          command: yarn install
      - run:
          name: Run test
          command: yarn test


================================================
FILE: .commitlintrc.yml
================================================
extends: ['armour']


================================================
FILE: .dockerignore
================================================
# General
.DS_Store
*~
*.swp
*.log

# Thumbnails
._*
Thumbs.db

# Trash folder or files
.Trashes
.Trash-*

# Folder config file
[Dd]esktop.ini

# Recycle Bin used on file shares
$RECYCLE.BIN/

# Editor config folder
.idea/
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json

# Code coverage
coverage/
.coveralls.yml

# Misc
__mocks__/
.circleci/
.github/
node_modules/
dist/
.appveyor.yml
.*lintrc*
.*lintignore*
yarn.lock


================================================
FILE: .editorconfig
================================================
# http://editorconfig.org

# top-most EditorConfig file
root = true

# Unix-style newlines with a newline ending every file
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true

# Trailing space override for markdown file
[*.md]
trim_trailing_whitespace = false

# Indentation override for js(x), ts(x) files
[*.{js,jsx,ts,tsx}]
indent_size = 2
indent_style = space

# Indentation override for css files
[*.{css,styl,scss,less,sass}]
indent_size = 2
indent_style = space

# Indentation override for html files
[*.html]
indent_size = 2
indent_style = space

# Indentation override for config files
[*.{json,yml}]
indent_size = 2
indent_style = space

# Minified JavaScript files shouldn't be changed
[**.min.js]
indent_style = ignore
insert_final_newline = ignore


================================================
FILE: .eslintignore
================================================
node_modules/
dist/
coverage/


================================================
FILE: .eslintrc
================================================
{
  "env": {
    "browser": true,
    "node": true,
    "es6": true
  },
  "extends": [
    "airbnb"
  ],
  "rules": {
    "import/export": "error",
    "import/first": "error",
    "import/no-commonjs": "error",
    "import/no-deprecated": "error",
    "import/no-duplicates": "error",
    "import/no-unresolved": "error",
    "import/no-webpack-loader-syntax": "error",
    "linebreak-style": "off",
    "indent": [
      "error",
      2
    ],
    "no-console": [
      "error",
      {
        "allow": [
          "warn",
          "error",
          "info"
        ]
      }
    ],
    "quotes": [
      "error",
      "single",
      {
        "allowTemplateLiterals": true
      }
    ]
  }
}


================================================
FILE: .gitattributes
================================================
# Details per file setting:
#   text    These files should be normalized (i.e. convert CRLF to LF).
#   binary  These files are binary and should be left untouched.

# Auto detect text files and perform LF normalization
* text=auto
# The above will handle all files NOT found below

# SOURCE CODE
*.bat      text eol=crlf
*.css      text
*.js       text
*.jsx      text
*.ejs      text
*.html     text
*.less     text
*.sass     text
*.scss     text
*.sh       text eol=lf
*.sql      text
*.ts       text
*.tsx      text

# DOCKER
*.dockerignore    text
Dockerfile        text

# COMPILED FILES
*.dll   binary
*.dylib binary
*.exe   binary
*.so    binary

# DOCUMENTATION
*.md         text
*.txt        text
LICENSE      text

# CONFIGS
.editorconfig  text
.gitattributes text
.gitignore     text
*.conf         text
*.config       text
*.json         text
*.lock         text
*.toml         text
*.yaml         text
*.yml          text

# GRAPHICS
*.ai   binary
*.bmp  binary
*.eps  binary
*.gif  binary
*.heic binary
*.ico  binary
*.jpeg binary
*.jpg  binary
*.pdf  binary
*.png  binary
*.psb  binary
*.psd  binary
*.svg  text
*.tif  binary
*.tiff binary
*.wbmp binary
*.webp binary

# AUDIO
*.aac  binary
*.flac binary
*.m4a  binary
*.mid  binary
*.midi binary
*.mp3  binary
*.mp4a binary
*.mpga binary
*.oga  binary
*.ogg  binary
*.wav  binary
*.weba binary

# VIDEO
*.3g2  binary
*.3gp  binary
*.asf  binary
*.avi  binary
*.f4v  binary
*.fla  binary
*.flv  binary
*.m4v  binary
*.mov  binary
*.mp4  binary
*.mpeg binary
*.mpg  binary
*.ogv  binary
*.qt   binary
*.swf  binary
*.webm binary

# ARCHIVES
*.7z  binary
*.gz  binary
*.jar binary
*.rar binary
*.tar binary
*.zip binary

# FONTS
*.eot   binary
*.otf   binary
*.ttf   binary
*.woff  binary
*.woff2 binary


================================================
FILE: .github/CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct

## Our Pledge

In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, gender identity and expression, level of experience,
education, socio-economic status, nationality, personal appearance, race,
religion, or sexual identity and orientation.

## Our Standards

Examples of behavior that contributes to creating a positive environment
include:

* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members

Examples of unacceptable behavior by participants include:

* The use of sexualized language or imagery and unwelcome sexual attention or
  advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
  address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
  professional setting

## Our Responsibilities

Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.

Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.

## Scope

This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.

## Enforcement

Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at [INSERT EMAIL ADDRESS]. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.

Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.

## Attribution

This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available 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)

[homepage]: https://www.contributor-covenant.org


================================================
FILE: .github/COMMIT_CONVENTION.md
================================================
# Git Commit Message Convention

> This is adapted from [Angular's commit convention](https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/conventional-changelog-angular).

## TL;DR

Messages must be matched by the following regex:

``` js
/^(Revert: )?(Feature|Fix|Docs|Improve|Config|Example|Refactor|Style|Test|Build|CI)(\(.+\))?: .{1,80}/
```

## Commit Message Format

Each 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**:

```text
<type>(<scope>): <subject>
<BLANK LINE>
<body>
<BLANK LINE>
<footer>
```

The **header** is mandatory and the **scope** of the header is optional.

Any line of the commit message cannot be longer 100 characters! This allows the message to be easier
to read on GitHub as well as in various git tools.

The footer should contain a [closing reference to an issue](https://help.github.com/articles/closing-issues-via-commit-messages/) if any.

Samples: (even more [samples](https://github.com/Armour/express-webpack-react-redux-typescript-boilerplate/commits/master))

```text
Docs(changelog): update changelog to beta.5
```

```text
Feature($browser): onUrlChange event (popstate/hashchange/polling)

Added new event to $browser:
- forward popstate event if available
- forward hashchange event if popstate not available
- do polling when neither popstate nor hashchange available

Breaks $browser.onHashChange, which was removed (use onUrlChange instead)
```

```text
Fix(release): need to depend on latest rxjs and zone.js

The version in our package.json gets copied to the one we publish, and users need the latest of these.

Closes #123, #245, #992

BREAKING CHANGE: isolate scope bindings definition has changed and
the inject option for the directive controller injection was removed.

To migrate the code follow the example below:

Before:

scope: {
  myAttr: 'attribute',
  myBind: 'bind',
  myExpression: 'expression',
  myEval: 'evaluate',
  myAccessor: 'accessor'
}

After:

scope: {
  myAttr: '@',
  myBind: '@',
  myExpression: '&',
  // myEval - usually not useful, but in cases where the expression is assignable, you can use '='
  myAccessor: '=' // in directive's template change myAccessor() to myAccessor
}

The removed `inject` wasn't generaly useful for directives so there should be no code using it.
```

### Revert

If 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.

### Type

Must be one of the following:

* **Build**: Changes that affect the build system or external dependencies (example scopes: gulp, npm, yarn)
* **CI**: Changes to CI related configuration files and scripts (example scopes: travis, circle, browserstack)
* **Config**: Changes to other configuration files (example scopes: webpack, babel, docker)
* **Docs**: Documentation only changes (example scopes: readme, changelog)
* **Example**: Changes for example code
* **Feature**: A new feature
* **Fix**: A bug fix
* **Improve**: Backwards-compatible enhancement changes
* **Refactor**: A code change that neither fixes a bug nor adds a feature
* **Style**: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
* **Test**: Changes for testing code

If 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.

### Scope

The scope could be anything specifying place of the commit change. For example `core`, `compiler`, `ssr`, `v-model`, `transition` etc...

### Subject

The subject contains succinct description of the change:

* use the imperative, present tense: "change" not "changed" nor "changes"
* don't capitalize the first letter
* no dot (.) at the end

### Body

Just as in the **subject**, use the imperative, present tense: "change" not "changed" nor "changes".
The body should include the motivation for the change and contrast this with previous behavior.

### Footer

The footer should contain any information about **Breaking Changes** and is also the place to
reference GitHub issues that this commit **Closes**.

**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.

A detailed explanation can be found in this [document](https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit)


================================================
FILE: .github/CONTRIBUTING.md
================================================
# Contributing

## Code of Conduct

Help 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).

## Found a Bug

If 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.

## Missing a Feature

You 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:

* 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.

* **Small Features** can be crafted and directly [submitted as a Pull Request](#submitting-a-pull-request).

## Submission Guidelines

### Submitting an Issue

Before 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.

We 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.

We 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.

Unfortunately, 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.

You can file new issues by filling out the [new issue form](https://github.com/Armour/express-webpack-react-redux-typescript-boilerplate/issues/new).

### Submitting a Pull Request

Before you submit your Pull Request (PR) consider the following guidelines:

1. 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.

1. Fork this repo.

1. Make your changes in a new git branch.

    ```shell
    git checkout -b my-new-feature master
    ```

1. 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.

    ```shell
    git commit -am 'Add some feature'
    ```

1. Push your branch.

    ```shell
    git push origin my-new-feature
    ```

1. Send a pull request :D

That's it! Thank you for your contribution!

#### After your pull request is merged

After your pull request is merged, you can safely delete your branch and pull the changes
from the main (upstream) repository:

* Delete the remote branch on GitHub either through the GitHub web UI or your local shell as follows:

    ```shell
    git push origin --delete my-new-feature
    ```

* Check out the master branch:

    ```shell
    git checkout master -f
    ```

* Delete the local branch:

    ```shell
    git branch -D my-new-feature
    ```

* Update your master with the latest upstream version:

    ```shell
    git pull
    ```

## Commit Message Convention

We 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**.

Please read and follow the [Commit Message Format](https://github.com/Armour/express-webpack-react-redux-typescript-boilerplate/blob/master/.github/COMMIT_CONVENTION.md).


================================================
FILE: .github/ISSUE_TEMPLATE.md
================================================
<!--
PLEASE HELP US PROCESS GITHUB ISSUES FASTER BY PROVIDING THE FOLLOWING INFORMATION.

ISSUES MISSING IMPORTANT INFORMATION MAY BE CLOSED WITHOUT INVESTIGATION.
-->

<!-- Please search GitHub for a similar issue or PR before submitting -->

# I'm submitting a

<!-- 
E.g.
    bug report,
    feature request,
    performance issue,
    regression (a behavior that used to work and stopped working in a new release),
    documentation issue or request,
    or others... (please describe)
-->

## Current behavior

<!-- Describe how the issue manifests. -->

## Expected behavior

<!-- Describe what the desired behavior would be. -->

## Minimal reproduction of the problem with instructions

<!--
For bug reports please provide the *STEPS TO REPRODUCE* and if possible a *MINIMAL DEMO* of the problem via github repo or similar tools.
-->

## What is the motivation / use case for changing the behavior

<!-- Describe the motivation or the concrete use case. -->

## Environment

<!-- Anything may be useful?  Platform, Operating system version, IDE, package manager, HTTP server, ... -->


================================================
FILE: .github/PULL_REQUEST_TEMPLATE.md
================================================
<!--
Please make sure to read the Pull Request Guidelines:
https://github.com/Armour/express-webpack-react-redux-typescript-boilerplate/blob/master/.github/CONTRIBUTING.md#submitting-a-pull-request
-->

<!-- PULL REQUEST TEMPLATE -->

**Make sure the PR fulfills these requirements:**

- 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)

- 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)

- If this PR introduce a **breaking change**, please describe the impact and migration path for existing applications

**What kind of change does this PR introduce?**

<!--
E.g.
    bugfix,
    feature,
    code style update,
    refactor,
    build-related changes,
    or others... (please describe)
-->

**More information:**


================================================
FILE: .github/main.workflow
================================================
workflow "Deploy on Heroku" {
  on = "push"
  resolves = [
    "verify",
  ]
}

# Login
action "login" {
  uses = "actions/heroku@master"
  args = "container:login"
  secrets = ["HEROKU_API_KEY"]
}

# Push
action "push" {
  needs = ["login"]
  uses = "actions/heroku@master"
  args = ["container:push", "--app", "$HEROKU_APP", "web"]
  secrets = ["HEROKU_API_KEY"]
  env = {
    HEROKU_APP = "express-react-typescript"
  }
}

# Release
action "deploy" {
  needs = ["push"]
  uses = "actions/heroku@master"
  args = ["container:release", "--app", "$HEROKU_APP", "web"]
  secrets = ["HEROKU_API_KEY"]
  env = {
    HEROKU_APP = "express-react-typescript"
  }
}

# Verify
action "verify" {
  needs = ["deploy"]
  uses = "actions/heroku@master"
  args = ["apps:info", "$HEROKU_APP"]
  secrets = ["HEROKU_API_KEY"]
  env = {
    HEROKU_APP = "express-react-typescript"
  }
}


================================================
FILE: .gitignore
================================================
# General
.DS_Store
*~
*.swp
*.log

# Thumbnails
._*
Thumbs.db

# Trash folder or files
.Trashes
.Trash-*

# Folder config file
[Dd]esktop.ini

# Recycle Bin used on file shares
$RECYCLE.BIN/

# Editor config folder
.idea/
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json

# Code coverage
coverage/
.coveralls.yml

# Misc
node_modules/
dist/

# Redis data
backend/redis-data/*
!backend/redis-data/.gitkeep


================================================
FILE: .stylelintignore
================================================
node_modules/
dist/
coverage/


================================================
FILE: .stylelintrc
================================================
{
  "extends": "stylelint-config-standard"
}


================================================
FILE: Dockerfile
================================================
FROM node:alpine

ARG PORT=${PORT}

WORKDIR /usr/src/app
COPY . /usr/src/app/

RUN apk add --no-cache --update make gcc libc-dev libpng-dev automake autoconf libtool nasm
RUN yarn install
RUN yarn build

EXPOSE ${PORT}

CMD ["yarn", "serve"]


================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2017 Chong Guo

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
# express-webpack-react-redux-typescript-boilerplate

[![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)
[![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)
[![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)
[![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)
[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat)](http://makeapullrequest.com)
[![Tested with jest](https://img.shields.io/badge/tested_with-jest-99424f.svg)](https://github.com/facebook/jest)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
[![Template from jarvis](https://img.shields.io/badge/Hi-Jarvis-ff69b4.svg)](https://github.com/Armour/Jarvis)

## Example

* [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)

  ![Home Page](https://user-images.githubusercontent.com/5276065/44188928-402d7800-a0d5-11e8-8445-0c0dece815c2.png)

  ![React Page](https://user-images.githubusercontent.com/5276065/44188929-402d7800-a0d5-11e8-8580-f9a330765f6a.png)

  ![404 Page](https://user-images.githubusercontent.com/5276065/44188930-402d7800-a0d5-11e8-919c-a4baa2c969ab.png)

## Stack

* [x] [yarn](https://github.com/yarnpkg/yarn) - dependency manager
* [x] [express](http://expressjs.com/) - node.js web framework for backend
* [x] [postgresql](https://www.postgresql.org/) - advanced open source database
* [x] [materialize](http://materializecss.com/) - a modern responsive front-end framework based on Material Design
* [x] [sass](https://github.com/sass/sass) - CSS pre-processors
* [x] [postcss](https://github.com/postcss/postcss) - CSS post-processor
* [x] [css-modules](https://github.com/css-modules/css-modules) - for default scoped/local css
* [x] [typescript](https://github.com/Microsoft/TypeScript) - a typed superset of javascript that scales
* [x] [webpack 4](https://github.com/webpack/webpack) - module bundler
* [x] [webpack-dev-server](https://github.com/webpack/webpack-dev-server) - serves a webpack app in development mode with hot reload
* [x] followed [ES6 standard](https://github.com/lukehoban/es6features)
* [x] [babel](https://babeljs.io/) - a JavaScript compiler that compile ES6 to ES5
* [x] [react](https://facebook.github.io/react/) - a JavaScript library for building user interfaces
* [x] [react-hot-loader 4](https://github.com/gaearon/react-hot-loader) - hot module reload!
* [x] [react-router 4](https://github.com/ReactTraining/react-router) - declarative routing for React
* [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)
* [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)
* [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)
* [x] [react-i18next](https://github.com/i18next/react-i18next) - internationalization for react done right
* [x] [immutable.js](https://github.com/facebook/immutable-js/) - persistent Immutable data structures for react redux state management
* [x] [editorconfig](http://editorconfig.org/) - maintain consistent coding styles between different editors and IDEs
* [x] [eslint](http://eslint.org/) - lint javascript files (.js, .jsx)
* [x] [tslint](https://palantir.github.io/tslint/) - lint typescript files (.ts, .tsx)
* [x] [stylelint](https://stylelint.io/) - lint style files (.css, .scss)
* [x] [commitlint](https://github.com/marionebl/commitlint) - lint git commit messages
* [x] [jest](https://facebook.github.io/jest/) - painless javascript testing
* [x] [coveralls](https://coveralls.io/) - test coverage
* [x] [husky](https://github.com/typicode/husky) - git hooks
* [x] [circle-ci 2](https://circleci.com/) - continuous integration tool for testing and deployment
* [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.
* [x] [docker](https://github.com/docker/docker) - the open-source application container engine
* [x] [RESTful API design](https://docs.microsoft.com/en-us/azure/architecture/best-practices/api-design) - follow RESTful api design best practice

## How to run the sample code

### Prerequisite

* `yarn` or `npm`
* (optional) `docker` with `docker-compose`

### Quickest way

The easiest way to run the example project is to use `docker-compose`:

```bash
docker-compose up --build
```

that's it :)

You can also follow instructions below if you want to customize it.

### Install project dependencies

Go to project root directory:

```bash
yarn install
```

If you find permission problem when trying to install yarn globally, check [this](https://github.com/yarnpkg/yarn/issues/1060#issuecomment-268160528) out.

### Setup database and session store

You can either

* setup `postgresql` and `redis` using docker images:

```bash
docker-compose up -d postgres redis
```

or

* maintain it by yourself, if so, make sure you set the right config in `backend/config.json`.

### Build & Run

On development (with hot reload):

```bash
yarn dev
```

On production (with [terser](https://github.com/terser-js/terser) and other optimazitions):

```bash
yarn prod
```

## Profile assets bundle

Thanks 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/).

```bash
yarn profile
```

## Run test

```bash
yarn test
```

## Code coverage

```bash
yarn coverage
```

## Deployment

Every push on master branch will trigger [Github Actions](.github/main.workflow) for heroku deployment.

## Contributing

See [CONTRIBUTING.md](https://github.com/Armour/express-webpack-react-redux-typescript-boilerplate/blob/master/.github/CONTRIBUTING.md)

## License

[MIT License](https://github.com/Armour/express-webpack-react-redux-typescript-boilerplate/blob/master/LICENSE)


================================================
FILE: __mocks__/fileMock.tsx
================================================
export default 'test-file-stub';


================================================
FILE: __mocks__/react-i18next.tsx
================================================
export const Translation = ({ children }: any) => children((k: string) => k, { i18n: {} });


================================================
FILE: backend/config.json
================================================
{
  "pgsql_hostname_dev": "localhost",
  "pgsql_hostname_prod": "postgres",
  "pgsql_username": "docker",
  "pgsql_password": "docker",
  "pgsql_database": "docker",
  "redis_hostname_dev": "localhost",
  "redis_hostname_prod": "redis",
  "redis_port": "6379",
  "http_port": "3003"
}


================================================
FILE: backend/controllers/notes.js
================================================
import status from 'http-status';

import db from '../db';

export const getAllNotes = async (req, res) => {
  try {
    const { rows: notes } = await db.query('SELECT * FROM notes ORDER BY id', []);
    return res.status(status.OK).json({
      data: {
        notes,
      },
      error: null,
    });
  } catch (e) {
    return res.status(status.INTERNAL_SERVER_ERROR).json({
      data: null,
      error: {
        message: e.message,
        // You can also add below fields for better error displaying
        //   code - error code for your project
        //   developerMessage - debug message for developers
      },
    });
  }
};

export const getNote = async (req, res) => {
  try {
    const { id } = req.params;
    const { rows: notes } = await db.query('SELECT * FROM notes WHERE id = $1', [id]);
    if (notes === undefined || notes.length === 0) {
      return res.status(status.NOT_FOUND).json({
        data: null,
        error: {
          message: `Note with id ${id} not found.`,
        },
      });
    }
    return res.status(status.OK).json({
      data: {
        note: notes[0],
      },
      error: null,
    });
  } catch (e) {
    return res.status(status.INTERNAL_SERVER_ERROR).json({
      data: null,
      error: {
        message: e.message,
      },
    });
  }
};

export const addNote = async (req, res) => {
  try {
    const { content } = req.body;
    const { rows: notes } = await db.query('INSERT INTO notes(content) VALUES($1) RETURNING *', [content]);
    if (notes === undefined || notes.length === 0) {
      return res.status(status.NOT_FOUND).json({
        data: null,
        error: {
          message: 'Insert note failed.',
        },
      });
    }
    return res.status(status.OK).json({
      data: {
        note: notes[0],
      },
      error: null,
    });
  } catch (e) {
    return res.status(status.INTERNAL_SERVER_ERROR).json({
      data: null,
      error: {
        message: e.message,
      },
    });
  }
};

export const editNote = async (req, res) => {
  try {
    const { id } = req.params;
    const { content } = req.body;
    const { rows: notes } = await db.query('UPDATE notes SET content = $1 WHERE id = $2 RETURNING *', [content, id]);
    if (notes === undefined || notes.length === 0) {
      return res.status(status.NOT_FOUND).json({
        data: null,
        error: {
          message: `Update note with id ${id} failed.`,
        },
      });
    }
    return res.status(status.OK).json({
      data: {
        note: notes[0],
      },
      error: null,
    });
  } catch (e) {
    return res.status(status.INTERNAL_SERVER_ERROR).json({
      data: null,
      error: {
        message: e.message,
      },
    });
  }
};

export const removeNote = async (req, res) => {
  try {
    const { id } = req.params;
    const { rows: notes } = await db.query('DELETE FROM notes WHERE id = $1 RETURNING *', [id]);
    if (notes === undefined || notes.length === 0) {
      return res.status(status.NOT_FOUND).json({
        data: null,
        error: {
          message: `Delete note with id ${id} failed.`,
        },
      });
    }
    return res.status(status.OK).json({
      data: {
        note: notes[0],
      },
      error: null,
    });
  } catch (e) {
    return res.status(status.INTERNAL_SERVER_ERROR).json({
      data: null,
      error: {
        message: e.message,
      },
    });
  }
};


================================================
FILE: backend/db/index.js
================================================
import { Pool } from 'pg';

import config from '../config.json';

const isProduction = process.env.NODE_ENV === 'production';

let pool;

// Create a postgresql connection pool
if (process.env.DATABASE_URL !== undefined) {
  // For Heroku Postgres deployment
  // See https://devcenter.heroku.com/articles/heroku-postgresql#connecting-in-node-js
  pool = new Pool({
    connectionString: process.env.DATABASE_URL,
    ssl: true,
  });
} else {
  // All config is optional, the environment variables will be used if the config is not present
  pool = new Pool({
    host: isProduction
      ? config.pgsql_hostname_prod
      : config.pgsql_hostname_dev, // Default: process.env.PGHOST
    database: config.pgsql_database, // Default: process.env.PGDATABASE
    user: config.pgsql_username, // Default: process.env.PGUSER
    password: config.pgsql_password, // Default: process.env.PGPASSWORD
    port: config.pgsql_port, // Default: process.env.PGPORT
  });
}

// Initializes a connection pool
export default {
  query: (text, params) => pool.query(text, params),
  pool,
};


================================================
FILE: backend/jsconfig.json
================================================
{
  "compilerOptions": {
    "baseUrl": "."
  }
}


================================================
FILE: backend/redis-data/.gitkeep
================================================


================================================
FILE: backend/routes/index.js
================================================
import express from 'express';

import notesRtr from './notes';

const router = express.Router();

router.use('/notes', notesRtr);

export default router;


================================================
FILE: backend/routes/notes.js
================================================
import express from 'express';

import {
  getAllNotes, getNote, addNote, editNote, removeNote,
} from '../controllers/notes';

const router = express.Router();

router.get('/', getAllNotes);
router.get('/:id', getNote);
router.post('/', addNote);
router.put('/:id', editNote);
router.delete('/:id', removeNote);

export default router;


================================================
FILE: backend/server.js
================================================
import express from 'express';
import path from 'path';
import logger from 'morgan';
import cookieParser from 'cookie-parser';
import bodyParser from 'body-parser';
import compression from 'compression';
import helmet from 'helmet';
import session from 'express-session';
import connectRedis from 'connect-redis';
import 'colors';

import config from './config.json';
import router from './routes/index';

const app = express();
const isProduction = process.env.NODE_ENV === 'production';
const port = process.env.PORT || config.http_port;
const RedisStore = connectRedis(session);
const server = require('http').createServer(app);

if (isProduction) {
  app.use(helmet());
  app.disable('x-powered-by');
  app.use(logger('combined'));
  app.set('trust proxy', 1);
} else {
  app.use(logger('dev'));
}

app.use(compression());
app.use(bodyParser.json({
  limit: '20mb',
}));
app.use(bodyParser.urlencoded({
  limit: '20mb',
  extended: true,
}));
app.use(cookieParser());
app.use(session({
  store: new RedisStore({
    host: isProduction ? config.redis_hostname_prod : config.redis_hostname_dev,
    port: config.redis_port,
  }),
  secret: 'keyboard cat',
  saveUninitialized: false,
  resave: false,
  cookie: {
    httpOnly: !isProduction,
    secure: isProduction,
  },
}));

// api router
app.use('/api/v1', router);

if (isProduction) {
  // Serve dist files if is production mode
  const distPath = path.resolve(__dirname, '../frontend/dist/prod');
  app.use(express.static(distPath));
  app.get('*', (_, res) => {
    res.sendFile(`${distPath}/index.html`);
  });
} else {
  // Return 404 for non-exist api
  app.get('*', (req, res) => {
    res.status(404).send(`Api not exist for ${req.url}`);
  });
}

// Start server listen on specific port
server.listen(port, (error) => {
  if (error) {
    console.error(`\n${error}`);
    server.close();
    process.exit(1);
  }
  if (isProduction) {
    console.info(`\nExpress: Listening on port ${port}, open up http://localhost:${port}/ in your broswer!\n`.green);
  } else {
    console.info(`\nExpress: Serve api on http://localhost:${port}/ \n`);
  }
});

const stopHandler = (signal) => {
  console.error(`\nExit process in responding to %s`.red, signal);
  server.close();
  process.exit(1);
};

process.on('SIGTERM', stopHandler, 'SIGTERM');
process.on('SIGINT', stopHandler, 'SIGINT');
process.on('SIGHUP', stopHandler, 'SIGINT');


================================================
FILE: backend/sql/data.sql
================================================
INSERT INTO notes (content) VALUES ('note data 1');
INSERT INTO notes (content) VALUES ('note data 2');


================================================
FILE: backend/sql/schema.sql
================================================
Create Table notes (
    id serial primary key,
    content text not null,
    created timestamptz default now()
);


================================================
FILE: docker-compose.yml
================================================
version: '3.7'

services:
  web:
    image: cguo/express-webpack-react-redux-typescript-boilerplate
    build: # ignored when deploying a stack in swarm mode or kubernetes
      context: .
      args:
        - PORT=${PORT}
    ports:
      - "${PORT}:${PORT}"
    depends_on: # ignored when deploying a stack in swarm mode or kubernetes
      - postgres
      - redis
    restart: always # ignored when deploying a stack in swarm mode or kubernetes
    deploy: # ignored by docker-compose
      replicas: 3
      restart_policy:
        condition: on-failure

  postgres:
    image: postgres:alpine
    volumes:
      - ./backend/sql/schema.sql:/docker-entrypoint-initdb.d/1-schema.sql
      - ./backend/sql/data.sql:/docker-entrypoint-initdb.d/2-data.sql
    environment:
      - POSTGRES_DB=${POSTGRES_DB}
      - POSTGRES_USER=${POSTGRES_USER}
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
    ports:
      - "5432:5432"
    restart: always # ignored when deploying a stack in swarm mode or kubernetes
    deploy: # ignored by docker-compose
      restart_policy:
        condition: on-failure

  redis:
    image: redis:alpine
    ports:
      - "6379:6379"
    volumes:
      - ./backend/redis-data:/data
    restart: always # ignored when deploying a stack in swarm mode or kubernetes
    deploy: # ignored by docker-compose
      restart_policy:
        condition: on-failure
    command: redis-server --appendonly yes


================================================
FILE: frontend/public/browserconfig.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<browserconfig>
    <msapplication>
        <tile>
            <square150x150logo src="/mstile-150x150.png"/>
            <TileColor>#000000</TileColor>
        </tile>
    </msapplication>
</browserconfig>


================================================
FILE: frontend/public/index.ejs
================================================
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta http-equiv="x-ua-compatible" content="ie=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
  <meta name="description" content="Full-stack boilerplate that using express with webpack, react and typescript!">
  <meta name="theme-color" content="#2196f3">
  <title><%= htmlWebpackPlugin.options.title %></title>
  <meta name="apple-mobile-web-app-title" content="Boilerplate">
  <meta name="apple-mobile-web-app-capable" content="yes">
  <meta name="apple-mobile-web-app-status-bar-style" content="default">
  <link rel="apple-touch-icon" sizes="120x120" href="/apple-touch-icon-120x120.png">
  <link rel="apple-touch-icon" sizes="152x152" href="/apple-touch-icon-152x152.png">
  <link rel="apple-touch-icon" sizes="167x167" href="/apple-touch-icon-167x167.png">
  <link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon-180x180.png">
  <link rel="apple-touch-startup-image" href="/apple-touch-startup-icon-1024x1024.png">
  <link rel="mask-icon" href="/safari-pinned-tab.svg" color="#2196f3">
  <link rel="manifest" href="/manifest.webmanifest">
  <link rel="shortcut icon" href="/favicon.ico">
</head>
<body>
  <noscript>
    For full functionality of this site it is necessary to enable JavaScript.
    Here are the <a href="https://www.enable-javascript.com/" target="_blank"> instructions how to enable JavaScript in your web browser</a>.
  </noscript>
  <div id="root"></div>
  <!--
    This HTML file is a template.
    If you open it directly in the browser, you will see an empty page.

    You can add webfonts, meta tags, or analytics to this file.
    The build step will place the bundled scripts into the <body> tag.

    To begin the development, run `yarn dev`.
    To run in production mode, use `yarn prod`.
  -->
  <script>
    // Async load Google Material Icons, eliminate render-blocking resources.
    // See https://github.com/typekit/webfontloader#get-started for details.
    WebFontConfig = {
      google: {
        families: ['Material Icons']
      }
    };
    (function (d) {
      var wf = d.createElement('script'), s = d.scripts[0];
      wf.src = 'https://cdnjs.cloudflare.com/ajax/libs/webfont/1.6.28/webfontloader.js';
      wf.async = true;
      s.parentNode.insertBefore(wf, s);
    })(document);
  </script>
</body>
</html>


================================================
FILE: frontend/public/locales/en/common.json
================================================
{
  "header": {
    "react": "React",
    "dropdown": "Dropdown",
    "notfound": "404"
  },
  "footer": {
    "title": "Footer Content",
    "content": "You can use rows and columns here to organize your footer content.",
    "links": "Links",
    "linkOne": "Link 1",
    "linkTwo": "Link 2",
    "linkThree": "Link 3",
    "linkFour": "Link 4",
    "moreLinks": "More Links"
  },
  "dropdown": {
    "parallax": "Parallax"
  }
}


================================================
FILE: frontend/public/locales/en/homePage.json
================================================
{
  "title": "Home Page",
  "carousel": {
    "focusButtonText": "Focus me!",
    "firstPanelTitle": "First Panel",
    "secondPanelTitle": "Second Panel",
    "thirdPanelTitle": "Third Panel",
    "fourthPanelTitle": "Fourth Panel",
    "firstPanelDescription": "This is your first panel, try to swipe it!",
    "secondPanelDescription": "This is your second panel, try to swipe it!",
    "thirdPanelDescription": "This is your third panel, try to swipe it!",
    "fourthPanelDescription": "This is your fourth panel, try to swipe it!",
    "tooltipText": "Click me! >. <",
    "toastText": "I am a toast!"
  },
  "pushpin": {
    "green": "Green",
    "blue": "Blue",
    "orange": "Orange",
    "red": "Red",
    "purple": "Purple",
    "cyan": "Cyan",
    "linkOne": "Link 1",
    "linkTwo": "Link 2"
  }
}


================================================
FILE: frontend/public/locales/en/notFoundPage.json
================================================
{
  "title": "404 Page Not Found"
}


================================================
FILE: frontend/public/locales/en/parallaxPage.json
================================================
{
  "title": "Parallax Page",
  "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."
}


================================================
FILE: frontend/public/locales/en/reactPage.json
================================================
{
  "title": "React Page",
  "fetchNote": {
    "asyncCalls": "async calls",
    "fetchAllNotes": "Fetch All Notes From DB"
  },
  "todoLayout": {
    "title": "todos",
    "todoFooter": {
      "all": "All",
      "active": "Active",
      "completed": "Completed"
    },
    "todoInput": {
      "addTodo": "Add todo"
    }
  }
}


================================================
FILE: frontend/public/locales/jp/common.json
================================================
{
  "header": {
    "react": "React",
    "dropdown": "ドロップダウンリスト",
    "notfound": "404"
  },
  "footer": {
    "title": "フッターコンテンツ",
    "content": "ここでは行と列を使用してフッターのコンテンツを整理できます。",
    "links": "リンク",
    "linkOne": "リンク 1",
    "linkTwo": "リンク 2",
    "linkThree": "リンク 3",
    "linkFour": "リンク 4",
    "moreLinks": "その他のリンク"
  },
  "dropdown": {
    "parallax": "視差"
  }
}


================================================
FILE: frontend/public/locales/jp/homePage.json
================================================
{
  "title": "ホームページ",
  "carousel": {
    "focusButtonText": "フォーカス!",
    "firstPanelTitle": "最初のパネル",
    "secondPanelTitle": "第2のパネル",
    "thirdPanelTitle": "第3のパネル",
    "fourthPanelTitle": "第4のパネル",
    "firstPanelDescription": "これはあなたの最初のパネルです、それをスワイプしよう!",
    "secondPanelDescription": "これはあなたの第2のパネルです、それをスワイプしよう!",
    "thirdPanelDescription": "これはあなたの第3のパネルです、それをスワイプしよう!",
    "fourthPanelDescription": "これはあなたの第4のパネルです、それをスワイプしよう!",
    "tooltipText": "クリック! >. <",
    "toastText": "私はトーストです!"
  },
  "pushpin": {
    "green": "緑",
    "blue": "青",
    "orange": "橙",
    "red": "赤",
    "purple": "紫",
    "cyan": "シアン",
    "linkOne": "リンク 1",
    "linkTwo": "リンク 2"
  }
}


================================================
FILE: frontend/public/locales/jp/notFoundPage.json
================================================
{
  "title": "404 ページが見つかりません"
}


================================================
FILE: frontend/public/locales/jp/parallaxPage.json
================================================
{
  "title": "視差ページ",
  "description": "Parallax(パララックス)は、この場合のバックグラウンドコンテンツまたは画像がスクロール中にフォアグラウンドコンテンツとは異なる速度で移動される場合の効果です。"
}


================================================
FILE: frontend/public/locales/jp/reactPage.json
================================================
{
  "title": "Reactページ",
  "fetchNote": {
    "asyncCalls": "非同期関数",
    "fetchAllNotes": "DB内のメモを取得する"
  },
  "todoLayout": {
    "title": "todoリスト",
    "todoFooter": {
      "all": "全部",
      "active": "アクティブ",
      "completed": "完成した"
    },
    "todoInput": {
      "addTodo": "追加する"
    }
  }
}


================================================
FILE: frontend/public/locales/zh/common.json
================================================
{
  "header": {
    "react": "React",
    "dropdown": "下拉列表",
    "notfound": "404"
  },
  "footer": {
    "title": "页脚内容",
    "content": "你可以在这里使用行或者列来编辑的页脚内容.",
    "links": "链接",
    "linkOne": "链接 1",
    "linkTwo": "链接 2",
    "linkThree": "链接 3",
    "linkFour": "链接 4",
    "moreLinks": "更多链接"
  },
  "dropdown": {
    "parallax": "视差"
  }
}


================================================
FILE: frontend/public/locales/zh/homePage.json
================================================
{
  "title": "首页",
  "carousel": {
    "focusButtonText": "看我看我!",
    "firstPanelTitle": "第一个面板",
    "secondPanelTitle": "第二个面板",
    "thirdPanelTitle": "第三个面板",
    "fourthPanelTitle": "第四个面板",
    "firstPanelDescription": "这是你的第一个面板,试试左右滑动它!",
    "secondPanelDescription": "这是你的第二个面板,试试左右滑动它!",
    "thirdPanelDescription": "这是你的第三个面板,试试左右滑动它!",
    "fourthPanelDescription": "这是你的第四个面板,试试左右滑动它!",
    "tooltipText": "戳我! >. <",
    "toastText": "哇我是一个toast!"
  },
  "pushpin": {
    "green": "绿色",
    "blue": "蓝色",
    "orange": "橘色",
    "red": "红色",
    "purple": "紫色",
    "cyan": "青色",
    "linkOne": "链接 1",
    "linkTwo": "链接 2"
  }
}


================================================
FILE: frontend/public/locales/zh/notFoundPage.json
================================================
{
  "title": "404 页面不存在"
}


================================================
FILE: frontend/public/locales/zh/parallaxPage.json
================================================
{
  "title": "视差页面",
  "description": "Parallax(视差) 是指一种背景内容或图像在滚动时以与前景内容不同的速度移动的效果。"
}


================================================
FILE: frontend/public/locales/zh/reactPage.json
================================================
{
  "title": "React页面",
  "fetchNote": {
    "asyncCalls": "异步函数",
    "fetchAllNotes": "获取数据库所有笔记"
  },
  "todoLayout": {
    "title": "待办事项",
    "todoFooter": {
      "all": "全部",
      "active": "待办",
      "completed": "已完成"
    },
    "todoInput": {
      "addTodo": "添加待办事项"
    }
  }
}


================================================
FILE: frontend/public/manifest.webmanifest
================================================
{
  "name": "Boilerplate",
  "short_name": "Boilerplate",
  "start_url": ".",
  "display": "standalone",
  "orientation": "portrait",
  "background_color": "#2196f3",
  "theme_color": "#2196f3",
  "description": "Full-stack boilerplate that using express with webpack, react and typescript!",
  "icons": [
    {
      "src": "icons/android-chrome-192x192.png",
      "sizes": "192x192",
      "type": "image/png"
    },
    {
      "src": "icons/android-chrome-512x512.png",
      "sizes": "512x512",
      "type": "image/png"
    }
  ]
}


================================================
FILE: frontend/public/robots.txt
================================================
User-agent: *
Disallow:


================================================
FILE: frontend/src/App.tsx
================================================
import { ConnectedRouter } from 'connected-react-router/immutable';
import { History } from 'history';
import React from 'react';
import { Provider } from 'react-redux';
import { Store } from 'redux';

import router from 'router';
import { IGlobalState } from 'types/global';

interface IAppProps {
  store: Store<IGlobalState>;
  history: History;
}

export default (props: IAppProps) => (
  <Provider store={props.store}>
    <ConnectedRouter history={props.history}>
      {router}
    </ConnectedRouter>
  </Provider>
);


================================================
FILE: frontend/src/components/ContentLoader/__tests__/__snapshots__/contentLoader.spec.tsx.snap
================================================
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`BodyContentLoader should renders correctly 1`] = `
<div
  className="bodyContentLoader"
>
  <ContentLoader
    height={200}
    primaryColor="#f3f3f3"
    secondaryColor="#d7d6d8"
    speed={1}
    width={400}
  >
    <rect
      height="15.1"
      rx="4"
      ry="4"
      width="76.63"
      x="160.5"
      y="11"
    />
    <rect
      height="151.36"
      rx="1"
      ry="1"
      width="275.4"
      x="60.5"
      y="35.05"
    />
  </ContentLoader>
</div>
`;

exports[`FooterContentLoader should renders correctly 1`] = `
<ContentLoader
  height={400}
  primaryColor="#f3f3f3"
  secondaryColor="#d7d6d8"
  speed={1}
  width={2500}
>
  <rect
    height="400"
    width="2500"
    x="0"
    y="0"
  />
</ContentLoader>
`;

exports[`HeaderContentLoader should renders correctly 1`] = `
<ContentLoader
  height={95}
  primaryColor="#f3f3f3"
  secondaryColor="#d7d6d8"
  speed={1}
  width={2500}
>
  <rect
    height="95"
    width="2500"
    x="0"
    y="0"
  />
</ContentLoader>
`;


================================================
FILE: frontend/src/components/ContentLoader/__tests__/contentLoader.spec.tsx
================================================
import React from 'react';
import ShallowRenderer from 'react-test-renderer/shallow';

import { BodyContentLoader, FooterContentLoader, HeaderContentLoader } from '../contentLoader';

describe('HeaderContentLoader', () => {
  it('should renders correctly', () => {
    const renderer = ShallowRenderer.createRenderer();
    const result = renderer.render(
      <HeaderContentLoader />,
    );
    expect(result).toMatchSnapshot();
    renderer.unmount();
  });
});

describe('BodyContentLoader', () => {
  it('should renders correctly', () => {
    const renderer = ShallowRenderer.createRenderer();
    const result = renderer.render(
      <BodyContentLoader />,
    );
    expect(result).toMatchSnapshot();
    renderer.unmount();
  });
});

describe('FooterContentLoader', () => {
  it('should renders correctly', () => {
    const renderer = ShallowRenderer.createRenderer();
    const result = renderer.render(
      <FooterContentLoader />,
    );
    expect(result).toMatchSnapshot();
    renderer.unmount();
  });
});


================================================
FILE: frontend/src/components/ContentLoader/contentLoader.scss
================================================
.bodyContentLoader {
  margin: auto;
}


================================================
FILE: frontend/src/components/ContentLoader/contentLoader.tsx
================================================
import React from 'react';
import ContentLoader from 'react-content-loader';

const styles = require('./contentLoader.scss');

export const BodyContentLoader = () => (
  <div className={styles.bodyContentLoader}>
    <ContentLoader
      height={200}
      width={400}
      speed={1}
      primaryColor='#f3f3f3'
      secondaryColor='#d7d6d8'
    >
      <rect x='160.5' y='11' rx='4' ry='4' width='76.63' height='15.1' />
      <rect x='60.5' y='35.05' rx='1' ry='1' width='275.4' height='151.36' />
    </ContentLoader>
  </div>
);

export const HeaderContentLoader = () => (
  <ContentLoader
    height={95}
    width={2500}
    speed={1}
    primaryColor='#f3f3f3'
    secondaryColor='#d7d6d8'
  >
    <rect x='0' y='0' width='2500' height='95' />
  </ContentLoader>
);

export const FooterContentLoader = () => (
  <ContentLoader
    height={400}
    width={2500}
    speed={1}
    primaryColor='#f3f3f3'
    secondaryColor='#d7d6d8'
  >
    <rect x='0' y='0' width='2500' height='400' />
  </ContentLoader>
);


================================================
FILE: frontend/src/components/ContentLoader/index.tsx
================================================
export { BodyContentLoader, HeaderContentLoader, FooterContentLoader } from './contentLoader';


================================================
FILE: frontend/src/components/Dropdown/__tests__/__snapshots__/dropdown.spec.tsx.snap
================================================
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Dropdown should renders correctly 1`] = `
Array [
  <ul
    className="dropdown-content"
    id="id"
  >
    <li>
      <a
        href="/dropdown"
        onClick={[Function]}
      >
        dropdown.dropdown
      </a>
    </li>
  </ul>,
  ",",
]
`;


================================================
FILE: frontend/src/components/Dropdown/__tests__/dropdown.spec.tsx
================================================
import 'materialize-css';
import React, { Fragment } from 'react';
import { BrowserRouter } from 'react-router-dom';
import TestRenderer from 'react-test-renderer';

import { Dropdown } from '../dropdown';

describe('Dropdown', () => {
  it('should renders correctly', () => {
    const renderer = TestRenderer.create(
      <BrowserRouter>
        <Fragment>
          <Dropdown id='id' dropdownLists={['dropdown']} />,
        </Fragment>
      </BrowserRouter>,
    );
    expect(renderer).toMatchSnapshot();
    renderer.unmount();
  });
});


================================================
FILE: frontend/src/components/Dropdown/dropdown.tsx
================================================
import React from 'react';
import { Translation } from 'react-i18next';
import { Link } from 'react-router-dom';

interface IDropdownProps {
  id: string;
  dropdownLists: string[];
}

const dropdownConfig: Partial<M.DropdownOptions> = {
  coverTrigger: false,
};

export class Dropdown extends React.Component<IDropdownProps> {
  public componentDidMount() {
    const elems = document.querySelectorAll('.dropdown-button');
    M.Dropdown.init(elems, dropdownConfig);
  }

  public render() {
    return (
      <Translation ns='common'>
        {(t) => (
          <ul id={this.props.id} className='dropdown-content'>
            {this.props.dropdownLists.map((key) =>
              <li key={key}><Link to={`/${key}`}>{t('dropdown.' + key)}</Link></li>,
            )}
          </ul>
        )}
      </Translation>
    );
  }
}

export default Dropdown;


================================================
FILE: frontend/src/components/Dropdown/index.tsx
================================================
export { default } from './dropdown';


================================================
FILE: frontend/src/components/Footer/__tests__/__snapshots__/footer.spec.tsx.snap
================================================
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Footer should renders correctly 1`] = `
<footer
  className="page-footer"
>
  <div
    className="container"
  >
    <div
      className="row"
    >
      <div
        className="col l6 s12"
      >
        <h5
          className="white-text"
        >
          footer.title
        </h5>
        <p
          className="grey-text text-lighten-4"
        >
          footer.content
        </p>
      </div>
      <div
        className="col l3 offset-l3 s12"
      >
        <h5
          className="white-text"
        >
          footer.links
        </h5>
        <ul>
          <li>
            <a
              className="grey-text text-lighten-3"
              href="#"
            >
              footer.linkOne
            </a>
          </li>
          <li>
            <a
              className="grey-text text-lighten-3"
              href="#"
            >
              footer.linkTwo
            </a>
          </li>
          <li>
            <a
              className="grey-text text-lighten-3"
              href="#"
            >
              footer.linkThree
            </a>
          </li>
          <li>
            <a
              className="grey-text text-lighten-3"
              href="#"
            >
              footer.linkFour
            </a>
          </li>
        </ul>
      </div>
    </div>
  </div>
  <div
    className="footer-copyright"
  >
    <div
      className="container"
    >
      © 2017 Copyright 
      <a
        className="grey-text text-lighten-4"
        href="https://github.com/Armour"
      >
        Armour
      </a>
      <a
        className="grey-text text-lighten-4 right"
        href="#"
      >
        footer.moreLinks
      </a>
    </div>
  </div>
</footer>
`;


================================================
FILE: frontend/src/components/Footer/__tests__/footer.spec.tsx
================================================
import React from 'react';
import TestRenderer from 'react-test-renderer';

import { Footer } from '../footer';

describe('Footer', () => {
  it('should renders correctly', () => {
    const renderer = TestRenderer.create(
      <Footer />,
    );
    expect(renderer).toMatchSnapshot();
    renderer.unmount();
  });
});


================================================
FILE: frontend/src/components/Footer/footer.tsx
================================================
import React from 'react';
import { Translation } from 'react-i18next';

export class Footer extends React.Component {
  public render() {
    return (
      <Translation ns='common'>
        {(t) => (
          <footer className='page-footer'>
            <div className='container'>
              <div className='row'>
                <div className='col l6 s12'>
                  <h5 className='white-text'>{t('footer.title')}</h5>
                  <p className='grey-text text-lighten-4'>{t('footer.content')}</p>
                </div>
                <div className='col l3 offset-l3 s12'>
                  <h5 className='white-text'>{t('footer.links')}</h5>
                  <ul>
                    <li><a className='grey-text text-lighten-3' href='#'>{t('footer.linkOne')}</a></li>
                    <li><a className='grey-text text-lighten-3' href='#'>{t('footer.linkTwo')}</a></li>
                    <li><a className='grey-text text-lighten-3' href='#'>{t('footer.linkThree')}</a></li>
                    <li><a className='grey-text text-lighten-3' href='#'>{t('footer.linkFour')}</a></li>
                  </ul>
                </div>
              </div>
            </div>
            <div className='footer-copyright'>
              <div className='container'>
                © 2017 Copyright <a className='grey-text text-lighten-4' href='https://github.com/Armour'>Armour</a>
                <a className='grey-text text-lighten-4 right' href='#'>{t('footer.moreLinks')}</a>
              </div>
            </div>
          </footer>
        )}
      </Translation>
    );
  }
}

export default Footer;


================================================
FILE: frontend/src/components/Footer/index.tsx
================================================
export { default } from './footer';


================================================
FILE: frontend/src/components/Header/__tests__/__snapshots__/header.spec.tsx.snap
================================================
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Header should renders correctly 1`] = `
Array [
  <div>
    <nav>
      <div
        className="nav-wrapper"
      >
        <div
          className="container"
        >
          <a
            className="brand-logo"
            href="/"
            onClick={[Function]}
          >
            <img
              alt="logo"
              className="logo"
              src={
                Object {
                  "default": "test-file-stub",
                }
              }
            />
          </a>
          <a
            className="sidenav-trigger"
            data-target="nav-mobile"
            href="#"
          >
            <i
              className="material-icons"
            >
              menu
            </i>
          </a>
          <ul
            className="right hide-on-med-and-down"
            id="nav-desktop"
          >
            <li
              className=""
            >
              <a
                href="/react"
                onClick={[Function]}
              >
                header.react
              </a>
            </li>
            <li
              className="active"
            >
              <a
                className="dropdown-button"
                data-target="header-dropdown-desktop"
                href="#"
              >
                header.dropdown
                <i
                  className="material-icons right"
                >
                  arrow_drop_down
                </i>
              </a>
            </li>
            <li
              className=""
            >
              <a
                href="/404"
                onClick={[Function]}
              >
                header.notfound
              </a>
            </li>
            <Dropdown
              dropdownLists={
                Array [
                  "parallax",
                ]
              }
              id="header-dropdown-desktop"
            />
          </ul>
        </div>
      </div>
    </nav>
    <ul
      className="sidenav"
      id="nav-mobile"
    >
      <li
        className=""
      >
        <a
          href="/react"
          onClick={[Function]}
        >
          header.react
        </a>
      </li>
      <li
        className="active"
      >
        <a
          className="dropdown-button"
          data-target="header-dropdown-mobile"
          href="#"
        >
          header.dropdown
          <i
            className="material-icons right"
          >
            arrow_drop_down
          </i>
        </a>
      </li>
      <li
        className=""
      >
        <a
          href="/404"
          onClick={[Function]}
        >
          header.notfound
        </a>
      </li>
      <Dropdown
        dropdownLists={
          Array [
            "parallax",
          ]
        }
        id="header-dropdown-mobile"
      />
    </ul>
  </div>,
  ",",
]
`;


================================================
FILE: frontend/src/components/Header/__tests__/header.spec.tsx
================================================
import 'materialize-css';
import React, { Fragment } from 'react';
import { BrowserRouter } from 'react-router-dom';
import TestRenderer from 'react-test-renderer';

import { Header } from '../header';

jest.mock('components/Dropdown', () => 'Dropdown');

describe('Header', () => {
  it('should renders correctly', () => {
    const renderer = TestRenderer.create(
      <BrowserRouter>
        <Fragment>
          <Header pathname='/parallax' />,
        </Fragment>
      </BrowserRouter>,
    );
    expect(renderer).toMatchSnapshot();
    renderer.unmount();
  });
});


================================================
FILE: frontend/src/components/Header/header.scss
================================================
.logo {
  margin: 6px 0 0 0;
  max-height: 50px;
}


================================================
FILE: frontend/src/components/Header/header.tsx
================================================
import React from 'react';
import { Translation } from 'react-i18next';
import { connect } from 'react-redux';
import { Link } from 'react-router-dom';

import Dropdown from 'components/Dropdown';
import { IGlobalStateRecord } from 'types/global';

const styles = require('./header.scss');

// Component

interface IHeaderStateProps {
  pathname: string;
}

export class Header extends React.Component<IHeaderStateProps> {
  constructor(props: IHeaderStateProps) {
    super(props);
  }

  public checkActive(urls: string[]) {
    let active = false;
    urls.forEach((url) => {
      if (this.props.pathname === '/' + url) {
        active = true;
      }
    });
    return active ? 'active' : '';
  }

  public componentDidMount() {
    const elems = document.querySelectorAll('.sidenav');
    M.Sidenav.init(elems);
  }

  public render() {
    const dropdownList = ['parallax'];
    return (
      <Translation ns='common'>
        {(t) => (
          <div>
            <nav>
              <div className='nav-wrapper'>
                <div className='container'>
                  <Link className='brand-logo' to='/'><img className={styles.logo} alt='logo' src={require('./assets/images/logo.png')}/></Link>
                  <a href='#' data-target='nav-mobile' className='sidenav-trigger'>
                    <i className='material-icons'>menu</i>
                  </a>
                  <ul id='nav-desktop' className='right hide-on-med-and-down'>
                    <li className={this.checkActive(['react'])} key='react'><Link to='/react'>{t('header.react')}</Link></li>
                    <li className={this.checkActive(dropdownList)} key='materialize'>
                      <a className='dropdown-button' href='#' data-target='header-dropdown-desktop'>{t('header.dropdown')}<i className='material-icons right'>arrow_drop_down</i></a>
                    </li>
                    <li className={this.checkActive(['404'])} key='404'><Link to='/404'>{t('header.notfound')}</Link></li>
                    <Dropdown id='header-dropdown-desktop' dropdownLists={dropdownList} />
                  </ul>
                </div>
              </div>
            </nav>
            <ul id='nav-mobile' className='sidenav'>
              <li className={this.checkActive(['react'])} key='react'><Link to='/react'>{t('header.react')}</Link></li>
              <li className={this.checkActive(dropdownList)} key='materialize'>
                <a className='dropdown-button' href='#' data-target='header-dropdown-mobile'>{t('header.dropdown')}<i className='material-icons right'>arrow_drop_down</i></a>
              </li>
              <li className={this.checkActive(['404'])} key='404'><Link to='/404'>{t('header.notfound')}</Link></li>
              <Dropdown id='header-dropdown-mobile' dropdownLists={dropdownList} />
            </ul>
          </div>
        )}
      </Translation>
    );
  }
}

// Container

const mapStateToProps = (state: IGlobalStateRecord): IHeaderStateProps => ({
  pathname: state.getIn(['router', 'location', 'pathname']),
});

export default connect(
  mapStateToProps,
  null,
)(Header);


================================================
FILE: frontend/src/components/Header/index.tsx
================================================
export { default } from './header';


================================================
FILE: frontend/src/i18n/index.tsx
================================================
import i18n from 'i18next';
import detector from 'i18next-browser-languagedetector';
import backend from 'i18next-xhr-backend';
import { initReactI18next } from 'react-i18next';

import { isProduction } from 'utils';

export default i18n
  .use(detector)
  .use(backend)
  .use(initReactI18next)
  .init({
    debug: !isProduction,
    backend: {
      loadPath: './locales/{{lng}}/{{ns}}.json',
    },
    whitelist: ['en', 'zh', 'jp'],
    fallbackLng: 'en',
    fallbackNS: 'common',
    interpolation: {
      escapeValue: false, // not needed for react.
    },
    react: {
      wait: false,
    },
  }, (err, _) => {
    if (err) {
      return console.error('Load i18n instance failed.', err);
    }
  });


================================================
FILE: frontend/src/index.tsx
================================================
import { createBrowserHistory } from 'history';
import OfflinePluginRuntime from 'offline-plugin/runtime';
import React from 'react';
import ReactDom from 'react-dom';
import { AppContainer } from 'react-hot-loader';

import 'i18n';

import App from 'App';
import { Map } from 'immutable';
import configureStore from 'store';
import { isProduction } from 'utils';

// Webpack offline plugin
if (isProduction) {
  OfflinePluginRuntime.install();
}

// To keep reducers self-sufficient and reusable, we choose to not set
// initial state here, and let each reducer to handle the default state
// https://github.com/reactjs/redux/issues/1189#issuecomment-168025590
const initialState = Map();

// Create browser history
const history = createBrowserHistory();

// Configure store
const store = configureStore(history, initialState);

// Create render function
const render = (Component: any) => {
  ReactDom.render(
    <AppContainer>
      <Component store={store} history={history} />
    </AppContainer>,
    document.getElementById('root'),
  );
};

// First time render
render(App);

// Hot Reload Module API
if (module.hot) {
  module.hot.accept('./App', () => {
    const NextApp = require('App').default;
    render(NextApp);
  });
}


================================================
FILE: frontend/src/pages/HomePage/__tests__/__snapshots__/homePage.spec.tsx.snap
================================================
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`HomePage should renders correctly 1`] = `
Array [
  <div
    className="home-page-block"
  >
    <h1
      className="page-title"
    >
      title
    </h1>
    <div
      className="container"
    >
      <Carousel />
    </div>
  </div>,
  <Pushpin
    color="blue"
    depth="lighten-1"
  />,
  <Pushpin
    color="green"
    depth="lighten-1"
  />,
  <Pushpin
    color="orange"
    depth="lighten-1"
  />,
  <Pushpin
    color="red"
    depth="lighten-1"
  />,
  <Pushpin
    color="purple"
    depth="lighten-1"
  />,
  <Pushpin
    color="cyan"
    depth="lighten-1"
  />,
  <TranslationButton />,
]
`;


================================================
FILE: frontend/src/pages/HomePage/__tests__/homePage.spec.tsx
================================================
import 'materialize-css';
import React from 'react';
import TestRenderer from 'react-test-renderer';

import { HomePage } from '../homePage';

jest.mock('../components/Carousel', () => 'Carousel');
jest.mock('../components/Pushpin', () => 'Pushpin');
jest.mock('../components/TranslationButton', () => 'TranslationButton');

describe('HomePage', () => {
  it('should renders correctly', () => {
    const renderer = TestRenderer.create(
      <HomePage />,
    );
    expect(renderer).toMatchSnapshot();
    renderer.unmount();
  });
});


================================================
FILE: frontend/src/pages/HomePage/components/Carousel/__tests__/__snapshots__/carousel.spec.tsx.snap
================================================
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Carousel should renders correctly 1`] = `
<div
  className="carousel carousel-slider center z-depth-3"
  data-indicators="true"
>
  <div
    className="carousel-fixed-item center"
  >
    <a
      className="btn tooltipped waves-effect white grey-text text-darken-2"
      onClick={[Function]}
      role="button"
    >
      carousel.focusButtonText
    </a>
  </div>
  <a
    className="carousel-item green white-text"
    href="#one!"
  >
    <h2>
      carousel.firstPanelTitle
    </h2>
    <p>
      carousel.firstPanelDescription
    </p>
  </a>
  <a
    className="carousel-item amber white-text"
    href="#two!"
  >
    <h2>
      carousel.secondPanelTitle
    </h2>
    <p>
      carousel.secondPanelDescription
    </p>
  </a>
  <a
    className="carousel-item red white-text"
    href="#three!"
  >
    <h2>
      carousel.thirdPanelTitle
    </h2>
    <p>
      carousel.thirdPanelDescription
    </p>
  </a>
  <a
    className="carousel-item purple white-text"
    href="#four!"
  >
    <h2>
      carousel.fourthPanelTitle
    </h2>
    <p>
      carousel.fourthPanelDescription
    </p>
  </a>
</div>
`;


================================================
FILE: frontend/src/pages/HomePage/components/Carousel/__tests__/carousel.spec.tsx
================================================
import 'materialize-css';
import React from 'react';
import TestRenderer from 'react-test-renderer';

import { Carousel } from '../carousel';

describe('Carousel', () => {
  it('should renders correctly', () => {
    const renderer = TestRenderer.create(
      <Carousel />,
    );
    expect(renderer).toMatchSnapshot();
    renderer.unmount();
  });
});


================================================
FILE: frontend/src/pages/HomePage/components/Carousel/carousel.tsx
================================================
import React from 'react';
import { Translation } from 'react-i18next';

import { CAROUSEL_AUTOPLAY_INTERVAL, TOAST_DISPLAY_DURATION, TOOLTIP_DELAY_TIME } from './constants/carousel';

const tooltipConfig: Partial<M.TooltipOptions> = {
  enterDelay: TOOLTIP_DELAY_TIME,
  position: 'top',
};

const carouselConfig: Partial<M.CarouselOptions> = {
  fullWidth: true,
  indicators: true,
};

const toastConfig: Partial<M.ToastOptions> = {
  inDuration: TOAST_DISPLAY_DURATION / 4,
  outDuration: TOAST_DISPLAY_DURATION / 4,
  displayLength: TOAST_DISPLAY_DURATION,
};

export class Carousel extends React.Component {
  public timer: number = 0;

  public componentDidMount() {
    const carouselElems = document.querySelectorAll('.carousel.carousel-slider');
    const carousels = M.Carousel.init(carouselElems, carouselConfig);
    this.timer = window.setTimeout(() => this.autoPlayCarousel(carousels), CAROUSEL_AUTOPLAY_INTERVAL);
  }

  public componentWillUnmount() {
    clearTimeout(this.timer);
  }

  public autoPlayCarousel = (carousels: M.Carousel[]) => {
    carousels.forEach((carousel) => {
      if (!carousel.pressed) {
        carousel.next();
      }
    });
    this.timer = window.setTimeout(() => this.autoPlayCarousel(carousels), CAROUSEL_AUTOPLAY_INTERVAL);
  }

  public displayToast = (text: string) => (e: React.MouseEvent<HTMLElement>) => {
    e.preventDefault();
    toastConfig.html = text;
    M.toast(toastConfig);
  }

  public initTooltip = (text: string) => {
    tooltipConfig.html = text;
    const tooltipElems = document.querySelectorAll('.tooltipped');
    M.Tooltip.init(tooltipElems, tooltipConfig);
  }

  public render() {
    return (
      <Translation ns='homePage'>
        {(t) => (
          this.initTooltip(t('carousel.tooltipText')),
          <div className='carousel carousel-slider center z-depth-3' data-indicators='true'>
            <div className='carousel-fixed-item center'>
              <a className='btn tooltipped waves-effect white grey-text text-darken-2' onClick={this.displayToast(t('carousel.toastText'))} role='button'>
                {t('carousel.focusButtonText')}
              </a>
            </div>
            <a className='carousel-item green white-text' href='#one!'>
              <h2>{t('carousel.firstPanelTitle')}</h2>
              <p>{t('carousel.firstPanelDescription')}</p>
            </a>
            <a className='carousel-item amber white-text' href='#two!'>
              <h2>{t('carousel.secondPanelTitle')}</h2>
              <p>{t('carousel.secondPanelDescription')}</p>
            </a>
            <a className='carousel-item red white-text' href='#three!'>
              <h2>{t('carousel.thirdPanelTitle')}</h2>
              <p>{t('carousel.thirdPanelDescription')}</p>
            </a>
            <a className='carousel-item purple white-text' href='#four!'>
              <h2>{t('carousel.fourthPanelTitle')}</h2>
              <p>{t('carousel.fourthPanelDescription')}</p>
            </a>
          </div>
        )}
      </Translation>
    );
  }
}

export default Carousel;


================================================
FILE: frontend/src/pages/HomePage/components/Carousel/constants/carousel.tsx
================================================
// Toast timer
export const TOAST_DISPLAY_DURATION = 3000;

// Tooltip timer
export const TOOLTIP_DELAY_TIME = 50;

// Carousel autoplay interval
export const CAROUSEL_AUTOPLAY_INTERVAL = 3500;


================================================
FILE: frontend/src/pages/HomePage/components/Carousel/index.tsx
================================================
export { default } from './carousel';


================================================
FILE: frontend/src/pages/HomePage/components/Pushpin/__tests__/__snapshots__/pushpin.spec.tsx.snap
================================================
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Pushpin should renders correctly 1`] = `
<div
  className="pushpin red base"
  id="red"
>
  <nav
    className="pushpin-nav pin-top"
    data-target="pushpin-red"
  >
    <div
      className="nav-wrapper red pushpin-red"
    >
      <div
        className="container"
      >
        <a
          className="brand-logo"
          href="#"
        >
          pushpin.red
        </a>
        <ul
          className="right hide-on-med-and-down"
          id="nav-demo-red"
        >
          <li>
            <a
              href="#"
            >
              pushpin.linkOne
            </a>
          </li>
          <li>
            <a
              href="#"
            >
              pushpin.linkTwo
            </a>
          </li>
        </ul>
      </div>
    </div>
  </nav>
</div>
`;


================================================
FILE: frontend/src/pages/HomePage/components/Pushpin/__tests__/pushpin.spec.tsx
================================================
import React from 'react';
import TestRenderer from 'react-test-renderer';

import { Pushpin } from '../pushpin';

describe('Pushpin', () => {
  it('should renders correctly', () => {
    const renderer = TestRenderer.create(
      <Pushpin color='red' depth='base' />,
    );
    expect(renderer).toMatchSnapshot();
    renderer.unmount();
  });
});


================================================
FILE: frontend/src/pages/HomePage/components/Pushpin/index.tsx
================================================
export { default } from './pushpin';


================================================
FILE: frontend/src/pages/HomePage/components/Pushpin/pushpin.scss
================================================
.pushpin {
  height: 825px;
}


================================================
FILE: frontend/src/pages/HomePage/components/Pushpin/pushpin.tsx
================================================
import React from 'react';
import { Translation } from 'react-i18next';

const styles = require('./pushpin.scss');

interface IPushpinProps {
  color: string;
  depth: string;
}

export class Pushpin extends React.Component<IPushpinProps> {
  public render() {
    return (
      <Translation ns='homePage'>
        {(t) => (
          <div id={this.props.color} className={`${styles.pushpin} ${this.props.color} ${this.props.depth}`}>
            <nav className='pushpin-nav pin-top' data-target={'pushpin-' + this.props.color}>
              <div className={`nav-wrapper ${this.props.color} pushpin-${this.props.color}`}>
                <div className='container'>
                  <a href='#' className='brand-logo'>{t('pushpin.' + this.props.color)}</a>
                  <ul id={`nav-demo-${this.props.color}`} className='right hide-on-med-and-down'>
                    <li><a href='#'>{t('pushpin.linkOne')}</a></li>
                    <li><a href='#'>{t('pushpin.linkTwo')}</a></li>
                  </ul>
                </div>
              </div>
            </nav>
          </div>
        )}
      </Translation>
    );
  }
}

export default Pushpin;


================================================
FILE: frontend/src/pages/HomePage/components/TranslationButton/__tests__/__snapshots__/translationButton.spec.tsx.snap
================================================
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`TranslationButton should renders correctly 1`] = `
<div
  className="fixed-action-btn vertical"
>
  <a
    className="btn-floating btn-large red"
  >
    <i
      className="large material-icons"
    >
      translate
    </i>
  </a>
  <ul>
    <li>
      <a
        className="btn-floating red"
        onClick={[Function]}
      >
        en
      </a>
    </li>
    <li>
      <a
        className="btn-floating green"
        onClick={[Function]}
      >
        zh
      </a>
    </li>
    <li>
      <a
        className="btn-floating yellow darken-1"
        onClick={[Function]}
      >
        jp
      </a>
    </li>
  </ul>
</div>
`;


================================================
FILE: frontend/src/pages/HomePage/components/TranslationButton/__tests__/translationButton.spec.tsx
================================================
import 'materialize-css';
import React from 'react';
import TestRenderer from 'react-test-renderer';

import { TranslationButton } from '../translationButton';

describe('TranslationButton', () => {
  it('should renders correctly', () => {
    const renderer = TestRenderer.create(
      <TranslationButton />,
    );
    expect(renderer).toMatchSnapshot();
    renderer.unmount();
  });
});


================================================
FILE: frontend/src/pages/HomePage/components/TranslationButton/index.tsx
================================================
export { default } from './translationButton';


================================================
FILE: frontend/src/pages/HomePage/components/TranslationButton/translationButton.tsx
================================================
import i18next from 'i18next';
import React from 'react';
import { Translation } from 'react-i18next';

const floatingActionButtonConfig: Partial<M.FloatingActionButtonOptions> = {
  direction: 'top',
};

export class TranslationButton extends React.Component {
  public componentDidMount() {
    const elems = document.querySelectorAll('.fixed-action-btn');
    M.FloatingActionButton.init(elems, floatingActionButtonConfig);
  }

  public changeLanguage = (i18n: i18next.i18n, lng: string) => (e: React.MouseEvent<HTMLElement>) => {
    e.preventDefault();
    i18n.changeLanguage(lng);
  }

  public render() {
    return (
      <Translation>
        {(_, {i18n}) => (
          <div className='fixed-action-btn vertical'>
            <a className='btn-floating btn-large red'>
              <i className='large material-icons'>translate</i>
            </a>
            <ul>
              <li><a className='btn-floating red' onClick={this.changeLanguage(i18n, 'en')}>en</a></li>
              <li><a className='btn-floating green' onClick={this.changeLanguage(i18n, 'zh')}>zh</a></li>
              <li><a className='btn-floating yellow darken-1' onClick={this.changeLanguage(i18n, 'jp')}>jp</a></li>
            </ul>
          </div>
        )}
      </Translation>
    );
  }
}

export default TranslationButton;


================================================
FILE: frontend/src/pages/HomePage/homePage.scss
================================================
.home-page-block {
  height: 820px;
}


================================================
FILE: frontend/src/pages/HomePage/homePage.tsx
================================================
import React, { Fragment } from 'react';
import { Translation } from 'react-i18next';

import Carousel from './components/Carousel';
import Pushpin from './components/Pushpin';
import TranslationButton from './components/TranslationButton';

const styles = require('./homePage.scss');

export class HomePage extends React.Component {
  public componentDidMount() {
    document.querySelectorAll('.pushpin-nav').forEach((elem, _) => {
      const target = document.querySelector('.' + elem.getAttribute('data-target')!);
      const rect = target!.getBoundingClientRect();
      let top = rect.top;
      // Make sure element is not hidden (display: none) or disconnected
      if (rect.width || rect.height || target!.getClientRects().length) {
        top += window.pageYOffset - target!.ownerDocument!.documentElement!.clientTop;
      }
      const bottom = top + elem.parentElement!.getBoundingClientRect().height - rect.height;
      M.Pushpin.init(elem, {
        top,
        bottom,
      });
    });
  }

  public render() {
    return (
      <Translation ns='homePage'>
        {(t) => (
          <Fragment>
            <div className={styles['home-page-block']}>
              <h1 className='page-title'>{t('title')}</h1>
              <div className='container'>
                <Carousel />
              </div>
            </div>
            <Pushpin color='blue' depth='lighten-1' />
            <Pushpin color='green' depth='lighten-1' />
            <Pushpin color='orange' depth='lighten-1' />
            <Pushpin color='red' depth='lighten-1' />
            <Pushpin color='purple' depth='lighten-1' />
            <Pushpin color='cyan' depth='lighten-1' />
            <TranslationButton />
          </Fragment>
        )}
      </Translation>
    );
  }
}

export default HomePage;


================================================
FILE: frontend/src/pages/HomePage/index.tsx
================================================
export { default } from './homePage';


================================================
FILE: frontend/src/pages/NotFoundPage/__tests__/__snapshots__/notFoundPage.spec.tsx.snap
================================================
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`NotFoundPage should renders correctly 1`] = `
Array [
  <h1
    className="page-title"
  >
    title
  </h1>,
  <img
    alt="not-found-img"
    className="not-found-img"
    height="550px"
    src={
      Object {
        "default": "test-file-stub",
      }
    }
    width="750px"
  />,
]
`;


================================================
FILE: frontend/src/pages/NotFoundPage/__tests__/notFoundPage.spec.tsx
================================================
import React from 'react';
import TestRenderer from 'react-test-renderer';

import { NotFoundPage } from '../notFoundPage';

describe('NotFoundPage', () => {
  it('should renders correctly', () => {
    const renderer = TestRenderer.create(
      <NotFoundPage />,
    );
    expect(renderer).toMatchSnapshot();
    renderer.unmount();
  });
});


================================================
FILE: frontend/src/pages/NotFoundPage/index.tsx
================================================
export { default } from './notFoundPage';


================================================
FILE: frontend/src/pages/NotFoundPage/notFoundPage.scss
================================================
@import 'sass/variables';

.not-found-img {
  display: block;
  margin: auto;
  padding: 60px;
  max-width: 90%;
  object-fit: cover;
}

@media only screen and (max-width: $small-screen) {
  .not-found-img {
    height: 360px;
    max-width: 100%;
  }
}


================================================
FILE: frontend/src/pages/NotFoundPage/notFoundPage.tsx
================================================
import React, { Fragment } from 'react';
import { Translation } from 'react-i18next';

const styles = require('./notFoundPage.scss');

const notFoundImageList = [
  '404.gif',
  '404-1.jpeg',
  '404-2.jpeg',
  '404.jpg',
];

interface INotFoundPageState {
  imageId: number;
}

export class NotFoundPage extends React.Component<{}, INotFoundPageState> {
  constructor(props: {}) {
    super(props);
    this.state = { imageId: this.getRandomInt(0, notFoundImageList.length) };
  }

  public getRandomInt = (min: number, max: number) => {
    min = Math.ceil(min);
    max = Math.floor(max);
    return Math.floor(Math.random() * (max - min)) + min; // The maximum is exclusive and the minimum is inclusive
  }

  public render() {
    return (
      <Translation ns='notFoundPage'>
        {(t) => (
          <Fragment>
            <h1 className='page-title'>{t('title')}</h1>
            <img className={styles['not-found-img']} src={require('./assets/images/' + notFoundImageList[this.state.imageId])} alt='not-found-img' height='550px' width='750px' />
          </Fragment>
        )}
      </Translation>
    );
  }
}

export default NotFoundPage;


================================================
FILE: frontend/src/pages/ParallaxPage/__tests__/__snapshots__/parallaxPage.spec.tsx.snap
================================================
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`ParallaxPage should renders correctly 1`] = `
Array [
  <div
    className="white"
  >
    <h1
      className="page-title"
    >
      title
    </h1>
  </div>,
  <div
    className="parallax-container"
  >
    <div
      className="parallax"
    >
      <img
        alt="parallax-img"
        className="parallax-img"
        src={
          Object {
            "default": "test-file-stub",
          }
        }
      />
    </div>
  </div>,
  <div
    className="section white"
  >
    <div
      className="row container"
    >
      <h2
        className="parallax-header"
      >
        Parallax
      </h2>
      <p
        className="grey-text text-darken-3"
      >
        description
      </p>
    </div>
    <div
      className="row container"
    >
      <h4
        className="light"
      >
        Parallax Demo Code
      </h4>
      <PrismCodes
        language="language-tsx"
      />
    </div>
  </div>,
  <div
    className="parallax-container"
  >
    <div
      className="parallax"
    >
      <img
        alt="parallax-img"
        className="parallax-img"
        src={
          Object {
            "default": "test-file-stub",
          }
        }
      />
    </div>
  </div>,
]
`;


================================================
FILE: frontend/src/pages/ParallaxPage/__tests__/parallaxPage.spec.tsx
================================================
import 'materialize-css';
import React from 'react';
import TestRenderer from 'react-test-renderer';

import { ParallaxPage } from '../parallaxPage';

jest.mock('../components/PrismCodes', () => 'PrismCodes');

describe('ParallaxPage', () => {
  it('should renders correctly', () => {
    const renderer = TestRenderer.create(
      <ParallaxPage />,
    );
    expect(renderer).toMatchSnapshot();
    renderer.unmount();
  });
});


================================================
FILE: frontend/src/pages/ParallaxPage/components/PrismCodes/__tests__/__snapshots__/prismCodes.spec.tsx.snap
================================================
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`PrismCodes component 1`] = `
<pre
  className="language-tsx"
>
  <code
    className="col s12 language-tsx"
  >
    
&lt;div&gt;
  &lt;div className='white'&gt;
    &lt;h1 className='page-title'&gt;{t('title')}&lt;/h1&gt;
  &lt;/div&gt;
  &lt;div className='parallax-container'&gt;
    &lt;div className='parallax'&gt;
      &lt;img className='parallax-img' src={require('./assets/images/parallax1.jpg')} alt='parallax-img' /&gt;
    &lt;/div&gt;
  &lt;/div&gt;
  &lt;div className='section white'&gt;
    &lt;div className='row container'&gt;
      &lt;h2 className={styles['parallax-header']}&gt;Parallax&lt;/h2&gt;
      &lt;p className='grey-text text-darken-3'&gt;{t('description')}&lt;/p&gt;
    &lt;/div&gt;
    &lt;div className='row container'&gt;
      &lt;h4 className='light'&gt;Parallax Demo Code&lt;/h4&gt;
      &lt;PrismCodes language='language-tsx'&gt;
        {PARALLAX_CODE}
      &lt;/PrismCodes&gt;
    &lt;/div&gt;
  &lt;/div&gt;
  &lt;div className='parallax-container'&gt;
    &lt;div className='parallax'&gt;
      &lt;img className='parallax-img' src={require('./assets/images/parallax2.jpg')} alt='parallax-img' /&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

  </code>
</pre>
`;


================================================
FILE: frontend/src/pages/ParallaxPage/components/PrismCodes/__tests__/prismCodes.spec.tsx
================================================
import React from 'react';
import renderer from 'react-test-renderer';

import { PARALLAX_CODE } from '../constants/prismCodes';
import PrismCodes from '../prismCodes';

jest.mock('prismjs', () => ({
  highlightAll: () => { return; },
}));

test('PrismCodes component', () => {
  const component = renderer.create(
    <PrismCodes language='language-tsx'>
      {PARALLAX_CODE}
    </PrismCodes>,
  );
  const tree = component.toJSON();
  expect(tree).toMatchSnapshot();
  component.unmount();
});


================================================
FILE: frontend/src/pages/ParallaxPage/components/PrismCodes/constants/prismCodes.tsx
================================================
// Code snippets
export const PARALLAX_CODE = `
<div>
  <div className='white'>
    <h1 className='page-title'>{t('title')}</h1>
  </div>
  <div className='parallax-container'>
    <div className='parallax'>
      <img className='parallax-img' src={require('./assets/images/parallax1.jpg')} alt='parallax-img' />
    </div>
  </div>
  <div className='section white'>
    <div className='row container'>
      <h2 className={styles['parallax-header']}>Parallax</h2>
      <p className='grey-text text-darken-3'>{t('description')}</p>
    </div>
    <div className='row container'>
      <h4 className='light'>Parallax Demo Code</h4>
      <PrismCodes language='language-tsx'>
        {PARALLAX_CODE}
      </PrismCodes>
    </div>
  </div>
  <div className='parallax-container'>
    <div className='parallax'>
      <img className='parallax-img' src={require('./assets/images/parallax2.jpg')} alt='parallax-img' />
    </div>
  </div>
</div>
`;


================================================
FILE: frontend/src/pages/ParallaxPage/components/PrismCodes/index.tsx
================================================
export { default } from './prismCodes';
export * from './constants/prismCodes';


================================================
FILE: frontend/src/pages/ParallaxPage/components/PrismCodes/prismCodes.scss
================================================
code,
pre {
  position: relative;
  font-size: 1em;
}

pre[class*="language-"] {
  padding: 20px 22px;
  border: solid 1px rgba(51, 51, 51, 0.12);
}

pre[class*="language-"]::before {
  position: absolute;
  padding: 1px 5px;
  background: #e8e6e3;
  top: 0;
  left: 0;
  font-family: "Roboto", sans-serif;
  -webkit-font-smoothing: antialiased;
  color: #555;
  content: attr(class);
  font-size: 0.9em;
  border: solid 1px rgba(51, 51, 51, 0.12);
  border-top: none;
  border-left: none;
}


================================================
FILE: frontend/src/pages/ParallaxPage/components/PrismCodes/prismCodes.tsx
================================================
import React from 'react';

import { highlightAll } from 'prismjs';
import 'prismjs/themes/prism.css';
import './prismCodes.scss';

interface IPrismCodesProps {
  language: string;
}

export default class PrismCodes extends React.Component<IPrismCodesProps> {
  public componentDidMount() {
    highlightAll();
  }

  public render() {
    return (
      <pre className={this.props.language}>
        <code className={`col s12 ${this.props.language}`}>
          {this.props.children}
        </code>
      </pre>
    );
  }
}


================================================
FILE: frontend/src/pages/ParallaxPage/index.tsx
================================================
export { default } from './parallaxPage';


================================================
FILE: frontend/src/pages/ParallaxPage/parallaxPage.scss
================================================
@import 'sass/variables';

.parallax-header {
  color: $primary-color;
  font-weight: 300;
}


================================================
FILE: frontend/src/pages/ParallaxPage/parallaxPage.tsx
================================================
import React, { Fragment } from 'react';
import { Translation } from 'react-i18next';

import PrismCodes, { PARALLAX_CODE } from './components/PrismCodes';

const styles = require('./parallaxPage.scss');

export class ParallaxPage extends React.Component {
  public componentDidMount() {
    const elems = document.querySelectorAll('.parallax');
    M.Parallax.init(elems);
  }

  public render() {
    return (
      <Translation ns='parallaxPage'>
        {(t) => (
          <Fragment>
            <div className='white'>
              <h1 className='page-title'>{t('title')}</h1>
            </div>
            <div className='parallax-container'>
              <div className='parallax'>
                <img className='parallax-img' src={require('./assets/images/parallax1.jpg')} alt='parallax-img' />
              </div>
            </div>
            <div className='section white'>
              <div className='row container'>
                <h2 className={styles['parallax-header']}>Parallax</h2>
                <p className='grey-text text-darken-3'>{t('description')}</p>
              </div>
              <div className='row container'>
                <h4 className='light'>Parallax Demo Code</h4>
                <PrismCodes language='language-tsx'>
                  {PARALLAX_CODE}
                </PrismCodes>
              </div>
            </div>
            <div className='parallax-container'>
              <div className='parallax'>
                <img className='parallax-img' src={require('./assets/images/parallax2.jpg')} alt='parallax-img' />
              </div>
            </div>
          </Fragment>
        )}
      </Translation>
    );
  }
}

export default ParallaxPage;


================================================
FILE: frontend/src/pages/ReactPage/__tests__/__snapshots__/reactPage.spec.tsx.snap
================================================
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`ReactPage should renders correctly 1`] = `
<div>
  <div
    className="react-container"
  >
    <h1
      className="page-title"
    >
      title
    </h1>
    <TodoLayout />
    <FetchNote />
  </div>
</div>
`;


================================================
FILE: frontend/src/pages/ReactPage/__tests__/reactPage.spec.tsx
================================================
import React from 'react';
import TestRenderer from 'react-test-renderer';

import { ReactPage } from '../reactPage';

jest.mock('../components/FetchNote', () => 'FetchNote');
jest.mock('../components/TodoLayout', () => 'TodoLayout');

describe('ReactPage', () => {
  it('should renders correctly', () => {
    const renderer = TestRenderer.create(
      <ReactPage />,
    );
    expect(renderer).toMatchSnapshot();
    renderer.unmount();
  });
});


================================================
FILE: frontend/src/pages/ReactPage/components/FetchNote/__tests__/__snapshots__/fetchNote.spec.tsx.snap
================================================
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`FetchNote should renders correctly 1`] = `
<div
  className="center-align z-depth-2 fetch-note-layout"
>
  <span
    className="fetch-note-title"
  >
    fetchNote.asyncCalls
  </span>
  <div
    className="fetchAllNotes"
  >
    <a
      className="btn waves-effect fetch-note-filter-btn"
      onClick={[Function]}
      role="button"
    >
      fetchNote.fetchAllNotes
    </a>
  </div>
</div>
`;


================================================
FILE: frontend/src/pages/ReactPage/components/FetchNote/__tests__/fetchNote.spec.tsx
================================================
import { List } from 'immutable';
import 'materialize-css';
import React from 'react';
import TestRenderer from 'react-test-renderer';

import { FetchNote } from '../fetchNote';

// Mock dispatch
const dispatchMock = () => { return; };

describe('FetchNote', () => {
  it('should renders correctly', () => {
    const renderer = TestRenderer.create(
      <FetchNote
        notes={List<any>()}
        loading={false}
        error={''}
        fetchAllNotes={dispatchMock}
        fetchNote={dispatchMock}
        addNote={dispatchMock}
        editNote={dispatchMock}
        removeNote={dispatchMock}
      />,
    );
    expect(renderer).toMatchSnapshot();
    renderer.unmount();
  });
});


================================================
FILE: frontend/src/pages/ReactPage/components/FetchNote/fetchNote.scss
================================================
@import 'sass/variables';
@import '../../reactPage';

.fetch-note-layout {
  @extend .react-block;
}

.fetch-note-title {
  @extend .react-block-title;
}

.fetch-note-collection {
  margin: 25px 0 0 0;
}

.fetch-note-error-panel {
  margin: 20px 0 0 0;
}

.fetch-note-filter-btn {
  width: 30%;
}

@media only screen and (max-width: $small-screen) {
  .fetch-note-filter-btn {
    width: 80%;
    max-width: 300px;
  }
}


================================================
FILE: frontend/src/pages/ReactPage/components/FetchNote/fetchNote.tsx
================================================
import { List } from 'immutable';
import React from 'react';
import { Translation } from 'react-i18next';
import { connect } from 'react-redux';
import { AnyAction, Dispatch } from 'redux';

import { ADD_NOTE_REQUESTED, EDIT_NOTE_REQUESTED, FETCH_ALL_NOTES_REQUESTED, FETCH_NOTE_REQUESTED, REMOVE_NOTE_REQUESTED } from 'services/notes/constants';
import { INote } from 'services/notes/types';
import { IGlobalStateRecord } from 'types/global';

const styles = require('./fetchNote.scss');

// Component

interface IFetchNoteStateProps {
  notes: List<INote>;
  loading: boolean;
  error: string;
}

interface IFetchNoteDispatchProps {
  fetchAllNotes(): void;
  fetchNote(id: number): void;
  addNote(content: string): void;
  editNote(id: number, content: string): void;
  removeNote(id: number): void;
}

interface IFetchNoteProps extends IFetchNoteStateProps, IFetchNoteDispatchProps { }

export class FetchNote extends React.Component<IFetchNoteProps> {
  public fetchAllNotes = (e: React.MouseEvent<HTMLElement>) => {
    e.preventDefault();
    this.props.fetchAllNotes();
  }

  public render() {
    const noteList = this.props.notes.map((note) =>
      (
        <li className='collection-item'>
          <div>Id: {note.id} &emsp; Content: {note.content}</div>
        </li>
      ),
    );
    const noteListCollection = <ul className={`collection ${styles['fetch-note-collection']}`}> {noteList} </ul>;
    const errorPanel = (
      <div className={`card-panel red ${styles['fetch-note-error-panel']}`}>
        <span className='white-text'>{this.props.error}</span>
      </div>
    );
    return (
      <Translation ns='reactPage'>
        {(t) => (
          <div className={`center-align z-depth-2 ${styles['fetch-note-layout']}`}>
            <span className={styles['fetch-note-title']}>{t('fetchNote.asyncCalls')}</span>
            <div className='fetchAllNotes'>
              <a className={`btn waves-effect ${styles['fetch-note-filter-btn']}`} onClick={this.fetchAllNotes} role='button'>
                {t('fetchNote.fetchAllNotes')}
              </a>
            </div>
            {noteList.count() > 0 && noteListCollection}
            {this.props.error !== '' && errorPanel}
          </div>
        )}
      </Translation>
    );
  }
}

// Container

const mapStateToProps = (state: IGlobalStateRecord): IFetchNoteStateProps => ({
    notes: state.get('notesState').notes,
    loading: state.get('notesState').loading,
    error: state.get('notesState').error,
});

const mapDispatchToProps = (dispatch: Dispatch<AnyAction>): IFetchNoteDispatchProps => ({
  fetchAllNotes: () => {
    dispatch({ type: FETCH_ALL_NOTES_REQUESTED, payload: {} });
  },
  fetchNote: (id: number) => {
    dispatch({ type: FETCH_NOTE_REQUESTED, payload: { id } });
  },
  addNote: (content: string) => {
    dispatch({ type: ADD_NOTE_REQUESTED, payload: { content } });
  },
  editNote: (id: number, content: string) => {
    dispatch({ type: EDIT_NOTE_REQUESTED, payload: { id, content } });
  },
  removeNote: (id: number) => {
    dispatch({ type: REMOVE_NOTE_REQUESTED, payload: { id } });
  },
});

export default connect(
  mapStateToProps,
  mapDispatchToProps,
)(FetchNote);


================================================
FILE: frontend/src/pages/ReactPage/components/FetchNote/index.tsx
================================================
export { default } from './fetchNote';


================================================
FILE: frontend/src/pages/ReactPage/components/TodoLayout/__tests__/__snapshots__/todoLayout.spec.tsx.snap
================================================
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`TodoLayout should renders correctly 1`] = `
<div
  className="center-align z-depth-2 todo-layout"
>
  <span
    className="todo-title"
  >
    title
  </span>
  <TodoInput />
  <TodoList />
  <TodoFooter />
</div>
`;


================================================
FILE: frontend/src/pages/ReactPage/components/TodoLayout/__tests__/todoLayout.spec.tsx
================================================
import React from 'react';
import TestRenderer from 'react-test-renderer';

import { TodoLayout } from '../todoLayout';

jest.mock('../components/TodoFooter', () => 'TodoFooter');
jest.mock('../components/TodoInput', () => 'TodoInput');
jest.mock('../components/TodoList', () => 'TodoList');

describe('TodoLayout', () => {
  it('should renders correctly', () => {
    const renderer = TestRenderer.create(
      <TodoLayout />,
    );
    expect(renderer).toMatchSnapshot();
    renderer.unmount();
  });
});


================================================
FILE: frontend/src/pages/ReactPage/components/TodoLayout/components/TodoFooter/__tests__/__snapshots__/todoFooter.spec.tsx.snap
================================================
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`TodoFooter should renders correctly 1`] = `
<div
  className="todo-footer"
>
  <TodoFilter
    filter="SHOW_ALL"
  >
    todoLayout.todoFooter.all
  </TodoFilter>
  <TodoFilter
    filter="SHOW_ACTIVE"
  >
    todoLayout.todoFooter.active
  </TodoFilter>
  <TodoFilter
    filter="SHOW_COMPLETED"
  >
    todoLayout.todoFooter.completed
  </TodoFilter>
</div>
`;


================================================
FILE: frontend/src/pages/ReactPage/components/TodoLayout/components/TodoFooter/__tests__/todoFooter.spec.tsx
================================================
import React from 'react';
import TestRenderer from 'react-test-renderer';

import { TodoFooter } from '../todoFooter';

jest.mock('../components/TodoFilter', () => 'TodoFilter');

describe('TodoFooter', () => {
  it('should renders correctly', () => {
    const renderer = TestRenderer.create(
      <TodoFooter />,
    );
    expect(renderer).toMatchSnapshot();
    renderer.unmount();
  });
});


================================================
FILE: frontend/src/pages/ReactPage/components/TodoLayout/components/TodoFooter/components/TodoFilter/__tests__/__snapshots__/todoFilter.spec.tsx.snap
================================================
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`TodoFilter should renders correctly 1`] = `
<a
  className="btn waves-effect waves-light todo-filter-btn"
  href="#"
  onClick={[Function]}
>
  test
</a>
`;


================================================
FILE: frontend/src/pages/ReactPage/components/TodoLayout/components/TodoFooter/components/TodoFilter/__tests__/todoFilter.spec.tsx
================================================
import React from 'react';
import TestRenderer from 'react-test-renderer';

import { TodoFilter } from '../todoFilter';

// Mock dispatch
const dispatchMock = () => { return; };

describe('TodoFilter', () => {
  it('should renders correctly', () => {
    const renderer = TestRenderer.create(
      <TodoFilter active={true} setVisibilityFilter={dispatchMock}>
        {'test'}
      </TodoFilter>,
    );
    expect(renderer).toMatchSnapshot();
    renderer.unmount();
  });
});


================================================
FILE: frontend/src/pages/ReactPage/components/TodoLayout/components/TodoFooter/components/TodoFilter/index.tsx
================================================
export { default } from './todoFilter';


================================================
FILE: frontend/src/pages/ReactPage/components/TodoLayout/components/TodoFooter/components/TodoFilter/todoFilter.scss
================================================
@import 'sass/variables';

.todo-filter-btn {
  width: 120px;
  text-align: center;
  margin: 3px;
  padding: 0;
}

@media only screen and (max-width: $small-screen) {
  .todo-filter-btn {
    display: block;
    width: 80%;
    max-width: 300px;
    margin: auto;
  }
}


================================================
FILE: frontend/src/pages/ReactPage/components/TodoLayout/components/TodoFooter/components/TodoFilter/todoFilter.tsx
================================================
import React from 'react';
import { connect } from 'react-redux';
import { AnyAction, Dispatch } from 'redux';

import { setVisibilityFilter } from 'services/todos/actions';
import { IGlobalStateRecord } from 'types/global';

const styles = require('./todoFilter.scss');

// Component

interface ITodoFilterStateProps {
  active: boolean;
}

interface ITodoFilterDispatchProps {
  setVisibilityFilter(): void;
}

interface ITodoFilterProps extends ITodoFilterStateProps, ITodoFilterDispatchProps { }

export class TodoFilter extends React.Component<ITodoFilterProps> {
  public onClick = (e: React.MouseEvent<HTMLElement>) => {
    e.preventDefault();
    this.props.setVisibilityFilter();
  }

  public render() {
    if (this.props.active) {
      return (
        <a href='#' className={`btn waves-effect waves-light ${styles['todo-filter-btn']}`} onClick={this.onClick}>
          {this.props.children}
        </a>
      );
    } else {
      return (
        <a href='#' className={`btn-flat waves-effect waves-light ${styles['todo-filter-btn']}`} onClick={this.onClick}>
          {this.props.children}
        </a>
      );
    }
  }
}

// Container

interface ITodoFilterOwnProps {
  filter: string;
}

const mapStateToProps = (state: IGlobalStateRecord, ownProps: ITodoFilterOwnProps): ITodoFilterStateProps => ({
  active: ownProps.filter === state.get('todosState').visibilityFilter,
});

const mapDispatchToProps = (dispatch: Dispatch<AnyAction>, ownProps: ITodoFilterOwnProps): ITodoFilterDispatchProps => ({
  setVisibilityFilter: () => {
    dispatch(setVisibilityFilter(ownProps.filter));
  },
});

export default connect(
  mapStateToProps,
  mapDispatchToProps,
)(TodoFilter);


================================================
FILE: frontend/src/pages/ReactPage/components/TodoLayout/components/TodoFooter/index.tsx
================================================
export { default } from './todoFooter';


================================================
FILE: frontend/src/pages/ReactPage/components/TodoLayout/components/TodoFooter/todoFooter.tsx
================================================
import React from 'react';
import { Translation } from 'react-i18next';

import TodoFilter from './components/TodoFilter';

export class TodoFooter extends React.Component {
  public render() {
    return (
      <Translation ns='reactPage'>
        {(t) => (
          <div className='todo-footer'>
            <TodoFilter filter='SHOW_ALL'>
              {t('todoLayout.todoFooter.all')}
            </TodoFilter>
            <TodoFilter filter='SHOW_ACTIVE'>
              {t('todoLayout.todoFooter.active')}
            </TodoFilter>
            <TodoFilter filter='SHOW_COMPLETED'>
              {t('todoLayout.todoFooter.completed')}
            </TodoFilter>
          </div>
        )}
      </Translation>
    );
  }
}

export default TodoFooter;


================================================
FILE: frontend/src/pages/ReactPage/components/TodoLayout/components/TodoInput/__tests__/__snapshots__/todoInput.spec.tsx.snap
================================================
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`TodoInput should renders correctly 1`] = `
<form
  onSubmit={[Function]}
>
  <div
    className="input-field"
  >
    <input
      id="input-addTodo"
      type="text"
    />
    <label>
      todoLayout.todoInput.addTodo
    </label>
  </div>
</form>
`;


================================================
FILE: frontend/src/pages/ReactPage/components/TodoLayout/components/TodoInput/__tests__/todoInput.spec.tsx
================================================
import React from 'react';
import TestRenderer from 'react-test-renderer';

import { TodoInput } from '../todoInput';

// Mock dispatch
const dispatchMock = () => { return; };

describe('TodoInput', () => {
  it('should renders correctly', () => {
    const renderer = TestRenderer.create(
      <TodoInput onSubmit={dispatchMock} />,
    );
    expect(renderer).toMatchSnapshot();
    renderer.unmount();
  });
});


================================================
FILE: frontend/src/pages/ReactPage/components/TodoLayout/components/TodoInput/index.tsx
================================================
export { default } from './todoInput';


================================================
FILE: frontend/src/pages/ReactPage/components/TodoLayout/components/TodoInput/todoInput.tsx
================================================
import React, { Fragment } from 'react';
import { Translation } from 'react-i18next';
import { connect } from 'react-redux';
import { AnyAction, Dispatch } from 'redux';

import { addTodo } from 'services/todos/actions';

// Component

interface ITodoInputDispatchProps {
  onSubmit(inputValue: string): void;
}

let input: HTMLInputElement;

export class TodoInput extends React.Component<ITodoInputDispatchProps> {
  public onSubmit = (e: React.FormEvent<HTMLFormElement>): void => {
    e.preventDefault();
    if (!input.value.trim()) {
      return;
    }
    this.props.onSubmit(input.value);
    input.value = '';
  }

  public setInput = (node: HTMLInputElement): void => {
    input = node;
  }

  public render() {
    return (
      <Translation ns='reactPage'>
        {(t) => (
          <Fragment>
            <form onSubmit={this.onSubmit}>
              <div className='input-field'>
                <input id='input-addTodo' type='text' ref={this.setInput} />
                <label>{t('todoLayout.todoInput.addTodo')}</label>
              </div>
            </form>
          </Fragment>
        )}
      </Translation>
    );
  }
}

// Container

const mapDispatchToProps = (dispatch: Dispatch<AnyAction>): ITodoInputDispatchProps => ({
  onSubmit: (inputValue: string) => {
    dispatch(addTodo(inputValue));
  },
});

export default connect(
  null,
  mapDispatchToProps,
)(TodoInput);


================================================
FILE: frontend/src/pages/ReactPage/components/TodoLayout/components/TodoList/__tests__/__snapshots__/todoList.spec.tsx.snap
================================================
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`TodoList should renders correctly 1`] = `null`;


================================================
FILE: frontend/src/pages/ReactPage/components/TodoLayout/components/TodoList/__tests__/todoList.spec.tsx
================================================
import { List } from 'immutable';
import React from 'react';
import TestRenderer from 'react-test-renderer';

import { TodoList } from '../todoList';

jest.mock('../components/Todo', () => 'Todo');

// Mock dispatch
const dispatchMock = () => { return; };

describe('TodoList', () => {
  it('should renders correctly', () => {
    const renderer = TestRenderer.create(
      <TodoList todos={List<any>()} toggleTodo={dispatchMock} />,
    );
    expect(renderer).toMatchSnapshot();
    renderer.unmount();
  });
});


================================================
FILE: frontend/src/pages/ReactPage/components/TodoLayout/components/TodoList/components/Todo/__tests__/__snapshots__/todo.spec.tsx.snap
================================================
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Todo should renders correctly 1`] = `
<a
  className="collection-item waves-effect waves-teal"
  href="#"
  onClick={[Function]}
>
  <div
    className="truncate todo-incompleted"
  >
    text
  </div>
</a>
`;


================================================
FILE: frontend/src/pages/ReactPage/components/TodoLayout/components/TodoList/components/Todo/__tests__/todo.spec.tsx
================================================
import React from 'react';
import TestRenderer from 'react-test-renderer';

import Todo from '../todo';

// Mock dispatch
const dispatchMock = () => { return; };

describe('Todo', () => {
  it('should renders correctly', () => {
    const todo = {
      id: 'id',
      completed: false,
      text: 'text',
    };
    const renderer = TestRenderer.create(
      <Todo {...todo} onClick={dispatchMock} />,
    );
    expect(renderer).toMatchSnapshot();
    renderer.unmount();
  });
});


================================================
FILE: frontend/src/pages/ReactPage/components/TodoLayout/components/TodoList/components/Todo/index.tsx
================================================
export { default } from './todo';


================================================
FILE: frontend/src/pages/ReactPage/components/TodoLayout/components/TodoList/components/Todo/todo.scss
================================================
.todo-completed {
  text-decoration: line-through;
  color: grey;
}

.todo-incompleted {
  text-decoration: none;
}


================================================
FILE: frontend/src/pages/ReactPage/components/TodoLayout/components/TodoList/components/Todo/todo.tsx
================================================
import React from 'react';

import { ITodo } from 'services/todos/types';

const styles = require('./todo.scss');

interface ITodoProps extends ITodo {
  onClick(e: React.MouseEvent<HTMLElement>): void;
}

export default class Todo extends React.Component<ITodoProps> {
  public onClick = (e: React.MouseEvent<HTMLElement>) => {
    e.preventDefault();
    this.props.onClick(e);
  }

  public render() {
    if (this.props.completed) {
      return (
        <a href='#' className='collection-item waves-effect' onClick={this.onClick} >
          <div className={`truncate ${styles['todo-completed']}`}>
            {this.props.text}
          </div>
        </a>
      );
    } else {
      return (
        <a href='#' className='collection-item waves-effect waves-teal' onClick={this.onClick} >
          <div className={`truncate ${styles['todo-incompleted']}`}>
            {this.props.text}
          </div>
        </a>
      );
    }
  }
}


================================================
FILE: frontend/src/pages/ReactPage/components/TodoLayout/components/TodoList/index.tsx
================================================
export { default } from './todoList';


================================================
FILE: frontend/src/pages/ReactPage/components/TodoLayout/components/TodoList/todoList.tsx
================================================
import { List } from 'immutable';
import React from 'react';
import { connect } from 'react-redux';
import { AnyAction, Dispatch } from 'redux';

import { toggleTodo } from 'services/todos/actions';
import { VISIBILITY_FILTER_OPTIONS } from 'services/todos/constants';
import { ITodo, ITodosStateRecord } from 'services/todos/types';
import { IGlobalStateRecord } from 'types/global';
import Todo from './components/Todo';

// Component

interface ITodoListStateProps {
  todos: List<ITodo>;
}

interface ITodoListDispatchProps {
  toggleTodo(id: string): void;
}

interface ITodoListProps extends ITodoListStateProps, ITodoListDispatchProps { }

export class TodoList extends React.Component<ITodoListProps> {
  public onClick = (id: string) => (e: React.MouseEvent<HTMLElement>) => {
    e.preventDefault();
    this.props.toggleTodo(id);
  }

  public render() {
    const todoList = this.props.todos.map((todo) =>
      <Todo key={todo.id} {...todo} onClick={this.onClick(todo.id)} />,
    );
    if (todoList.count() > 0) {
      return (
        <ul className='collection'>
          {todoList}
        </ul>
      );
    } else {
      return (null);
    }
  }
}

// Container

const getVisibleTodos = (todosState: ITodosStateRecord): List<ITodo> => {
  switch (todosState.visibilityFilter) {
    case VISIBILITY_FILTER_OPTIONS.SHOW_ALL:
      return todosState.todos;
    case VISIBILITY_FILTER_OPTIONS.SHOW_COMPLETED:
      return todosState.todos.filter((t) => t !== undefined && t.completed);
    case VISIBILITY_FILTER_OPTIONS.SHOW_ACTIVE:
      return todosState.todos.filter((t) => t !== undefined && !t.completed);
    default:
      return todosState.todos;
  }
};

const mapStateToProps = (state: IGlobalStateRecord): ITodoListStateProps => ({
  todos: getVisibleTodos(state.get('todosState')),
});

const mapDispatchToProps = (dispatch: Dispatch<AnyAction>): ITodoListDispatchProps => ({
  toggleTodo: (id: string) => {
    dispatch(toggleTodo(id));
  },
});

export default connect(
  mapStateToProps,
  mapDispatchToProps,
)(TodoList);


================================================
FILE: frontend/src/pages/ReactPage/components/TodoLayout/index.tsx
================================================
export { default } from './todoLayout';


================================================
FILE: frontend/src/pages/ReactPage/components/TodoLayout/todoLayout.scss
================================================
@import 'sass/variables';
@import '../../reactPage';

.todo-layout {
  @extend .react-block;
}

.todo-title {
  @extend .react-block-title;
}


================================================
FILE: frontend/src/pages/ReactPage/components/TodoLayout/todoLayout.tsx
================================================
import React from 'react';
import { Translation } from 'react-i18next';

import TodoFooter from './components/TodoFooter';
import TodoInput from './components/TodoInput';
import TodoList from './components/TodoList';

const styles = require('./todoLayout.scss');

export class TodoLayout extends React.Component {
  public render() {
    return (
      <Translation ns='reactPage'>
        {(t) => (
          <div className={`center-align z-depth-2 ${styles['todo-layout']}`}>
            <span className={styles['todo-title']}>{t('title')}</span>
            <TodoInput />
            <TodoList />
            <TodoFooter />
          </div>
        )}
      </Translation>
    );
  }
}

export default TodoLayout;


================================================
FILE: frontend/src/pages/ReactPage/index.tsx
================================================
export { default } from './reactPage';


================================================
FILE: frontend/src/pages/ReactPage/reactPage.scss
================================================
@import 'sass/variables';

.react-block {
  border: 1px lightgray solid;
  margin: 50px auto;
  width: 850px;
  padding: 20px 30px 40px;
}

@media only screen and (max-width: $medium-screen) {
  .react-block {
    max-width: 80%;
  }
}

@media only screen and (max-width: $small-screen) {
  .react-block {
    max-width: 85%;
  }
}

.react-block-title {
  display: block;
  font-weight: 100;
  font-size: 4.5em;
  color: $secondary-color;
  margin-bottom: 20px;
}

@media only screen and (max-width: $medium-screen) {
  .react-block-title {
    font-weight: 100;
    font-size: 4em;
  }
}

@media only screen and (max-width: $small-screen) {
  .react-block-title {
    font-weight: 100;
    font-size: 3em;
  }
}


================================================
FILE: frontend/src/pages/ReactPage/reactPage.tsx
================================================
import React from 'react';
import { Translation } from 'react-i18next';

import FetchNote from './components/FetchNote';
import TodoLayout from './components/TodoLayout';

export class ReactPage extends React.Component {
  public render() {
    return (
      <Translation ns='reactPage'>
        {(t) => (
          <div>
            <div className='react-container'>
              <h1 className='page-title'>{t('title')}</h1>
              <TodoLayout />
              <FetchNote />
            </div>
          </div>
        )}
      </Translation>
    );
  }
}

export default ReactPage;


================================================
FILE: frontend/src/reducers/index.tsx
================================================
import { connectRouter } from 'connected-react-router/immutable';
import { History } from 'history';
import { combineReducers } from 'redux-immutable';

import notesReducer from 'services/notes/reducer';
import todosReducer from 'services/todos/reducer';
import { IGlobalState } from 'types/global';

export default (history: History) => combineReducers<IGlobalState>({
  router: connectRouter(history),
  notesState: notesReducer,
  todosState: todosReducer,
});


================================================
FILE: frontend/src/router.tsx
================================================
import React, { Fragment, lazy, Suspense } from 'react';
import { Route, Switch } from 'react-router';

import 'materialize-css';
import 'sass/global';

import { BodyContentLoader, FooterContentLoader, HeaderContentLoader } from 'components/ContentLoader';
import Footer from 'components/Footer';
import Header from 'components/Header';

const HomePage = lazy(() => import(
  /*
    webpackChunkName: "home-page",
    webpackPreload: true
  */
  'pages/HomePage'));

const NotFoundPage = lazy(() => import(
  /*
    webpackChunkName: "not-found-page",
    webpackPrefetch: true
  */
 'pages/NotFoundPage'));

const ParallaxPage = lazy(() => import(
  /*
    webpackChunkName: "parallax-page",
    webpackPrefetch: true
  */
  'pages/ParallaxPage'));

const ReactPage = lazy(() => import(
  /*
    webpackChunkName: "react-page",
    webpackPrefetch: true
  */
  'pages/ReactPage'));

export default (
  <Fragment>
    <Suspense fallback={<HeaderContentLoader />}>
      <Header />
    </Suspense>
    <Suspense fallback={<BodyContentLoader />}>
      <Switch>
        <Route exact path='/' component={HomePage} />
        <Route path='/react' component={ReactPage} />
        <Route path='/parallax' component={ParallaxPage} />
        <Route component={NotFoundPage} />
      </Switch>
    </Suspense>
    <Suspense fallback={<FooterContentLoader />}>
      <Footer />
    </Suspense>
  </Fragment>
);


================================================
FILE: frontend/src/sagas/index.tsx
================================================
import { all } from 'redux-saga/effects';

import notes from 'services/notes/sagas';

export default function* sagas() {
  yield all([
    notes(),
  ]);
}


================================================
FILE: frontend/src/sass/global.scss
================================================
@import "variables";

:global {
  @import "~materialize-css/sass/materialize";

  .container {
    height: 100%;
  }

  .page-title {
    color: $primary-color;
    text-align: center;
  }

  @media only screen and (max-width: $medium-screen) {
    .page-title {
      font-size: 4em;
    }
  }

  @media only screen and (max-width: $small-screen) {
    .page-title {
      font-size: 2.5em;
    }
  }

  // Highlight css-module caused undefined class
  .undefined {
    border: solid white 3px;
    outline: solid red 3px;
  }
}


================================================
FILE: frontend/src/sass/variables.scss
================================================
// This file override materialize css variables
// https://github.com/Dogfalo/materialize/blob/master/sass/components/_variables.scss

$primary-color: #2196F3;
$secondary-color: #26A69A;
$carousel-height: 80%;
$small-screen: 600px;
$medium-screen: 992px;
$large-screen: 1200px;


================================================
FILE: frontend/src/services/notes/actions.tsx
================================================
import {ADD_NOTE_REQUESTED, EDIT_NOTE_REQUESTED, FETCH_ALL_NOTES_REQUESTED, FETCH_NOTE_REQUESTED, REMOVE_NOTE_REQUESTED } from './constants';
import { IActionAddNoteRequested, IActionEditNoteRequested, IActionFetchAllNotesRequested, IActionFetchNoteRequested, IActionRemoveNoteRequested } from './types';

export const fetchAllNotes = (payload: IActionFetchAllNotesRequested['payload']): IActionFetchAllNotesRequested => ({
  type: FETCH_ALL_NOTES_REQUESTED,
  payload,
});

export const fetchNote = (payload: IActionFetchNoteRequested['payload']): IActionFetchNoteRequested => ({
  type: FETCH_NOTE_REQUESTED,
  payload,
});

export const editNote = (payload: IActionEditNoteRequested['payload']): IActionEditNoteRequested => ({
  type: EDIT_NOTE_REQUESTED,
  payload,
});

export const addNote = (payload: IActionAddNoteRequested['payload']): IActionAddNoteRequested => ({
  type: ADD_NOTE_REQUESTED,
  payload,
});

export const removeNote = (payload: IActionRemoveNoteRequested['payload']): IActionRemoveNoteRequested => ({
  type: REMOVE_NOTE_REQUESTED,
  payload,
});


================================================
FILE: frontend/src/services/notes/apis.tsx
================================================
import axios from 'axios';

import { IActionAddNoteRequested, IActionEditNoteRequested, IActionFetchNoteRequested, IActionRemoveNoteRequested } from './types';

const notesUrl = 'api/v1/notes';

export default class NotesAPI {
  public static fetchAll() {
    return axios.get(`${notesUrl}`, {
      headers: {
        Accept: 'application/json',
      },
    }).then((res) => {
      return res.data;
    }).catch((err) => {
      if (err.response != null) {
        throw Error(err.response.data.error.message);
      }
      throw Error(err);
    });
  }

  public static fetch(payload: IActionFetchNoteRequested['payload']) {
    return axios.get(`${notesUrl}/${payload.id}`, {
      headers: {
        Accept: 'application/json',
      },
    }).then((res) => {
      return res.data;
    }).catch((err) => {
      if (err.response != null) {
        throw Error(err.response.data.error.message);
      }
      throw Error(err);
    });
  }

  public static add(payload: IActionAddNoteRequested['payload']) {
    return axios.post(notesUrl, payload, {
      headers: {
        'Content-Type': 'application/json',
        'Accept': 'application/json',
      },
    }).then((res) => {
      return res.data;
    }).catch((err) => {
      if (err.response != null) {
        throw Error(err.response.data.error.message);
      }
      throw Error(err);
    });
  }

  public static edit(payload: IActionEditNoteRequested['payload']) {
    return axios.put(`${notesUrl}/${payload.id}`, payload, {
      headers: {
        'Content-Type': 'application/json',
        'Accept': 'application/json',
      },
    }).then((res) => {
      return res.data;
    }).catch((err) => {
      if (err.response != null) {
        throw Error(err.response.data.error.message);
      }
      throw Error(err);
    });
  }

  public static remove(payload: IActionRemoveNoteRequested['payload']) {
    return axios.delete(`${notesUrl}/${payload.id}`, {
      headers: {
        Accept: 'application/json',
      },
    }).then((res) => {
      return res.data;
    }).catch((err) => {
      if (err.response != null) {
        throw Error(err.response.data.error.message);
      }
      throw Error(err);
    });
  }
}


================================================
FILE: frontend/src/services/notes/constants.tsx
================================================
import { IAsyncCall } from 'types/global';

export const FETCH_ALL_NOTES_REQUESTED = 'FETCH_ALL_NOTES/REQUESTED';
export const FETCH_ALL_NOTES_SUCCESS = 'FETCH_ALL_NOTES/SUCCESS';
export const FETCH_ALL_NOTES_FAILURE = 'FETCH_ALL_NOTES/FAILURE';
export const ASYNC_FETCH_ALL_NOTES: IAsyncCall = {
  REQUESTED: FETCH_ALL_NOTES_REQUESTED,
  SUCCESS: FETCH_ALL_NOTES_SUCCESS,
  FAILURE: FETCH_ALL_NOTES_FAILURE,
};
export const FETCH_NOTE_REQUESTED = 'FETCH_NOTE/REQUESTED';
export const FETCH_NOTE_SUCCESS = 'FETCH_NOTE/SUCCESS';
export const FETCH_NOTE_FAILURE = 'FETCH_NOTE/FAILURE';
export const ASYNC_FETCH_NOTE: IAsyncCall = {
  REQUESTED: FETCH_NOTE_REQUESTED,
  SUCCESS: FETCH_NOTE_SUCCESS,
  FAILURE: FETCH_NOTE_FAILURE,
};
export const EDIT_NOTE_REQUESTED = 'EDIT_NOTE/REQUESTED';
export const EDIT_NOTE_SUCCESS = 'EDIT_NOTE/SUCCESS';
export const EDIT_NOTE_FAILURE = 'EDIT_NOTE/FAILURE';
export const ASYNC_EDIT_NOTE: IAsyncCall = {
  REQUESTED: EDIT_NOTE_REQUESTED,
  SUCCESS: EDIT_NOTE_SUCCESS,
  FAILURE: EDIT_NOTE_FAILURE,
};
export const ADD_NOTE_REQUESTED = 'ADD_NOTE/REQUESTED';
export const ADD_NOTE_SUCCESS = 'ADD_NOTE/SUCCESS';
export const ADD_NOTE_FAILURE = 'ADD_NOTE/FAILURE';
export const ASYNC_ADD_NOTE: IAsyncCall = {
  REQUESTED: ADD_NOTE_REQUESTED,
  SUCCESS: ADD_NOTE_SUCCESS,
  FAILURE: ADD_NOTE_FAILURE,
};
export const REMOVE_NOTE_REQUESTED = 'REMOVE_NOTE/REQUESTED';
export const REMOVE_NOTE_SUCCESS = 'REMOVE_NOTE/SUCCESS';
export const REMOVE_NOTE_FAILURE = 'REMOVE_NOTE/FAILURE';
export const ASYNC_REMOVE_NOTE: IAsyncCall = {
  REQUESTED: REMOVE_NOTE_REQUESTED,
  SUCCESS: REMOVE_NOTE_SUCCESS,
  FAILURE: REMOVE_NOTE_FAILURE,
};


================================================
FILE: frontend/src/services/notes/reducer.tsx
================================================
import { List, Record } from 'immutable';

import { FETCH_ALL_NOTES_FAILURE, FETCH_ALL_NOTES_REQUESTED, FETCH_ALL_NOTES_SUCCESS } from './constants';
import { IActionsNotes, INote, INotesState, INotesStateRecord } from './types';

export const getNotesStateRecord = (state: INotesState): INotesStateRecord => {
  class NotesStateRecord extends Record(state) implements INotesStateRecord {}
  return new NotesStateRecord();
};

const initialState = getNotesStateRecord({
  notes: List<INote>(),
  loading: false,
  error: '',
});

export default (state: INotesStateRecord = initialState, action: IActionsNotes): INotesStateRecord => {
  switch (action.type) {
    case FETCH_ALL_NOTES_REQUESTED:
      return state.set('loading', true);
    case FETCH_ALL_NOTES_SUCCESS:
      const noteList: INote[] = [];
      action.payload.data.notes.forEach((note: INote) => {
        noteList.push({
          id: note.id,
          content: note.content,
        });
      });
      return state.clear().set('notes', List(noteList));
    case FETCH_ALL_NOTES_FAILURE:
      return state.clear().set('error', action.payload.error);
    default:
      return state;
  }
};


================================================
FILE: frontend/src/services/notes/sagas.tsx
================================================
import { all, call, put, takeEvery } from 'redux-saga/effects';

import { IAsyncCall } from 'types/global';
import NotesAPI from './apis';
import { ASYNC_ADD_NOTE, ASYNC_EDIT_NOTE, ASYNC_FETCH_ALL_NOTES, ASYNC_FETCH_NOTE, ASYNC_REMOVE_NOTE } from './constants';

function* asyncHandler(action: IAsyncCall, api: (payload: any) => Promise<any>, payload: any) {
  try {
    const resJson = yield call(api, payload);
    yield put({ type: action.SUCCESS, payload: { data: resJson.data } });
  } catch (err) {
    yield put({ type: action.FAILURE, payload: { error: err.message } });
  }
}

function* sagaAsyncCallGenerator(action: IAsyncCall, api: (payload: any) => Promise<any>) {
  yield takeEvery(action.REQUESTED, asyncHandler, action, api);
}

export default function* rootSaga() {
  yield all([
    sagaAsyncCallGenerator(ASYNC_FETCH_ALL_NOTES, NotesAPI.fetchAll),
    sagaAsyncCallGenerator(ASYNC_FETCH_NOTE, NotesAPI.fetch),
    sagaAsyncCallGenerator(ASYNC_ADD_NOTE, NotesAPI.add),
    sagaAsyncCallGenerator(ASYNC_EDIT_NOTE, NotesAPI.edit),
    sagaAsyncCallGenerator(ASYNC_REMOVE_NOTE, NotesAPI.remove),
  ]);
}


================================================
FILE: frontend/src/services/notes/types.d.ts
================================================
import { List, Record } from 'immutable';

import { 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';

// Notes state
export interface INotesState {
  notes: List<INote>;
  loading: boolean;
  error: string;
}
export interface INote {
  id: number;
  content: string;
}
export interface INotesStateRecord extends Record<INotesState>, INotesState {}

// Notes actions
export interface IActionFetchAllNotesRequested {
  type: typeof FETCH_ALL_NOTES_REQUESTED;
  payload: {};
}
export interface IActionFetchAllNotesSuccess {
  type: typeof FETCH_ALL_NOTES_SUCCESS;
  payload: {
    data: any,
  };
}
export interface IActionFetchAllNotesFailure {
  type: typeof FETCH_ALL_NOTES_FAILURE;
  payload: {
    error: string,
  };
}

export interface IActionFetchNoteRequested {
  type: typeof FETCH_NOTE_REQUESTED;
  payload: {
    id: number,
  };
}
export interface IActionFetchNoteSuccess {
  type: typeof FETCH_NOTE_SUCCESS;
  payload: {
    data: any,
  };
}
export interface IActionFetchNoteFailure {
  type: typeof FETCH_NOTE_FAILURE;
  payload: {
    error: string,
  };
}

export interface IActionAddNoteRequested {
  type: typeof ADD_NOTE_REQUESTED;
  payload: {
    content: string,
  };
}
export interface IActionAddNoteSuccess {
  type: typeof ADD_NOTE_SUCCESS;
  payload: {
    data: any,
  };
}
export interface IActionAddNoteFailure {
  type: typeof ADD_NOTE_FAILURE;
  payload: {
    error: string,
  };
}

export interface IActionEditNoteRequested {
  type: typeof EDIT_NOTE_REQUESTED;
  payload: {
    id: number,
    content: string,
  };
}
export interface IActionEditNoteSuccess {
  type: typeof EDIT_NOTE_SUCCESS;
  payload: {
    data: any,
  };
}
export interface IActionEditNoteFailure {
  type: typeof EDIT_NOTE_FAILURE;
  payload: {
    error: string,
  };
}

export interface IActionRemoveNoteRequested {
  type: typeof REMOVE_NOTE_REQUESTED;
  payload: {
    id: number,
  };
}
export interface IActionRemoveNoteSuccess {
  type: typeof REMOVE_NOTE_SUCCESS;
  payload: {
    data: any,
  };
}
export interface IActionRemoveNoteFailure {
  type: typeof REMOVE_NOTE_FAILURE;
  payload: {
    error: string,
  };
}

export type IActionsNotes
  = IActionFetchAllNotesRequested
  | IActionFetchAllNotesSuccess
  | IActionFetchAllNotesFailure
  | IActionFetchNoteRequested
  | IActionFetchNoteSuccess
  | IActionFetchNoteFailure
  | IActionAddNoteRequested
  | IActionAddNoteSuccess
  | IActionAddNoteFailure
  | IActionEditNoteRequested
  | IActionEditNoteSuccess
  | IActionEditNoteFailure
  | IActionRemoveNoteRequested
  | IActionRemoveNoteSuccess
  | IActionRemoveNoteFailure;


================================================
FILE: frontend/src/services/todos/__test__/actions.spec.tsx
================================================
import { addTodo, setVisibilityFilter, toggleTodo } from '../actions';
import { ADD_TODO, SET_VISIBILITY_FILTER, TOGGLE_TODO, VISIBILITY_FILTER_OPTIONS } from '../constants';
import { IActionAddTodo, IActionSetVisibilityFilter, IActionToggleTodo } from '../types';

describe('[Actions] todos test', () => {
  it('[addTodo] should return IActionAddTodo with input text, random id string and completed as false', () => {
    const actionAddTodo: IActionAddTodo = addTodo('text');
    expect(actionAddTodo.type === ADD_TODO).toBeTruthy();
    expect(typeof actionAddTodo.id === 'string').toBeTruthy();
    expect(actionAddTodo.text).toBe('text');
    expect(actionAddTodo.completed).toBe(false);
  });

  it('[toggleTodo] should return IActionToggleTodo with input id', () => {
    const actionToggleTodo: IActionToggleTodo = toggleTodo('id');
    expect(actionToggleTodo.type === TOGGLE_TODO).toBeTruthy();
    expect(actionToggleTodo.id).toBe('id');
  });

  it('[setVisibilityFilter] should return IActionSetVisibilityFilter with input filter', () => {
    const actionSetVisibilityShowAll: IActionSetVisibilityFilter = setVisibilityFilter(VISIBILITY_FILTER_OPTIONS.SHOW_ALL);
    expect(actionSetVisibilityShowAll.type === SET_VISIBILITY_FILTER).toBeTruthy();
    expect(actionSetVisibilityShowAll.filter).toBe(VISIBILITY_FILTER_OPTIONS.SHOW_ALL);

    const actionSetVisibilityShowActive: IActionSetVisibilityFilter = setVisibilityFilter(VISIBILITY_FILTER_OPTIONS.SHOW_ACTIVE);
    expect(actionSetVisibilityShowActive.type === SET_VISIBILITY_FILTER).toBeTruthy();
    expect(actionSetVisibilityShowActive.filter).toBe(VISIBILITY_FILTER_OPTIONS.SHOW_ACTIVE);

    const actionSetVisibilityShowComleted: IActionSetVisibilityFilter = setVisibilityFilter(VISIBILITY_FILTER_OPTIONS.SHOW_COMPLETED);
    expect(actionSetVisibilityShowComleted.type === SET_VISIBILITY_FILTER).toBeTruthy();
    expect(actionSetVisibilityShowComleted.filter).toBe(VISIBILITY_FILTER_OPTIONS.SHOW_COMPLETED);
  });
});


================================================
FILE: frontend/src/services/todos/__test__/reducer.spec.tsx
================================================
import { List } from 'immutable';

import { ADD_TODO, SET_VISIBILITY_FILTER, TOGGLE_TODO, VISIBILITY_FILTER_OPTIONS } from '../constants';
import reducer, { getTodosStateRecord } from '../reducer';
import { IActionAddTodo, IActionSetVisibilityFilter, IActionToggleTodo, ITodo } from '../types';

describe('[Reducers] todos test', () => {
  const initialState = getTodosStateRecord({
    todos: List<ITodo>([
      {
        id: 'initial_id',
        text: 'initial_text',
        completed: false,
      },
    ]),
    visibilityFilter: VISIBILITY_FILTER_OPTIONS.SHOW_ALL,
  });

  const actionAddTodo: IActionAddTodo = {
    type: ADD_TODO,
    id: 'test_id',
    text: 'test_text',
    completed: false,
  };
  const actionToggleTodo: IActionToggleTodo = {
    type: TOGGLE_TODO,
    id: 'initial_id',
  };
  const actionToggleTodoInvalid: IActionToggleTodo = {
    type: TOGGLE_TODO,
    id: 'invalid_id',
  };
  const actionShowAll: IActionSetVisibilityFilter = {
    type: SET_VISIBILITY_FILTER,
    filter: VISIBILITY_FILTER_OPTIONS.SHOW_ALL,
  };
  const actionShowActive: IActionSetVisibilityFilter = {
    type: SET_VISIBILITY_FILTER,
    filter: VISIBILITY_FILTER_OPTIONS.SHOW_ACTIVE,
  };
  const actionShowCompleted: IActionSetVisibilityFilter = {
    type: SET_VISIBILITY_FILTER,
    filter: VISIBILITY_FILTER_OPTIONS.SHOW_COMPLETED,
  };

  beforeEach(() => {
    expect(initialState.todos.count()).toBe(1);
    expect(initialState.visibilityFilter).toBe(VISIBILITY_FILTER_OPTIONS.SHOW_ALL);
  });

  it('[ADD_TODO] should return state with new todo added', () => {
    const stateAddTodo = reducer(initialState, actionAddTodo);
    expect(stateAddTodo.visibilityFilter).toEqual(initialState.visibilityFilter);
    expect(stateAddTodo.todos).toEqual(List([
      {
        id: 'initial_id',
        text: 'initial_text',
        completed: false,
      },
      {
        id: 'test_id',
        text: 'test_text',
        completed: false,
      },
    ]));
  });

  it('[TOGGLE_TODO] should return state with one todo completed toggled', () => {
    const stateToggleTodo = reducer(initialState, actionToggleTodo);
    expect(stateToggleTodo.visibilityFilter).toEqual(initialState.visibilityFilter);
    expect(stateToggleTodo.todos).toEqual(List([
      {
        id: 'initial_id',
        text: 'initial_text',
        completed: true,
      },
    ]));
  });

  it('[TOGGLE_TODO] should return previous state if id is not found', () => {
    const stateToggleTodoInvalid = reducer(initialState, actionToggleTodoInvalid);
    expect(stateToggleTodoInvalid.visibilityFilter).toEqual(initialState.visibilityFilter);
    expect(stateToggleTodoInvalid.todos).toEqual(initialState.todos);
  });

  it('[SET_VISIBILITY_FILTER] should return state with corresponding filter', () => {
    const stateShowCompleted = reducer(initialState, actionShowCompleted);
    expect(stateShowCompleted.visibilityFilter).toBe(VISIBILITY_FILTER_OPTIONS.SHOW_COMPLETED);
    expect(stateShowCompleted.todos).toEqual(initialState.todos);

    const stateShowActive = reducer(stateShowCompleted, actionShowActive);
    expect(stateShowActive.visibilityFilter).toBe(VISIBILITY_FILTER_OPTIONS.SHOW_ACTIVE);
    expect(stateShowActive.todos).toEqual(initialState.todos);

    const stateShowAll = reducer(stateShowActive, actionShowAll);
    expect(stateShowAll.visibilityFilter).toBe(VISIBILITY_FILTER_OPTIONS.SHOW_ALL);
    expect(stateShowAll.todos).toEqual(initialState.todos);
  });

  it('[DEFAULT_ACTION] should return previous state if action is not found', () => {
    const stateTestDefault = reducer(initialState, {} as any);
    expect(stateTestDefault.visibilityFilter).toEqual(initialState.visibilityFilter);
    expect(stateTestDefault.todos).toEqual(initialState.todos);
  });

  it('[UNDEFINED_STATE] should use default state if state is not defined', () => {
    const stateTestUndefined = reducer(undefined, {} as any);
    expect(stateTestUndefined.visibilityFilter).toEqual(initialState.visibilityFilter);
    expect(stateTestUndefined.todos).toEqual(List([
      {
        id: 'fake_id',
        text: 'Add your own todo task above, click to mark each todo as completed',
        completed: false,
      },
    ]));
  });
});


================================================
FILE: frontend/src/services/todos/actions.tsx
================================================
import { v4 } from 'uuid';

import { ADD_TODO, SET_VISIBILITY_FILTER, TOGGLE_TODO } from './constants';
import { IActionAddTodo, IActionSetVisibilityFilter, IActionToggleTodo } from './types';

export const addTodo = (text: string): IActionAddTodo => ({
  type: ADD_TODO,
  id: v4(),
  text,
  completed: false,
});

export const toggleTodo = (id: string): IActionToggleTodo => ({
  type: TOGGLE_TODO,
  id,
});

export const setVisibilityFilter = (filter: string): IActionSetVisibilityFilter => ({
  type: SET_VISIBILITY_FILTER,
  filter,
});


================================================
FILE: frontend/src/services/todos/constants.tsx
================================================
// Todo action types
export const ADD_TODO = 'ADD_TODO';
export const TOGGLE_TODO = 'TOGGLE_TODO';

// Visibility action types
export const SET_VISIBILITY_FILTER = 'SET_VISIBILITY_FILTER';

// Visibility filter options
export const VISIBILITY_FILTER_OPTIONS = {
  SHOW_ALL: 'SHOW_ALL',
  SHOW_COMPLETED: 'SHOW_COMPLETED',
  SHOW_ACTIVE: 'SHOW_ACTIVE',
};


================================================
FILE: frontend/src/services/todos/reducer.tsx
================================================
import { List, Record } from 'immutable';

import { ADD_TODO, SET_VISIBILITY_FILTER, TOGGLE_TODO, VISIBILITY_FILTER_OPTIONS } from './constants';
import { IActionsTodo, ITodo, ITodosState, ITodosStateRecord } from './types';

export const getTodosStateRecord = (state: ITodosState): ITodosStateRecord => {
  class TodosStateRecord extends Record(state) implements ITodosStateRecord {}
  return new TodosStateRecord();
};

const initialState = getTodosStateRecord({
  todos: List<ITodo>([
    {
      id: 'fake_id',
      text: 'Add your own todo task above, click to mark each todo as completed',
      completed: false,
    },
  ]),
  visibilityFilter: VISIBILITY_FILTER_OPTIONS.SHOW_ALL,
});

export default (state: ITodosStateRecord = initialState, action: IActionsTodo): ITodosStateRecord => {
  switch (action.type) {
    case ADD_TODO:
      return state.update('todos', (todos) => todos.push({
        id: action.id,
        text: action.text,
        completed: action.completed,
      }));
    case TOGGLE_TODO:
      const index = state.todos.findIndex((s) => s !== undefined && s.id === action.id);
      return state.update('todos', (todos) => index === -1 ? todos : todos.update(index, (s) => ({ ...s, completed: !s.completed })));
    case SET_VISIBILITY_FILTER:
      return state.set('visibilityFilter', action.filter);
    default:
      return state;
  }
};


================================================
FILE: frontend/src/services/todos/types.d.ts
================================================
import { List, Record } from 'immutable';

import { ADD_TODO, SET_VISIBILITY_FILTER, TOGGLE_TODO } from './constants';

// Todos state
export interface ITodosState {
  todos: List<ITodo>;
  visibilityFilter: string;
}
export interface ITodo {
  id: string;
  text: string;
  completed: boolean;
}
export interface ITodosStateRecord extends Record<ITodosState>, ITodosState {}

// Todos actions
export interface IActionAddTodo {
  type: typeof ADD_TODO;
  id: string;
  text: string;
  completed: boolean;
}
export interface IActionToggleTodo {
  type: typeof TOGGLE_TODO;
  id: string;
}
export interface IActionSetVisibilityFilter {
  type: typeof SET_VISIBILITY_FILTER;
  filter: string;
}

export type IActionsTodo
  = IActionAddTodo
  | IActionToggleTodo
  | IActionSetVisibilityFilter;


================================================
FILE: frontend/src/store/index.tsx
================================================
import { routerMiddleware } from 'connected-react-router/immutable';
import { History } from 'history';
import Immutable from 'immutable';
import { applyMiddleware, compose, createStore, Store } from 'redux';
import logger from 'redux-logger';
import createSagaMiddleware from 'redux-saga';

import createRootReducer from 'reducers';
import sagas from 'sagas';
import { IGlobalState } from 'types/global';

export default (history: History, initialState: IGlobalState | {}): Store<IGlobalState> => {
  // Create the saga middleware
  const sagaMiddleware = createSagaMiddleware();

  // Enhancer
  const composeEnhancers =
    typeof window === 'object' && (window as any).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?
      (window as any).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({
        serialize: {
          immutable: Immutable,
        },
      }) : compose;
  const enhancer = composeEnhancers(
    applyMiddleware(routerMiddleware(history), sagaMiddleware, logger),
  );

  // Store
  const store = createStore(createRootReducer(history), initialState, enhancer);
  sagaMiddleware.run(sagas);

  // Enable Webpack hot module replacement for reducers
  if (module.hot) {
    module.hot.accept('../reducers', () => {
      store.replaceReducer(createRootReducer(history));
    });
  }

  return store;
};


================================================
FILE: frontend/src/types/global.d.ts
================================================
import { RouterState } from 'connected-react-router/immutable';
import { Record } from 'immutable';

import { INotesState, INotesStateRecord } from 'services/notes/types';
import { ITodosState, ITodosStateRecord } from 'services/todos/types';

// Global state
export interface IGlobalState {
  todosState: ITodosStateRecord;
  notesState: INotesStateRecord;
  router: RouterState;
}
export interface IGlobalStateRecord extends Record<IGlobalState>, IGlobalState {}

// Interface for async call steps
export interface IAsyncCall {
  REQUESTED: string;
  SUCCESS: string;
  FAILURE: string;
}


================================================
FILE: frontend/src/utils/index.tsx
================================================
export const isProduction = process.env.NODE_ENV === 'production';


================================================
FILE: package.json
================================================
{
  "name": "express-webpack-react-redux-typescript-boilerplate",
  "version": "1.0.0",
  "repository": {
    "type": "git",
    "url": "https://github.com/Armour/express-webpack-react-redux-typescript-boilerplate.git"
  },
  "author": "Chong Guo <armourcy@gmail.com>",
  "license": "MIT",
  "scripts": {
    "clean": "trash frontend/dist coverage",
    "dll": "cross-env NODE_ENV=production webpack --config webpack.config.dll.babel.js",
    "dev": "yarn clean && yarn dll && concurrently \"yarn dev-server\" \"yarn dev-client\"",
    "dev-server": "cross-env NODE_ENV=development nodemon --exec babel-node backend/server.js",
    "dev-client": "cross-env NODE_ENV=development webpack-dev-server --hot --config webpack.config.dev.babel.js",
    "prod": "yarn build && yarn serve",
    "build": "yarn clean && yarn dll && cross-env NODE_ENV=production webpack --config webpack.config.prod.babel.js",
    "serve": "cross-env NODE_ENV=production nodemon --exec babel-node backend/server.js",
    "profile": "yarn clean && yarn dll && cross-env NODE_ENV=production webpack --config webpack.config.profile.babel.js",
    "eslint": "eslint -f codeframe \"**/*.js\"",
    "tslint": "tslint -t codeFrame -c tslint.json \"**/*.tsx\" \"**/*.d.ts\"",
    "stylelint": "stylelint \"**/*.css **/*.sass **/*.scss\"",
    "lint": "yarn eslint && yarn tslint && yarn stylelint",
    "test": "jest --runInBand --coverage",
    "coverage": "cat ./coverage/lcov.info | ./node_modules/.bin/coveralls"
  },
  "husky": {
    "hooks": {
      "pre-commit": "yarn lint",
      "commit-msg": "commitlint -e $HUSKY_GIT_PARAMS",
      "pre-push": "yarn test && yarn coverage"
    }
  },
  "dependencies": {
    "@babel/cli": "^7.2.3",
    "@babel/core": "^7.2.2",
    "@babel/plugin-proposal-class-properties": "^7.3.0",
    "@babel/plugin-syntax-dynamic-import": "^7.2.0",
    "@babel/plugin-transform-runtime": "^7.2.0",
    "@babel/preset-env": "^7.3.1",
    "@babel/preset-react": "^7.0.0",
    "@babel/preset-typescript": "^7.1.0",
    "@types/i18next": "^12.1.0",
    "@types/i18next-browser-languagedetector": "^2.0.1",
    "@types/i18next-xhr-backend": "^1.4.1",
    "@types/materialize-css": "^1.0.6",
    "@types/prismjs": "^1.9.0",
    "@types/react": "^16.8.2",
    "@types/react-content-loader": "^3.1.4",
    "@types/react-dom": "^16.8.0",
    "@types/react-redux": "^7.0.1",
    "@types/react-router": "^4.4.3",
    "@types/react-router-dom": "^4.3.1",
    "@types/redux-immutable": "^4.0.1",
    "@types/redux-logger": "^3.0.6",
    "@types/uuid": "^3.4.4",
    "@types/webpack-env": "^1.13.7",
    "add-asset-html-webpack-plugin": "^3.1.3",
    "awesome-typescript-loader": "^5.2.1",
    "axios": "^0.18.0",
    "babel-loader": "^8.0.5",
    "babel-plugin-prismjs": "^1.0.2",
    "body-parser": "^1.18.3",
    "colors": "^1.3.3",
    "compression": "^1.7.3",
    "concurrently": "^4.1.0",
    "connect-redis": "^3.4.0",
    "connected-react-router": "^6.2.2",
    "cookie-parser": "^1.4.3",
    "copy-webpack-plugin": "^4.6.0",
    "cross-env": "^5.2.0",
    "css-loader": "^2.1.0",
    "cssnano": "^4.1.8",
    "dotenv-webpack": "^1.7.0",
    "duplicate-package-checker-webpack-plugin": "^3.0.0",
    "express": "^4.16.4",
    "express-session": "^1.15.6",
    "file-loader": "^3.0.1",
    "fork-ts-checker-webpack-plugin": "^0.5.2",
    "helmet": "^3.15.0",
    "history": "^4.7.2",
    "html-webpack-plugin": "^3.2.0",
    "http-status": "^1.3.1",
    "i18next": "^14.1.1",
    "i18next-browser-languagedetector": "^2.2.4",
    "i18next-xhr-backend": "^1.5.1",
    "image-webpack-loader": "^4.6.0",
    "immutable": "4.0.0-rc.12",
    "materialize-css": "1.0.0",
    "mini-css-extract-plugin": "^0.5.0",
    "morgan": "^1.9.1",
    "node-sass": "^4.11.0",
    "nodemon": "^1.18.9",
    "offline-plugin": "^5.0.6",
    "pg": "^7.8.0",
    "postcss": "^7.0.14",
    "postcss-import": "^12.0.1",
    "postcss-loader": "^3.0.0",
    "postcss-preset-env": "^6.5.0",
    "prismjs": "^1.15.0",
    "progress-bar-webpack-plugin": "^1.12.1",
    "react": "^16.8.1",
    "react-content-loader": "^4.0.1",
    "react-dom": "^16.8.1",
    "react-hot-loader": "^4.6.5",
    "react-i18next": "^10.0.1",
    "react-redux": "^6.0.0",
    "react-router": "^4.3.1",
    "react-router-dom": "^4.3.1",
    "redux": "^4.0.1",
    "redux-immutable": "^4.0.0",
    "redux-logger": "^3.0.6",
    "redux-saga": "^1.0.1",
    "sass-loader": "^7.1.0",
    "style-loader": "^0.23.1",
    "trash-cli": "^1.4.0",
    "ts-loader": "^5.3.3",
    "typescript": "^3.3.3",
    "url-loader": "^1.1.2",
    "uuid": "^3.3.2",
    "webpack": "^4.29.3",
    "webpack-bundle-analyzer": "^3.0.3",
    "webpack-cli": "^3.2.3",
    "webpack-merge": "^4.2.1",
    "webpack-pwa-manifest": "^4.0.0"
  },
  "devDependencies": {
    "@babel/node": "^7.2.2",
    "@types/jest": "^24.0.0",
    "@types/react-test-renderer": "^16.8.0",
    "babel-core": "7.0.0-bridge.0",
    "case-sensitive-paths-webpack-plugin": "^2.2.0",
    "commitlint": "^7.5.0",
    "commitlint-config-armour": "^1.2.1",
    "coveralls": "^3.0.2",
    "dotenv": "^6.2.0",
    "eslint": "^5.13.0",
    "eslint-config-airbnb": "^17.1.0",
    "eslint-import-resolver-webpack": "^0.11.0",
    "eslint-plugin-import": "^2.16.0",
    "eslint-plugin-jsx-a11y": "^6.2.1",
    "eslint-plugin-react": "^7.12.4",
    "husky": "1.3.1",
    "identity-obj-proxy": "^3.0.0",
    "jest": "^24.1.0",
    "react-test-renderer": "^16.8.1",
    "stylelint": "^9.10.1",
    "stylelint-config-standard": "^18.2.0",
    "ts-jest": "^23.10.5",
    "tslint": "^5.12.1",
    "tslint-react": "^3.6.0",
    "webpack-dev-server": "^3.1.14"
  },
  "resolutions": {
    "**/event-stream": "^4.0.1"
  },
  "browserslist": [
    "> 1%",
    "last 2 versions"
  ],
  "nodemonConfig": {
    "watch": [
      "backend/controllers/",
      "backend/routes/",
      "backend/db/",
      "backend/config.json",
      "backend/server.js"
    ]
  },
  "jest": {
    "preset": "ts-jest",
    "moduleNameMapper": {
      "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/__mocks__/fileMock.tsx",
      "\\.(css|less|scss|sass)$": "identity-obj-proxy"
    },
    "moduleDirectories": [
      "node_modules",
      "frontend/src",
      "backend"
    ],
    "moduleFileExtensions": [
      "js",
      "jsx",
      "json",
      "ts",
      "tsx"
    ]
  }
}


================================================
FILE: tsconfig.json
================================================
{
  "compileOnSave": false,
  "compilerOptions": {
    "baseUrl": "frontend/src",
    "target": "es6",
    "module": "esnext",
    "moduleResolution": "node",
    "jsx": "react",
    "esModuleInterop": true,
    "noEmitOnError": true,
    "noImplicitReturns": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "sourceMap": true,
    "strict": true
  },
  "exclude": [
    "node_modules",
    "**/__tests__/**"
  ]
}


================================================
FILE: tslint.json
================================================
{
  "extends": [
    "tslint:recommended",
    "tslint-react"
  ],
  "rules": {
    "jsx-boolean-value": false,
    "jsx-no-multiline-js": false,
    "max-line-length": false,
    "no-console": [
      true,
      {
        "allow": [
          "warn",
          "error"
        ]
      }
    ],
    "no-require-imports": false,
    "no-submodule-imports": false,
    "no-var-requires": false,
    "newline-before-return": false,
    "object-literal-sort-keys": false,
    "quotemark": [
      true,
      "single",
      "avoid-escape",
      "avoid-template"
    ],
    "trailing-comma": [
      true,
      {
        "multiline": "always",
        "singleline": "never"
      }
    ],
    "variable-name": [
      true,
      "ban-keywords"
    ]
  },
  "linterOptions": {
    "exclude": [
      "node_modules/**"
    ]
  }
}


================================================
FILE: webpack.config.base.babel.js
================================================
import 'dotenv/config'; // Allow webpack config file to use .env variables

import path from 'path';
import webpack from 'webpack';

import cssnano from 'cssnano';
import postcssImport from 'postcss-import';
import postcssPresetEnv from 'postcss-preset-env';

import AddAssetHtmlPlugin from 'add-asset-html-webpack-plugin';
import CaseSensitivePathsPlugin from 'case-sensitive-paths-webpack-plugin';
import CopyWebpackPlugin from 'copy-webpack-plugin';
import DotenvPlugin from 'dotenv-webpack';
import DuplicatePackageCheckerPlugin from 'duplicate-package-checker-webpack-plugin';
import HtmlWebpackPlugin from 'html-webpack-plugin';
import MiniCssExtractPlugin from 'mini-css-extract-plugin';
import ProgressBarWebpackPlugin from 'progress-bar-webpack-plugin';
import ForkTsCheckerWebpackPlugin from 'fork-ts-checker-webpack-plugin';

const ReactManifest = './frontend/dist/dll/react_manifest.json';
const MaterializeManifest = './frontend/dist/dll/materialize_manifest.json';
const I18nextManifest = './frontend/dist/dll/i18next_manifest.json';
const OtherManifest = './frontend/dist/dll/other_manifest.json';
const devMode = process.env.NODE_ENV !== 'production';

export default {
  // The base directory, an absolute path, for resolving entry points and loaders from configuration
  context: path.resolve(__dirname),

  // Get mode from NODE_ENV
  mode: process.env.NODE_ENV,

  // Determine how the different types of modules within a project will be treated
  module: {
    rules: [
      // Use babel-loader for ts(x) files
      {
        test: /\.tsx?$/,
        exclude: /node_modules/,
        use: [
          {
            loader: 'babel-loader',
            options: {
              cacheDirectory: true,
            },
          },
        ],
      },
      // Use a list of loaders to load materialize and prism css files
      {
        test: /\.css$/,
        use: [
          devMode ? 'style-loader' : MiniCssExtractPlugin.loader,
          {
            loader: 'css-loader',
            options: {
              sourceMap: !devMode,
              modules: true,
              importLoaders: 1,
            },
          },
          {
            loader: 'postcss-loader',
            options: {
              sourceMap: true,
              plugins: () => [postcssImport, postcssPresetEnv, cssnano],
            },
          },
        ],
      },
      // Use a list of loaders to load scss files
      {
        test: /\.scss$/,
        use: [
          devMode ? 'style-loader' : MiniCssExtractPlugin.loader,
          {
            loader: 'css-loader',
            options: {
              sourceMap: !devMode,
              modules: true,
              importLoaders: 2,
            },
          },
          {
            loader: 'postcss-loader',
            options: {
              sourceMap: true,
              plugins: () => [postcssImport, postcssPresetEnv, cssnano],
            },
          },
          { loader: 'sass-loader', options: { sourceMap: true } },
        ],
      },
      // Use image-webpack-loader and url-loader to load images
      {
        test: /\.(png|jpe?g|gif|svg|webp|tiff)(\?.*)?$/,
        use: [
          { loader: 'url-loader', options: { limit: 10000, name: '[name].[ext]' } },
          { loader: 'image-webpack-loader', options: { disable: devMode } },
        ],
      },
      // Use url-loader to load font related files
      {
        test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
        use: [
          { loader: 'url-loader', options: { limit: 10000, name: '[name].[ext]' } },
        ],
      },
      // Use url-loader to load audio related files
      {
        test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
        use: [
          { loader: 'url-loader', options: { limit: 10000, name: '[name].[ext]' } },
        ],
      },
    ],
  },

  // A list of used webpack plugins
  plugins: [
    // Enforces case sensitive paths.
    new CaseSensitivePathsPlugin(),
    // Supports dotenv file
    new DotenvPlugin(),
    // Warns when multiple versions of the same package exist in a build
    new DuplicatePackageCheckerPlugin(),
    // Load pre-build dll reference files
    new webpack.DllReferencePlugin({ manifest: ReactManifest }),
    new webpack.DllReferencePlugin({ manifest: MaterializeManifest }),
    new webpack.DllReferencePlugin({ manifest: I18nextManifest }),
    new webpack.DllReferencePlugin({ manifest: OtherManifest }),
    // Extract css part from javascript bundle into separated file
    new MiniCssExtractPlugin({
      filename: '[name].[contenthash:10].css',
      chunkFilename: '[name].[contenthash:10].css',
    }),
    // Better building progress display
    new ProgressBarWebpackPlugin(),
    // Runs typescript type checker on a separate process
    new ForkTsCheckerWebpackPlugin(),
    // Generate html file to dist folder
    new HtmlWebpackPlugin({
      title: 'Boilerplate',
      template: path.resolve(__dirname, 'frontend/public/index.ejs'),
    }),
    // Add dll reference files to html
    new AddAssetHtmlPlugin({
      filepath: path.resolve(__dirname, 'frontend/dist/dll/*_dll.js'),
      includeSourcemap: false,
    }),
    // Copy static files to build dir
    new CopyWebpackPlugin([
      {
        from: 'frontend/public',
        ignore: ['index.ejs'],
      },
    ]),
  ],

  // Change how modules are resolved
  resolve: {
    // What directories should be searched when resolving modules
    modules: [
      'node_modules',
      'frontend/src',
    ],
    // Automatically resolve certain extensions (Ex. import 'folder/name(.ext)')
    extensions: [
      '.ts',
      '.tsx',
      '.js',
      '.jsx',
      '.json',
      '.css',
      '.scss',
    ],
  },
};


================================================
FILE: webpack.config.dev.babel.js
================================================
import path from 'path';
import merge from 'webpack-merge';

import BaseWebpackConfig from './webpack.config.base.babel';

export default merge(BaseWebpackConfig, {
  // The point or points to enter the application.
  entry: {
    app: [
      './frontend/src/index',
    ],
  },

  // Affecting the output of the compilation
  output: {
    // path: the output directory as an absolute path (required)
    path: path.resolve(__dirname, 'frontend/dist/dev'),
    // filename: specifies the name of entry output file (required)
    filename: '[name].[hash:10].js',
    // chunkFilename: specifies the name of non-entry output files (e.g. dynamic import component)
    chunkFilename: '[name].[hash:10].js',
  },

  devServer: {
    // Port number for webpack dev server
    port: process.env.PORT_WEBPACK_DEV_SERVER,
    // Proxy for api call
    proxy: {
      '/api/v1': {
        target: `http://localhost:${process.env.PORT}/`,
        secure: false,
      },
    },
    // Automatically open page
    open: true,
    // Serves index.html (contains 404 page in react-router) in place of any 404 responses
    historyApiFallback: true,
    // Shows a full-screen overlay when there are compiler errors
    overlay: true,
  },

  // Source map mode
  // https://webpack.js.org/configuration/devtool
  devtool: 'eval-source-map',
});


================================================
FILE: webpack.config.dll.babel.js
================================================
import path from 'path';
import webpack from 'webpack';

import ProgressBarWebpackPlugin from 'progress-bar-webpack-plugin';

const reactVendors = [
  'connected-react-router',
  'connected-react-router/immutable',
  'react',
  'react-dom',
  'react-hot-loader',
  'react-i18next',
  'redux-immutable',
  'react-router-dom',
  'react-redux',
  'redux',
  'redux-logger',
  'redux-saga',
];

const materializeVendors = [
  'materialize-css',
];

const i18nextVendors = [
  'i18next',
  'i18next-xhr-backend',
  'i18next-browser-languagedetector',
];

const otherVendors = [
  'axios',
  'immutable',
  'regenerator-runtime',
];

const config = {
  // Get mode from NODE_ENV
  mode: process.env.NODE_ENV,

  // The base directory, an absolute path, for resolving entry points and loaders from configuration
  context: path.resolve(__dirname),

  // The point or points to enter the application.
  entry: {
    react: reactVendors,
    materialize: materializeVendors,
    i18next: i18nextVendors,
    other: otherVendors,
  },

  // Affecting the output of the compilation
  output: {
    // path: the output directory as an absolute path (required)
    path: path.resolve(__dirname, 'frontend/dist/dll/'),
    // filename: specifies the name of output file on disk (required)
    filename: '[name]_dll.js',
    // library: name of the generated dll reference
    library: '[name]_dll',
  },

  // A list of used webpack plugins
  plugins: [
    // Better building progress display
    new ProgressBarWebpackPlugin(),
    // Output manifest json file for each generated dll reference file
    new webpack.DllPlugin({
      path: path.resolve(__dirname, 'frontend/dist/dll/[name]_manifest.json'),
      name: '[name]_dll',
      format: true,
    }),
  ],

  // Turn off performance hints (assets size limit)
  performance: {
    hints: false,
  },
};

export default config;


================================================
FILE: webpack.config.prod.babel.js
================================================
import path from 'path';
import merge from 'webpack-merge';

import OfflinePlugin from 'offline-plugin';

import BaseWebpackConfig from './webpack.config.base.babel';

export default merge(BaseWebpackConfig, {
  // The point or points to enter the application.
  entry: {
    app: './frontend/src/index',
  },

  // Affecting the output of the compilation
  output: {
    // path: the output directory as an absolute path (required)
    path: path.resolve(__dirname, 'frontend/dist/prod'),
    // filename: specifies the name of entry output file (required)
    filename: '[name].[chunkhash:10].js',
    // chunkFilename: specifies the name of non-entry output files (e.g. dynamic import component)
    chunkFilename: '[name].[chunkhash:10].js',
    // publicPath: specifies the server-relative URL of the output resource directory
    // https://webpack.js.org/configuration/output/#output-publicpath
    publicPath: '/',
  },

  // A list of used webpack plugins
  plugins: [
    // It's always better if OfflinePlugin is the last plugin added
    new OfflinePlugin(),
  ],

  // Source map mode
  // https://webpack.js.org/configuration/devtool
  devtool: 'source-map',
});


================================================
FILE: webpack.config.profile.babel.js
================================================
import merge from 'webpack-merge';

import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer';

import ProdWebpackConfig from './webpack.config.prod.babel';

export default merge(ProdWebpackConfig, {
  plugins: [
    // Webpack bundle analyzer for profiling
    new BundleAnalyzerPlugin({
      analyzerPort: process.env.PORT_BUNDLE_ANALYZER || 3005,
      generateStatsFile: true,
    }),
  ],
});
Download .txt
gitextract_w64eli_i/

├── .appveyor.yml
├── .babelrc
├── .circleci/
│   └── config.yml
├── .commitlintrc.yml
├── .dockerignore
├── .editorconfig
├── .eslintignore
├── .eslintrc
├── .gitattributes
├── .github/
│   ├── CODE_OF_CONDUCT.md
│   ├── COMMIT_CONVENTION.md
│   ├── CONTRIBUTING.md
│   ├── ISSUE_TEMPLATE.md
│   ├── PULL_REQUEST_TEMPLATE.md
│   └── main.workflow
├── .gitignore
├── .stylelintignore
├── .stylelintrc
├── Dockerfile
├── LICENSE
├── README.md
├── __mocks__/
│   ├── fileMock.tsx
│   └── react-i18next.tsx
├── backend/
│   ├── config.json
│   ├── controllers/
│   │   └── notes.js
│   ├── db/
│   │   └── index.js
│   ├── jsconfig.json
│   ├── redis-data/
│   │   └── .gitkeep
│   ├── routes/
│   │   ├── index.js
│   │   └── notes.js
│   ├── server.js
│   └── sql/
│       ├── data.sql
│       └── schema.sql
├── docker-compose.yml
├── frontend/
│   ├── public/
│   │   ├── browserconfig.xml
│   │   ├── index.ejs
│   │   ├── locales/
│   │   │   ├── en/
│   │   │   │   ├── common.json
│   │   │   │   ├── homePage.json
│   │   │   │   ├── notFoundPage.json
│   │   │   │   ├── parallaxPage.json
│   │   │   │   └── reactPage.json
│   │   │   ├── jp/
│   │   │   │   ├── common.json
│   │   │   │   ├── homePage.json
│   │   │   │   ├── notFoundPage.json
│   │   │   │   ├── parallaxPage.json
│   │   │   │   └── reactPage.json
│   │   │   └── zh/
│   │   │       ├── common.json
│   │   │       ├── homePage.json
│   │   │       ├── notFoundPage.json
│   │   │       ├── parallaxPage.json
│   │   │       └── reactPage.json
│   │   ├── manifest.webmanifest
│   │   └── robots.txt
│   └── src/
│       ├── App.tsx
│       ├── components/
│       │   ├── ContentLoader/
│       │   │   ├── __tests__/
│       │   │   │   ├── __snapshots__/
│       │   │   │   │   └── contentLoader.spec.tsx.snap
│       │   │   │   └── contentLoader.spec.tsx
│       │   │   ├── contentLoader.scss
│       │   │   ├── contentLoader.tsx
│       │   │   └── index.tsx
│       │   ├── Dropdown/
│       │   │   ├── __tests__/
│       │   │   │   ├── __snapshots__/
│       │   │   │   │   └── dropdown.spec.tsx.snap
│       │   │   │   └── dropdown.spec.tsx
│       │   │   ├── dropdown.tsx
│       │   │   └── index.tsx
│       │   ├── Footer/
│       │   │   ├── __tests__/
│       │   │   │   ├── __snapshots__/
│       │   │   │   │   └── footer.spec.tsx.snap
│       │   │   │   └── footer.spec.tsx
│       │   │   ├── footer.tsx
│       │   │   └── index.tsx
│       │   └── Header/
│       │       ├── __tests__/
│       │       │   ├── __snapshots__/
│       │       │   │   └── header.spec.tsx.snap
│       │       │   └── header.spec.tsx
│       │       ├── header.scss
│       │       ├── header.tsx
│       │       └── index.tsx
│       ├── i18n/
│       │   └── index.tsx
│       ├── index.tsx
│       ├── pages/
│       │   ├── HomePage/
│       │   │   ├── __tests__/
│       │   │   │   ├── __snapshots__/
│       │   │   │   │   └── homePage.spec.tsx.snap
│       │   │   │   └── homePage.spec.tsx
│       │   │   ├── components/
│       │   │   │   ├── Carousel/
│       │   │   │   │   ├── __tests__/
│       │   │   │   │   │   ├── __snapshots__/
│       │   │   │   │   │   │   └── carousel.spec.tsx.snap
│       │   │   │   │   │   └── carousel.spec.tsx
│       │   │   │   │   ├── carousel.tsx
│       │   │   │   │   ├── constants/
│       │   │   │   │   │   └── carousel.tsx
│       │   │   │   │   └── index.tsx
│       │   │   │   ├── Pushpin/
│       │   │   │   │   ├── __tests__/
│       │   │   │   │   │   ├── __snapshots__/
│       │   │   │   │   │   │   └── pushpin.spec.tsx.snap
│       │   │   │   │   │   └── pushpin.spec.tsx
│       │   │   │   │   ├── index.tsx
│       │   │   │   │   ├── pushpin.scss
│       │   │   │   │   └── pushpin.tsx
│       │   │   │   └── TranslationButton/
│       │   │   │       ├── __tests__/
│       │   │   │       │   ├── __snapshots__/
│       │   │   │       │   │   └── translationButton.spec.tsx.snap
│       │   │   │       │   └── translationButton.spec.tsx
│       │   │   │       ├── index.tsx
│       │   │   │       └── translationButton.tsx
│       │   │   ├── homePage.scss
│       │   │   ├── homePage.tsx
│       │   │   └── index.tsx
│       │   ├── NotFoundPage/
│       │   │   ├── __tests__/
│       │   │   │   ├── __snapshots__/
│       │   │   │   │   └── notFoundPage.spec.tsx.snap
│       │   │   │   └── notFoundPage.spec.tsx
│       │   │   ├── index.tsx
│       │   │   ├── notFoundPage.scss
│       │   │   └── notFoundPage.tsx
│       │   ├── ParallaxPage/
│       │   │   ├── __tests__/
│       │   │   │   ├── __snapshots__/
│       │   │   │   │   └── parallaxPage.spec.tsx.snap
│       │   │   │   └── parallaxPage.spec.tsx
│       │   │   ├── components/
│       │   │   │   └── PrismCodes/
│       │   │   │       ├── __tests__/
│       │   │   │       │   ├── __snapshots__/
│       │   │   │       │   │   └── prismCodes.spec.tsx.snap
│       │   │   │       │   └── prismCodes.spec.tsx
│       │   │   │       ├── constants/
│       │   │   │       │   └── prismCodes.tsx
│       │   │   │       ├── index.tsx
│       │   │   │       ├── prismCodes.scss
│       │   │   │       └── prismCodes.tsx
│       │   │   ├── index.tsx
│       │   │   ├── parallaxPage.scss
│       │   │   └── parallaxPage.tsx
│       │   └── ReactPage/
│       │       ├── __tests__/
│       │       │   ├── __snapshots__/
│       │       │   │   └── reactPage.spec.tsx.snap
│       │       │   └── reactPage.spec.tsx
│       │       ├── components/
│       │       │   ├── FetchNote/
│       │       │   │   ├── __tests__/
│       │       │   │   │   ├── __snapshots__/
│       │       │   │   │   │   └── fetchNote.spec.tsx.snap
│       │       │   │   │   └── fetchNote.spec.tsx
│       │       │   │   ├── fetchNote.scss
│       │       │   │   ├── fetchNote.tsx
│       │       │   │   └── index.tsx
│       │       │   └── TodoLayout/
│       │       │       ├── __tests__/
│       │       │       │   ├── __snapshots__/
│       │       │       │   │   └── todoLayout.spec.tsx.snap
│       │       │       │   └── todoLayout.spec.tsx
│       │       │       ├── components/
│       │       │       │   ├── TodoFooter/
│       │       │       │   │   ├── __tests__/
│       │       │       │   │   │   ├── __snapshots__/
│       │       │       │   │   │   │   └── todoFooter.spec.tsx.snap
│       │       │       │   │   │   └── todoFooter.spec.tsx
│       │       │       │   │   ├── components/
│       │       │       │   │   │   └── TodoFilter/
│       │       │       │   │   │       ├── __tests__/
│       │       │       │   │   │       │   ├── __snapshots__/
│       │       │       │   │   │       │   │   └── todoFilter.spec.tsx.snap
│       │       │       │   │   │       │   └── todoFilter.spec.tsx
│       │       │       │   │   │       ├── index.tsx
│       │       │       │   │   │       ├── todoFilter.scss
│       │       │       │   │   │       └── todoFilter.tsx
│       │       │       │   │   ├── index.tsx
│       │       │       │   │   └── todoFooter.tsx
│       │       │       │   ├── TodoInput/
│       │       │       │   │   ├── __tests__/
│       │       │       │   │   │   ├── __snapshots__/
│       │       │       │   │   │   │   └── todoInput.spec.tsx.snap
│       │       │       │   │   │   └── todoInput.spec.tsx
│       │       │       │   │   ├── index.tsx
│       │       │       │   │   └── todoInput.tsx
│       │       │       │   └── TodoList/
│       │       │       │       ├── __tests__/
│       │       │       │       │   ├── __snapshots__/
│       │       │       │       │   │   └── todoList.spec.tsx.snap
│       │       │       │       │   └── todoList.spec.tsx
│       │       │       │       ├── components/
│       │       │       │       │   └── Todo/
│       │       │       │       │       ├── __tests__/
│       │       │       │       │       │   ├── __snapshots__/
│       │       │       │       │       │   │   └── todo.spec.tsx.snap
│       │       │       │       │       │   └── todo.spec.tsx
│       │       │       │       │       ├── index.tsx
│       │       │       │       │       ├── todo.scss
│       │       │       │       │       └── todo.tsx
│       │       │       │       ├── index.tsx
│       │       │       │       └── todoList.tsx
│       │       │       ├── index.tsx
│       │       │       ├── todoLayout.scss
│       │       │       └── todoLayout.tsx
│       │       ├── index.tsx
│       │       ├── reactPage.scss
│       │       └── reactPage.tsx
│       ├── reducers/
│       │   └── index.tsx
│       ├── router.tsx
│       ├── sagas/
│       │   └── index.tsx
│       ├── sass/
│       │   ├── global.scss
│       │   └── variables.scss
│       ├── services/
│       │   ├── notes/
│       │   │   ├── actions.tsx
│       │   │   ├── apis.tsx
│       │   │   ├── constants.tsx
│       │   │   ├── reducer.tsx
│       │   │   ├── sagas.tsx
│       │   │   └── types.d.ts
│       │   └── todos/
│       │       ├── __test__/
│       │       │   ├── actions.spec.tsx
│       │       │   └── reducer.spec.tsx
│       │       ├── actions.tsx
│       │       ├── constants.tsx
│       │       ├── reducer.tsx
│       │       └── types.d.ts
│       ├── store/
│       │   └── index.tsx
│       ├── types/
│       │   └── global.d.ts
│       └── utils/
│           └── index.tsx
├── package.json
├── tsconfig.json
├── tslint.json
├── webpack.config.base.babel.js
├── webpack.config.dev.babel.js
├── webpack.config.dll.babel.js
├── webpack.config.prod.babel.js
└── webpack.config.profile.babel.js
Download .txt
SYMBOL INDEX (131 symbols across 30 files)

FILE: backend/sql/schema.sql
  type notes (line 1) | Create Table notes (

FILE: frontend/src/App.tsx
  type IAppProps (line 10) | interface IAppProps {

FILE: frontend/src/components/Dropdown/dropdown.tsx
  type IDropdownProps (line 5) | interface IDropdownProps {
  class Dropdown (line 14) | class Dropdown extends React.Component<IDropdownProps> {
    method componentDidMount (line 15) | public componentDidMount() {
    method render (line 20) | public render() {

FILE: frontend/src/components/Footer/footer.tsx
  class Footer (line 4) | class Footer extends React.Component {
    method render (line 5) | public render() {

FILE: frontend/src/components/Header/header.tsx
  type IHeaderStateProps (line 13) | interface IHeaderStateProps {
  class Header (line 17) | class Header extends React.Component<IHeaderStateProps> {
    method constructor (line 18) | constructor(props: IHeaderStateProps) {
    method checkActive (line 22) | public checkActive(urls: string[]) {
    method componentDidMount (line 32) | public componentDidMount() {
    method render (line 37) | public render() {

FILE: frontend/src/pages/HomePage/components/Carousel/carousel.tsx
  class Carousel (line 22) | class Carousel extends React.Component {
    method componentDidMount (line 25) | public componentDidMount() {
    method componentWillUnmount (line 31) | public componentWillUnmount() {
    method render (line 56) | public render() {

FILE: frontend/src/pages/HomePage/components/Carousel/constants/carousel.tsx
  constant TOAST_DISPLAY_DURATION (line 2) | const TOAST_DISPLAY_DURATION = 3000;
  constant TOOLTIP_DELAY_TIME (line 5) | const TOOLTIP_DELAY_TIME = 50;
  constant CAROUSEL_AUTOPLAY_INTERVAL (line 8) | const CAROUSEL_AUTOPLAY_INTERVAL = 3500;

FILE: frontend/src/pages/HomePage/components/Pushpin/pushpin.tsx
  type IPushpinProps (line 6) | interface IPushpinProps {
  class Pushpin (line 11) | class Pushpin extends React.Component<IPushpinProps> {
    method render (line 12) | public render() {

FILE: frontend/src/pages/HomePage/components/TranslationButton/translationButton.tsx
  class TranslationButton (line 9) | class TranslationButton extends React.Component {
    method componentDidMount (line 10) | public componentDidMount() {
    method render (line 20) | public render() {

FILE: frontend/src/pages/HomePage/homePage.tsx
  class HomePage (line 10) | class HomePage extends React.Component {
    method componentDidMount (line 11) | public componentDidMount() {
    method render (line 28) | public render() {

FILE: frontend/src/pages/NotFoundPage/notFoundPage.tsx
  type INotFoundPageState (line 13) | interface INotFoundPageState {
  class NotFoundPage (line 17) | class NotFoundPage extends React.Component<{}, INotFoundPageState> {
    method constructor (line 18) | constructor(props: {}) {
    method render (line 29) | public render() {

FILE: frontend/src/pages/ParallaxPage/components/PrismCodes/constants/prismCodes.tsx
  constant PARALLAX_CODE (line 2) | const PARALLAX_CODE = `

FILE: frontend/src/pages/ParallaxPage/components/PrismCodes/prismCodes.tsx
  type IPrismCodesProps (line 7) | interface IPrismCodesProps {
  class PrismCodes (line 11) | class PrismCodes extends React.Component<IPrismCodesProps> {
    method componentDidMount (line 12) | public componentDidMount() {
    method render (line 16) | public render() {

FILE: frontend/src/pages/ParallaxPage/parallaxPage.tsx
  class ParallaxPage (line 8) | class ParallaxPage extends React.Component {
    method componentDidMount (line 9) | public componentDidMount() {
    method render (line 14) | public render() {

FILE: frontend/src/pages/ReactPage/components/FetchNote/fetchNote.tsx
  type IFetchNoteStateProps (line 15) | interface IFetchNoteStateProps {
  type IFetchNoteDispatchProps (line 21) | interface IFetchNoteDispatchProps {
  type IFetchNoteProps (line 29) | interface IFetchNoteProps extends IFetchNoteStateProps, IFetchNoteDispat...
  class FetchNote (line 31) | class FetchNote extends React.Component<IFetchNoteProps> {
    method render (line 37) | public render() {

FILE: frontend/src/pages/ReactPage/components/TodoLayout/components/TodoFooter/components/TodoFilter/todoFilter.tsx
  type ITodoFilterStateProps (line 12) | interface ITodoFilterStateProps {
  type ITodoFilterDispatchProps (line 16) | interface ITodoFilterDispatchProps {
  type ITodoFilterProps (line 20) | interface ITodoFilterProps extends ITodoFilterStateProps, ITodoFilterDis...
  class TodoFilter (line 22) | class TodoFilter extends React.Component<ITodoFilterProps> {
    method render (line 28) | public render() {
  type ITodoFilterOwnProps (line 47) | interface ITodoFilterOwnProps {

FILE: frontend/src/pages/ReactPage/components/TodoLayout/components/TodoFooter/todoFooter.tsx
  class TodoFooter (line 6) | class TodoFooter extends React.Component {
    method render (line 7) | public render() {

FILE: frontend/src/pages/ReactPage/components/TodoLayout/components/TodoInput/todoInput.tsx
  type ITodoInputDispatchProps (line 10) | interface ITodoInputDispatchProps {
  class TodoInput (line 16) | class TodoInput extends React.Component<ITodoInputDispatchProps> {
    method render (line 30) | public render() {

FILE: frontend/src/pages/ReactPage/components/TodoLayout/components/TodoList/components/Todo/todo.tsx
  type ITodoProps (line 7) | interface ITodoProps extends ITodo {
  class Todo (line 11) | class Todo extends React.Component<ITodoProps> {
    method render (line 17) | public render() {

FILE: frontend/src/pages/ReactPage/components/TodoLayout/components/TodoList/todoList.tsx
  type ITodoListStateProps (line 14) | interface ITodoListStateProps {
  type ITodoListDispatchProps (line 18) | interface ITodoListDispatchProps {
  type ITodoListProps (line 22) | interface ITodoListProps extends ITodoListStateProps, ITodoListDispatchP...
  class TodoList (line 24) | class TodoList extends React.Component<ITodoListProps> {
    method render (line 30) | public render() {

FILE: frontend/src/pages/ReactPage/components/TodoLayout/todoLayout.tsx
  class TodoLayout (line 10) | class TodoLayout extends React.Component {
    method render (line 11) | public render() {

FILE: frontend/src/pages/ReactPage/reactPage.tsx
  class ReactPage (line 7) | class ReactPage extends React.Component {
    method render (line 8) | public render() {

FILE: frontend/src/services/notes/apis.tsx
  class NotesAPI (line 7) | class NotesAPI {
    method fetchAll (line 8) | public static fetchAll() {
    method fetch (line 23) | public static fetch(payload: IActionFetchNoteRequested['payload']) {
    method add (line 38) | public static add(payload: IActionAddNoteRequested['payload']) {
    method edit (line 54) | public static edit(payload: IActionEditNoteRequested['payload']) {
    method remove (line 70) | public static remove(payload: IActionRemoveNoteRequested['payload']) {

FILE: frontend/src/services/notes/constants.tsx
  constant FETCH_ALL_NOTES_REQUESTED (line 3) | const FETCH_ALL_NOTES_REQUESTED = 'FETCH_ALL_NOTES/REQUESTED';
  constant FETCH_ALL_NOTES_SUCCESS (line 4) | const FETCH_ALL_NOTES_SUCCESS = 'FETCH_ALL_NOTES/SUCCESS';
  constant FETCH_ALL_NOTES_FAILURE (line 5) | const FETCH_ALL_NOTES_FAILURE = 'FETCH_ALL_NOTES/FAILURE';
  constant ASYNC_FETCH_ALL_NOTES (line 6) | const ASYNC_FETCH_ALL_NOTES: IAsyncCall = {
  constant FETCH_NOTE_REQUESTED (line 11) | const FETCH_NOTE_REQUESTED = 'FETCH_NOTE/REQUESTED';
  constant FETCH_NOTE_SUCCESS (line 12) | const FETCH_NOTE_SUCCESS = 'FETCH_NOTE/SUCCESS';
  constant FETCH_NOTE_FAILURE (line 13) | const FETCH_NOTE_FAILURE = 'FETCH_NOTE/FAILURE';
  constant ASYNC_FETCH_NOTE (line 14) | const ASYNC_FETCH_NOTE: IAsyncCall = {
  constant EDIT_NOTE_REQUESTED (line 19) | const EDIT_NOTE_REQUESTED = 'EDIT_NOTE/REQUESTED';
  constant EDIT_NOTE_SUCCESS (line 20) | const EDIT_NOTE_SUCCESS = 'EDIT_NOTE/SUCCESS';
  constant EDIT_NOTE_FAILURE (line 21) | const EDIT_NOTE_FAILURE = 'EDIT_NOTE/FAILURE';
  constant ASYNC_EDIT_NOTE (line 22) | const ASYNC_EDIT_NOTE: IAsyncCall = {
  constant ADD_NOTE_REQUESTED (line 27) | const ADD_NOTE_REQUESTED = 'ADD_NOTE/REQUESTED';
  constant ADD_NOTE_SUCCESS (line 28) | const ADD_NOTE_SUCCESS = 'ADD_NOTE/SUCCESS';
  constant ADD_NOTE_FAILURE (line 29) | const ADD_NOTE_FAILURE = 'ADD_NOTE/FAILURE';
  constant ASYNC_ADD_NOTE (line 30) | const ASYNC_ADD_NOTE: IAsyncCall = {
  constant REMOVE_NOTE_REQUESTED (line 35) | const REMOVE_NOTE_REQUESTED = 'REMOVE_NOTE/REQUESTED';
  constant REMOVE_NOTE_SUCCESS (line 36) | const REMOVE_NOTE_SUCCESS = 'REMOVE_NOTE/SUCCESS';
  constant REMOVE_NOTE_FAILURE (line 37) | const REMOVE_NOTE_FAILURE = 'REMOVE_NOTE/FAILURE';
  constant ASYNC_REMOVE_NOTE (line 38) | const ASYNC_REMOVE_NOTE: IAsyncCall = {

FILE: frontend/src/services/notes/reducer.tsx
  class NotesStateRecord (line 7) | class NotesStateRecord extends Record(state) implements INotesStateRecor...

FILE: frontend/src/services/notes/types.d.ts
  type INotesState (line 6) | interface INotesState {
  type INote (line 11) | interface INote {
  type INotesStateRecord (line 15) | interface INotesStateRecord extends Record<INotesState>, INotesState {}
  type IActionFetchAllNotesRequested (line 18) | interface IActionFetchAllNotesRequested {
  type IActionFetchAllNotesSuccess (line 22) | interface IActionFetchAllNotesSuccess {
  type IActionFetchAllNotesFailure (line 28) | interface IActionFetchAllNotesFailure {
  type IActionFetchNoteRequested (line 35) | interface IActionFetchNoteRequested {
  type IActionFetchNoteSuccess (line 41) | interface IActionFetchNoteSuccess {
  type IActionFetchNoteFailure (line 47) | interface IActionFetchNoteFailure {
  type IActionAddNoteRequested (line 54) | interface IActionAddNoteRequested {
  type IActionAddNoteSuccess (line 60) | interface IActionAddNoteSuccess {
  type IActionAddNoteFailure (line 66) | interface IActionAddNoteFailure {
  type IActionEditNoteRequested (line 73) | interface IActionEditNoteRequested {
  type IActionEditNoteSuccess (line 80) | interface IActionEditNoteSuccess {
  type IActionEditNoteFailure (line 86) | interface IActionEditNoteFailure {
  type IActionRemoveNoteRequested (line 93) | interface IActionRemoveNoteRequested {
  type IActionRemoveNoteSuccess (line 99) | interface IActionRemoveNoteSuccess {
  type IActionRemoveNoteFailure (line 105) | interface IActionRemoveNoteFailure {
  type IActionsNotes (line 112) | type IActionsNotes

FILE: frontend/src/services/todos/constants.tsx
  constant ADD_TODO (line 2) | const ADD_TODO = 'ADD_TODO';
  constant TOGGLE_TODO (line 3) | const TOGGLE_TODO = 'TOGGLE_TODO';
  constant SET_VISIBILITY_FILTER (line 6) | const SET_VISIBILITY_FILTER = 'SET_VISIBILITY_FILTER';
  constant VISIBILITY_FILTER_OPTIONS (line 9) | const VISIBILITY_FILTER_OPTIONS = {

FILE: frontend/src/services/todos/reducer.tsx
  class TodosStateRecord (line 7) | class TodosStateRecord extends Record(state) implements ITodosStateRecor...

FILE: frontend/src/services/todos/types.d.ts
  type ITodosState (line 6) | interface ITodosState {
  type ITodo (line 10) | interface ITodo {
  type ITodosStateRecord (line 15) | interface ITodosStateRecord extends Record<ITodosState>, ITodosState {}
  type IActionAddTodo (line 18) | interface IActionAddTodo {
  type IActionToggleTodo (line 24) | interface IActionToggleTodo {
  type IActionSetVisibilityFilter (line 28) | interface IActionSetVisibilityFilter {
  type IActionsTodo (line 33) | type IActionsTodo

FILE: frontend/src/types/global.d.ts
  type IGlobalState (line 8) | interface IGlobalState {
  type IGlobalStateRecord (line 13) | interface IGlobalStateRecord extends Record<IGlobalState>, IGlobalState {}
  type IAsyncCall (line 16) | interface IAsyncCall {
Condensed preview — 174 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (173K chars).
[
  {
    "path": ".appveyor.yml",
    "chars": 205,
    "preview": "environment:\n  nodejs_version: \"11\"\n\ninstall:\n  - ps: Install-Product node $env:nodejs_version\n  - yarn install\n\nbuild_s"
  },
  {
    "path": ".babelrc",
    "chars": 814,
    "preview": "{\n  \"presets\": [\n    \"@babel/preset-env\",\n    \"@babel/preset-react\",\n    \"@babel/preset-typescript\"\n  ],\n  \"plugins\": [\n"
  },
  {
    "path": ".circleci/config.yml",
    "chars": 1227,
    "preview": "# Javascript Node CircleCI 2.0 configuration file\n#\n# Check https://circleci.com/docs/2.0/language-javascript/ for more "
  },
  {
    "path": ".commitlintrc.yml",
    "chars": 20,
    "preview": "extends: ['armour']\n"
  },
  {
    "path": ".dockerignore",
    "chars": 471,
    "preview": "# 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 conf"
  },
  {
    "path": ".editorconfig",
    "chars": 807,
    "preview": "# http://editorconfig.org\n\n# top-most EditorConfig file\nroot = true\n\n# Unix-style newlines with a newline ending every f"
  },
  {
    "path": ".eslintignore",
    "chars": 30,
    "preview": "node_modules/\ndist/\ncoverage/\n"
  },
  {
    "path": ".eslintrc",
    "chars": 702,
    "preview": "{\n  \"env\": {\n    \"browser\": true,\n    \"node\": true,\n    \"es6\": true\n  },\n  \"extends\": [\n    \"airbnb\"\n  ],\n  \"rules\": {\n "
  },
  {
    "path": ".gitattributes",
    "chars": 1769,
    "preview": "# Details per file setting:\n#   text    These files should be normalized (i.e. convert CRLF to LF).\n#   binary  These fi"
  },
  {
    "path": ".github/CODE_OF_CONDUCT.md",
    "chars": 3303,
    "preview": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, w"
  },
  {
    "path": ".github/COMMIT_CONVENTION.md",
    "chars": 4644,
    "preview": "# Git Commit Message Convention\n\n> This is adapted from [Angular's commit convention](https://github.com/conventional-ch"
  },
  {
    "path": ".github/CONTRIBUTING.md",
    "chars": 4590,
    "preview": "# Contributing\n\n## Code of Conduct\n\nHelp us keep express-webpack-react-redux-typescript-boilerplate open and inclusive. "
  },
  {
    "path": ".github/ISSUE_TEMPLATE.md",
    "chars": 1092,
    "preview": "<!--\nPLEASE HELP US PROCESS GITHUB ISSUES FASTER BY PROVIDING THE FOLLOWING INFORMATION.\n\nISSUES MISSING IMPORTANT INFOR"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "chars": 979,
    "preview": "<!--\nPlease make sure to read the Pull Request Guidelines:\nhttps://github.com/Armour/express-webpack-react-redux-typescr"
  },
  {
    "path": ".github/main.workflow",
    "chars": 870,
    "preview": "workflow \"Deploy on Heroku\" {\n  on = \"push\"\n  resolves = [\n    \"verify\",\n  ]\n}\n\n# Login\naction \"login\" {\n  uses = \"actio"
  },
  {
    "path": ".gitignore",
    "chars": 456,
    "preview": "# 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 conf"
  },
  {
    "path": ".stylelintignore",
    "chars": 30,
    "preview": "node_modules/\ndist/\ncoverage/\n"
  },
  {
    "path": ".stylelintrc",
    "chars": 45,
    "preview": "{\n  \"extends\": \"stylelint-config-standard\"\n}\n"
  },
  {
    "path": "Dockerfile",
    "chars": 242,
    "preview": "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 "
  },
  {
    "path": "LICENSE",
    "chars": 1066,
    "preview": "MIT License\n\nCopyright (c) 2017 Chong Guo\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\n"
  },
  {
    "path": "README.md",
    "chars": 7267,
    "preview": "# express-webpack-react-redux-typescript-boilerplate\n\n[![Dependency Status](https://david-dm.org/Armour/express-webpack-"
  },
  {
    "path": "__mocks__/fileMock.tsx",
    "chars": 33,
    "preview": "export default 'test-file-stub';\n"
  },
  {
    "path": "__mocks__/react-i18next.tsx",
    "chars": 92,
    "preview": "export const Translation = ({ children }: any) => children((k: string) => k, { i18n: {} });\n"
  },
  {
    "path": "backend/config.json",
    "chars": 285,
    "preview": "{\n  \"pgsql_hostname_dev\": \"localhost\",\n  \"pgsql_hostname_prod\": \"postgres\",\n  \"pgsql_username\": \"docker\",\n  \"pgsql_passw"
  },
  {
    "path": "backend/controllers/notes.js",
    "chars": 3400,
    "preview": "import status from 'http-status';\n\nimport db from '../db';\n\nexport const getAllNotes = async (req, res) => {\n  try {\n   "
  },
  {
    "path": "backend/db/index.js",
    "chars": 1076,
    "preview": "import { Pool } from 'pg';\n\nimport config from '../config.json';\n\nconst isProduction = process.env.NODE_ENV === 'product"
  },
  {
    "path": "backend/jsconfig.json",
    "chars": 50,
    "preview": "{\n  \"compilerOptions\": {\n    \"baseUrl\": \".\"\n  }\n}\n"
  },
  {
    "path": "backend/redis-data/.gitkeep",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "backend/routes/index.js",
    "chars": 155,
    "preview": "import express from 'express';\n\nimport notesRtr from './notes';\n\nconst router = express.Router();\n\nrouter.use('/notes', "
  },
  {
    "path": "backend/routes/notes.js",
    "chars": 337,
    "preview": "import express from 'express';\n\nimport {\n  getAllNotes, getNote, addNote, editNote, removeNote,\n} from '../controllers/n"
  },
  {
    "path": "backend/server.js",
    "chars": 2393,
    "preview": "import express from 'express';\nimport path from 'path';\nimport logger from 'morgan';\nimport cookieParser from 'cookie-pa"
  },
  {
    "path": "backend/sql/data.sql",
    "chars": 104,
    "preview": "INSERT INTO notes (content) VALUES ('note data 1');\nINSERT INTO notes (content) VALUES ('note data 2');\n"
  },
  {
    "path": "backend/sql/schema.sql",
    "chars": 116,
    "preview": "Create Table notes (\n    id serial primary key,\n    content text not null,\n    created timestamptz default now()\n);\n"
  },
  {
    "path": "docker-compose.yml",
    "chars": 1428,
    "preview": "version: '3.7'\n\nservices:\n  web:\n    image: cguo/express-webpack-react-redux-typescript-boilerplate\n    build: # ignored"
  },
  {
    "path": "frontend/public/browserconfig.xml",
    "chars": 246,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<browserconfig>\n    <msapplication>\n        <tile>\n            <square150x150logo"
  },
  {
    "path": "frontend/public/index.ejs",
    "chars": 2395,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"utf-8\">\n  <meta http-equiv=\"x-ua-compatible\" content=\"ie=edge\">"
  },
  {
    "path": "frontend/public/locales/en/common.json",
    "chars": 432,
    "preview": "{\n  \"header\": {\n    \"react\": \"React\",\n    \"dropdown\": \"Dropdown\",\n    \"notfound\": \"404\"\n  },\n  \"footer\": {\n    \"title\": "
  },
  {
    "path": "frontend/public/locales/en/homePage.json",
    "chars": 811,
    "preview": "{\n  \"title\": \"Home Page\",\n  \"carousel\": {\n    \"focusButtonText\": \"Focus me!\",\n    \"firstPanelTitle\": \"First Panel\",\n    "
  },
  {
    "path": "frontend/public/locales/en/notFoundPage.json",
    "chars": 36,
    "preview": "{\n  \"title\": \"404 Page Not Found\"\n}\n"
  },
  {
    "path": "frontend/public/locales/en/parallaxPage.json",
    "chars": 200,
    "preview": "{\n  \"title\": \"Parallax Page\",\n  \"description\": \"Parallax is an effect where the background content or image in this case"
  },
  {
    "path": "frontend/public/locales/en/reactPage.json",
    "chars": 332,
    "preview": "{\n  \"title\": \"React Page\",\n  \"fetchNote\": {\n    \"asyncCalls\": \"async calls\",\n    \"fetchAllNotes\": \"Fetch All Notes From "
  },
  {
    "path": "frontend/public/locales/jp/common.json",
    "chars": 378,
    "preview": "{\n  \"header\": {\n    \"react\": \"React\",\n    \"dropdown\": \"ドロップダウンリスト\",\n    \"notfound\": \"404\"\n  },\n  \"footer\": {\n    \"title\""
  },
  {
    "path": "frontend/public/locales/jp/homePage.json",
    "chars": 691,
    "preview": "{\n  \"title\": \"ホームページ\",\n  \"carousel\": {\n    \"focusButtonText\": \"フォーカス!\",\n    \"firstPanelTitle\": \"最初のパネル\",\n    \"secondPane"
  },
  {
    "path": "frontend/public/locales/jp/notFoundPage.json",
    "chars": 33,
    "preview": "{\n  \"title\": \"404 ページが見つかりません\"\n}\n"
  },
  {
    "path": "frontend/public/locales/jp/parallaxPage.json",
    "chars": 127,
    "preview": "{\n  \"title\": \"視差ページ\",\n  \"description\": \"Parallax(パララックス)は、この場合のバックグラウンドコンテンツまたは画像がスクロール中にフォアグラウンドコンテンツとは異なる速度で移動される場合の効果"
  },
  {
    "path": "frontend/public/locales/jp/reactPage.json",
    "chars": 303,
    "preview": "{\n  \"title\": \"Reactページ\",\n  \"fetchNote\": {\n    \"asyncCalls\": \"非同期関数\",\n    \"fetchAllNotes\": \"DB内のメモを取得する\"\n  },\n  \"todoLayo"
  },
  {
    "path": "frontend/public/locales/zh/common.json",
    "chars": 350,
    "preview": "{\n  \"header\": {\n    \"react\": \"React\",\n    \"dropdown\": \"下拉列表\",\n    \"notfound\": \"404\"\n  },\n  \"footer\": {\n    \"title\": \"页脚内"
  },
  {
    "path": "frontend/public/locales/zh/homePage.json",
    "chars": 648,
    "preview": "{\n  \"title\": \"首页\",\n  \"carousel\": {\n    \"focusButtonText\": \"看我看我!\",\n    \"firstPanelTitle\": \"第一个面板\",\n    \"secondPanelTitle"
  },
  {
    "path": "frontend/public/locales/zh/notFoundPage.json",
    "chars": 27,
    "preview": "{\n  \"title\": \"404 页面不存在\"\n}\n"
  },
  {
    "path": "frontend/public/locales/zh/parallaxPage.json",
    "chars": 88,
    "preview": "{\n  \"title\": \"视差页面\",\n  \"description\": \"Parallax(视差) 是指一种背景内容或图像在滚动时以与前景内容不同的速度移动的效果。\"\n}\n"
  },
  {
    "path": "frontend/public/locales/zh/reactPage.json",
    "chars": 294,
    "preview": "{\n  \"title\": \"React页面\",\n  \"fetchNote\": {\n    \"asyncCalls\": \"异步函数\",\n    \"fetchAllNotes\": \"获取数据库所有笔记\"\n  },\n  \"todoLayout\":"
  },
  {
    "path": "frontend/public/manifest.webmanifest",
    "chars": 539,
    "preview": "{\n  \"name\": \"Boilerplate\",\n  \"short_name\": \"Boilerplate\",\n  \"start_url\": \".\",\n  \"display\": \"standalone\",\n  \"orientation\""
  },
  {
    "path": "frontend/public/robots.txt",
    "chars": 24,
    "preview": "User-agent: *\nDisallow:\n"
  },
  {
    "path": "frontend/src/App.tsx",
    "chars": 525,
    "preview": "import { ConnectedRouter } from 'connected-react-router/immutable';\nimport { History } from 'history';\nimport React from"
  },
  {
    "path": "frontend/src/components/ContentLoader/__tests__/__snapshots__/contentLoader.spec.tsx.snap",
    "chars": 1044,
    "preview": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`BodyContentLoader should renders correctly 1`] = `\n<div\n  className"
  },
  {
    "path": "frontend/src/components/ContentLoader/__tests__/contentLoader.spec.tsx",
    "chars": 1028,
    "preview": "import React from 'react';\nimport ShallowRenderer from 'react-test-renderer/shallow';\n\nimport { BodyContentLoader, Foote"
  },
  {
    "path": "frontend/src/components/ContentLoader/contentLoader.scss",
    "chars": 39,
    "preview": ".bodyContentLoader {\n  margin: auto;\n}\n"
  },
  {
    "path": "frontend/src/components/ContentLoader/contentLoader.tsx",
    "chars": 1018,
    "preview": "import React from 'react';\nimport ContentLoader from 'react-content-loader';\n\nconst styles = require('./contentLoader.sc"
  },
  {
    "path": "frontend/src/components/ContentLoader/index.tsx",
    "chars": 95,
    "preview": "export { BodyContentLoader, HeaderContentLoader, FooterContentLoader } from './contentLoader';\n"
  },
  {
    "path": "frontend/src/components/Dropdown/__tests__/__snapshots__/dropdown.spec.tsx.snap",
    "chars": 306,
    "preview": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`Dropdown should renders correctly 1`] = `\nArray [\n  <ul\n    classNa"
  },
  {
    "path": "frontend/src/components/Dropdown/__tests__/dropdown.spec.tsx",
    "chars": 546,
    "preview": "import 'materialize-css';\nimport React, { Fragment } from 'react';\nimport { BrowserRouter } from 'react-router-dom';\nimp"
  },
  {
    "path": "frontend/src/components/Dropdown/dropdown.tsx",
    "chars": 858,
    "preview": "import React from 'react';\nimport { Translation } from 'react-i18next';\nimport { Link } from 'react-router-dom';\n\ninterf"
  },
  {
    "path": "frontend/src/components/Dropdown/index.tsx",
    "chars": 38,
    "preview": "export { default } from './dropdown';\n"
  },
  {
    "path": "frontend/src/components/Footer/__tests__/__snapshots__/footer.spec.tsx.snap",
    "chars": 1793,
    "preview": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`Footer should renders correctly 1`] = `\n<footer\n  className=\"page-f"
  },
  {
    "path": "frontend/src/components/Footer/__tests__/footer.spec.tsx",
    "chars": 322,
    "preview": "import React from 'react';\nimport TestRenderer from 'react-test-renderer';\n\nimport { Footer } from '../footer';\n\ndescrib"
  },
  {
    "path": "frontend/src/components/Footer/footer.tsx",
    "chars": 1631,
    "preview": "import React from 'react';\nimport { Translation } from 'react-i18next';\n\nexport class Footer extends React.Component {\n "
  },
  {
    "path": "frontend/src/components/Footer/index.tsx",
    "chars": 36,
    "preview": "export { default } from './footer';\n"
  },
  {
    "path": "frontend/src/components/Header/__tests__/__snapshots__/header.spec.tsx.snap",
    "chars": 2953,
    "preview": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`Header should renders correctly 1`] = `\nArray [\n  <div>\n    <nav>\n "
  },
  {
    "path": "frontend/src/components/Header/__tests__/header.spec.tsx",
    "chars": 575,
    "preview": "import 'materialize-css';\nimport React, { Fragment } from 'react';\nimport { BrowserRouter } from 'react-router-dom';\nimp"
  },
  {
    "path": "frontend/src/components/Header/header.scss",
    "chars": 51,
    "preview": ".logo {\n  margin: 6px 0 0 0;\n  max-height: 50px;\n}\n"
  },
  {
    "path": "frontend/src/components/Header/header.tsx",
    "chars": 3129,
    "preview": "import React from 'react';\nimport { Translation } from 'react-i18next';\nimport { connect } from 'react-redux';\nimport { "
  },
  {
    "path": "frontend/src/components/Header/index.tsx",
    "chars": 36,
    "preview": "export { default } from './header';\n"
  },
  {
    "path": "frontend/src/i18n/index.tsx",
    "chars": 714,
    "preview": "import i18n from 'i18next';\nimport detector from 'i18next-browser-languagedetector';\nimport backend from 'i18next-xhr-ba"
  },
  {
    "path": "frontend/src/index.tsx",
    "chars": 1239,
    "preview": "import { createBrowserHistory } from 'history';\nimport OfflinePluginRuntime from 'offline-plugin/runtime';\nimport React "
  },
  {
    "path": "frontend/src/pages/HomePage/__tests__/__snapshots__/homePage.spec.tsx.snap",
    "chars": 664,
    "preview": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`HomePage should renders correctly 1`] = `\nArray [\n  <div\n    classN"
  },
  {
    "path": "frontend/src/pages/HomePage/__tests__/homePage.spec.tsx",
    "chars": 538,
    "preview": "import 'materialize-css';\nimport React from 'react';\nimport TestRenderer from 'react-test-renderer';\n\nimport { HomePage "
  },
  {
    "path": "frontend/src/pages/HomePage/components/Carousel/__tests__/__snapshots__/carousel.spec.tsx.snap",
    "chars": 1174,
    "preview": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`Carousel should renders correctly 1`] = `\n<div\n  className=\"carouse"
  },
  {
    "path": "frontend/src/pages/HomePage/components/Carousel/__tests__/carousel.spec.tsx",
    "chars": 356,
    "preview": "import 'materialize-css';\nimport React from 'react';\nimport TestRenderer from 'react-test-renderer';\n\nimport { Carousel "
  },
  {
    "path": "frontend/src/pages/HomePage/components/Carousel/carousel.tsx",
    "chars": 3080,
    "preview": "import React from 'react';\nimport { Translation } from 'react-i18next';\n\nimport { CAROUSEL_AUTOPLAY_INTERVAL, TOAST_DISP"
  },
  {
    "path": "frontend/src/pages/HomePage/components/Carousel/constants/carousel.tsx",
    "chars": 194,
    "preview": "// Toast timer\nexport const TOAST_DISPLAY_DURATION = 3000;\n\n// Tooltip timer\nexport const TOOLTIP_DELAY_TIME = 50;\n\n// C"
  },
  {
    "path": "frontend/src/pages/HomePage/components/Carousel/index.tsx",
    "chars": 38,
    "preview": "export { default } from './carousel';\n"
  },
  {
    "path": "frontend/src/pages/HomePage/components/Pushpin/__tests__/__snapshots__/pushpin.spec.tsx.snap",
    "chars": 854,
    "preview": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`Pushpin should renders correctly 1`] = `\n<div\n  className=\"pushpin "
  },
  {
    "path": "frontend/src/pages/HomePage/components/Pushpin/__tests__/pushpin.spec.tsx",
    "chars": 351,
    "preview": "import React from 'react';\nimport TestRenderer from 'react-test-renderer';\n\nimport { Pushpin } from '../pushpin';\n\ndescr"
  },
  {
    "path": "frontend/src/pages/HomePage/components/Pushpin/index.tsx",
    "chars": 37,
    "preview": "export { default } from './pushpin';\n"
  },
  {
    "path": "frontend/src/pages/HomePage/components/Pushpin/pushpin.scss",
    "chars": 30,
    "preview": ".pushpin {\n  height: 825px;\n}\n"
  },
  {
    "path": "frontend/src/pages/HomePage/components/Pushpin/pushpin.tsx",
    "chars": 1168,
    "preview": "import React from 'react';\nimport { Translation } from 'react-i18next';\n\nconst styles = require('./pushpin.scss');\n\ninte"
  },
  {
    "path": "frontend/src/pages/HomePage/components/TranslationButton/__tests__/__snapshots__/translationButton.spec.tsx.snap",
    "chars": 698,
    "preview": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`TranslationButton should renders correctly 1`] = `\n<div\n  className"
  },
  {
    "path": "frontend/src/pages/HomePage/components/TranslationButton/__tests__/translationButton.spec.tsx",
    "chars": 392,
    "preview": "import 'materialize-css';\nimport React from 'react';\nimport TestRenderer from 'react-test-renderer';\n\nimport { Translati"
  },
  {
    "path": "frontend/src/pages/HomePage/components/TranslationButton/index.tsx",
    "chars": 47,
    "preview": "export { default } from './translationButton';\n"
  },
  {
    "path": "frontend/src/pages/HomePage/components/TranslationButton/translationButton.tsx",
    "chars": 1321,
    "preview": "import i18next from 'i18next';\nimport React from 'react';\nimport { Translation } from 'react-i18next';\n\nconst floatingAc"
  },
  {
    "path": "frontend/src/pages/HomePage/homePage.scss",
    "chars": 38,
    "preview": ".home-page-block {\n  height: 820px;\n}\n"
  },
  {
    "path": "frontend/src/pages/HomePage/homePage.tsx",
    "chars": 1807,
    "preview": "import React, { Fragment } from 'react';\nimport { Translation } from 'react-i18next';\n\nimport Carousel from './component"
  },
  {
    "path": "frontend/src/pages/HomePage/index.tsx",
    "chars": 38,
    "preview": "export { default } from './homePage';\n"
  },
  {
    "path": "frontend/src/pages/NotFoundPage/__tests__/__snapshots__/notFoundPage.spec.tsx.snap",
    "chars": 348,
    "preview": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`NotFoundPage should renders correctly 1`] = `\nArray [\n  <h1\n    cla"
  },
  {
    "path": "frontend/src/pages/NotFoundPage/__tests__/notFoundPage.spec.tsx",
    "chars": 346,
    "preview": "import React from 'react';\nimport TestRenderer from 'react-test-renderer';\n\nimport { NotFoundPage } from '../notFoundPag"
  },
  {
    "path": "frontend/src/pages/NotFoundPage/index.tsx",
    "chars": 42,
    "preview": "export { default } from './notFoundPage';\n"
  },
  {
    "path": "frontend/src/pages/NotFoundPage/notFoundPage.scss",
    "chars": 254,
    "preview": "@import 'sass/variables';\n\n.not-found-img {\n  display: block;\n  margin: auto;\n  padding: 60px;\n  max-width: 90%;\n  objec"
  },
  {
    "path": "frontend/src/pages/NotFoundPage/notFoundPage.tsx",
    "chars": 1154,
    "preview": "import React, { Fragment } from 'react';\nimport { Translation } from 'react-i18next';\n\nconst styles = require('./notFoun"
  },
  {
    "path": "frontend/src/pages/ParallaxPage/__tests__/__snapshots__/parallaxPage.spec.tsx.snap",
    "chars": 1274,
    "preview": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`ParallaxPage should renders correctly 1`] = `\nArray [\n  <div\n    cl"
  },
  {
    "path": "frontend/src/pages/ParallaxPage/__tests__/parallaxPage.spec.tsx",
    "chars": 432,
    "preview": "import 'materialize-css';\nimport React from 'react';\nimport TestRenderer from 'react-test-renderer';\n\nimport { ParallaxP"
  },
  {
    "path": "frontend/src/pages/ParallaxPage/components/PrismCodes/__tests__/__snapshots__/prismCodes.spec.tsx.snap",
    "chars": 1264,
    "preview": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`PrismCodes component 1`] = `\n<pre\n  className=\"language-tsx\"\n>\n  <c"
  },
  {
    "path": "frontend/src/pages/ParallaxPage/components/PrismCodes/__tests__/prismCodes.spec.tsx",
    "chars": 498,
    "preview": "import React from 'react';\nimport renderer from 'react-test-renderer';\n\nimport { PARALLAX_CODE } from '../constants/pris"
  },
  {
    "path": "frontend/src/pages/ParallaxPage/components/PrismCodes/constants/prismCodes.tsx",
    "chars": 944,
    "preview": "// Code snippets\nexport const PARALLAX_CODE = `\n<div>\n  <div className='white'>\n    <h1 className='page-title'>{t('title"
  },
  {
    "path": "frontend/src/pages/ParallaxPage/components/PrismCodes/index.tsx",
    "chars": 80,
    "preview": "export { default } from './prismCodes';\nexport * from './constants/prismCodes';\n"
  },
  {
    "path": "frontend/src/pages/ParallaxPage/components/PrismCodes/prismCodes.scss",
    "chars": 492,
    "preview": "code,\npre {\n  position: relative;\n  font-size: 1em;\n}\n\npre[class*=\"language-\"] {\n  padding: 20px 22px;\n  border: solid 1"
  },
  {
    "path": "frontend/src/pages/ParallaxPage/components/PrismCodes/prismCodes.tsx",
    "chars": 527,
    "preview": "import React from 'react';\n\nimport { highlightAll } from 'prismjs';\nimport 'prismjs/themes/prism.css';\nimport './prismCo"
  },
  {
    "path": "frontend/src/pages/ParallaxPage/index.tsx",
    "chars": 42,
    "preview": "export { default } from './parallaxPage';\n"
  },
  {
    "path": "frontend/src/pages/ParallaxPage/parallaxPage.scss",
    "chars": 93,
    "preview": "@import 'sass/variables';\n\n.parallax-header {\n  color: $primary-color;\n  font-weight: 300;\n}\n"
  },
  {
    "path": "frontend/src/pages/ParallaxPage/parallaxPage.tsx",
    "chars": 1716,
    "preview": "import React, { Fragment } from 'react';\nimport { Translation } from 'react-i18next';\n\nimport PrismCodes, { PARALLAX_COD"
  },
  {
    "path": "frontend/src/pages/ReactPage/__tests__/__snapshots__/reactPage.spec.tsx.snap",
    "chars": 266,
    "preview": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`ReactPage should renders correctly 1`] = `\n<div>\n  <div\n    classNa"
  },
  {
    "path": "frontend/src/pages/ReactPage/__tests__/reactPage.spec.tsx",
    "chars": 451,
    "preview": "import React from 'react';\nimport TestRenderer from 'react-test-renderer';\n\nimport { ReactPage } from '../reactPage';\n\nj"
  },
  {
    "path": "frontend/src/pages/ReactPage/components/FetchNote/__tests__/__snapshots__/fetchNote.spec.tsx.snap",
    "chars": 454,
    "preview": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`FetchNote should renders correctly 1`] = `\n<div\n  className=\"center"
  },
  {
    "path": "frontend/src/pages/ReactPage/components/FetchNote/__tests__/fetchNote.spec.tsx",
    "chars": 696,
    "preview": "import { List } from 'immutable';\nimport 'materialize-css';\nimport React from 'react';\nimport TestRenderer from 'react-t"
  },
  {
    "path": "frontend/src/pages/ReactPage/components/FetchNote/fetchNote.scss",
    "chars": 421,
    "preview": "@import 'sass/variables';\n@import '../../reactPage';\n\n.fetch-note-layout {\n  @extend .react-block;\n}\n\n.fetch-note-title "
  },
  {
    "path": "frontend/src/pages/ReactPage/components/FetchNote/fetchNote.tsx",
    "chars": 3195,
    "preview": "import { List } from 'immutable';\nimport React from 'react';\nimport { Translation } from 'react-i18next';\nimport { conne"
  },
  {
    "path": "frontend/src/pages/ReactPage/components/FetchNote/index.tsx",
    "chars": 39,
    "preview": "export { default } from './fetchNote';\n"
  },
  {
    "path": "frontend/src/pages/ReactPage/components/TodoLayout/__tests__/__snapshots__/todoLayout.spec.tsx.snap",
    "chars": 270,
    "preview": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`TodoLayout should renders correctly 1`] = `\n<div\n  className=\"cente"
  },
  {
    "path": "frontend/src/pages/ReactPage/components/TodoLayout/__tests__/todoLayout.spec.tsx",
    "chars": 510,
    "preview": "import React from 'react';\nimport TestRenderer from 'react-test-renderer';\n\nimport { TodoLayout } from '../todoLayout';\n"
  },
  {
    "path": "frontend/src/pages/ReactPage/components/TodoLayout/components/TodoFooter/__tests__/__snapshots__/todoFooter.spec.tsx.snap",
    "chars": 416,
    "preview": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`TodoFooter should renders correctly 1`] = `\n<div\n  className=\"todo-"
  },
  {
    "path": "frontend/src/pages/ReactPage/components/TodoLayout/components/TodoFooter/__tests__/todoFooter.spec.tsx",
    "chars": 398,
    "preview": "import React from 'react';\nimport TestRenderer from 'react-test-renderer';\n\nimport { TodoFooter } from '../todoFooter';\n"
  },
  {
    "path": "frontend/src/pages/ReactPage/components/TodoLayout/components/TodoFooter/components/TodoFilter/__tests__/__snapshots__/todoFilter.spec.tsx.snap",
    "chars": 210,
    "preview": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`TodoFilter should renders correctly 1`] = `\n<a\n  className=\"btn wav"
  },
  {
    "path": "frontend/src/pages/ReactPage/components/TodoLayout/components/TodoFooter/components/TodoFilter/__tests__/todoFilter.spec.tsx",
    "chars": 480,
    "preview": "import React from 'react';\nimport TestRenderer from 'react-test-renderer';\n\nimport { TodoFilter } from '../todoFilter';\n"
  },
  {
    "path": "frontend/src/pages/ReactPage/components/TodoLayout/components/TodoFooter/components/TodoFilter/index.tsx",
    "chars": 40,
    "preview": "export { default } from './todoFilter';\n"
  },
  {
    "path": "frontend/src/pages/ReactPage/components/TodoLayout/components/TodoFooter/components/TodoFilter/todoFilter.scss",
    "chars": 271,
    "preview": "@import 'sass/variables';\n\n.todo-filter-btn {\n  width: 120px;\n  text-align: center;\n  margin: 3px;\n  padding: 0;\n}\n\n@med"
  },
  {
    "path": "frontend/src/pages/ReactPage/components/TodoLayout/components/TodoFooter/components/TodoFilter/todoFilter.tsx",
    "chars": 1696,
    "preview": "import React from 'react';\nimport { connect } from 'react-redux';\nimport { AnyAction, Dispatch } from 'redux';\n\nimport {"
  },
  {
    "path": "frontend/src/pages/ReactPage/components/TodoLayout/components/TodoFooter/index.tsx",
    "chars": 40,
    "preview": "export { default } from './todoFooter';\n"
  },
  {
    "path": "frontend/src/pages/ReactPage/components/TodoLayout/components/TodoFooter/todoFooter.tsx",
    "chars": 756,
    "preview": "import React from 'react';\nimport { Translation } from 'react-i18next';\n\nimport TodoFilter from './components/TodoFilter"
  },
  {
    "path": "frontend/src/pages/ReactPage/components/TodoLayout/components/TodoInput/__tests__/__snapshots__/todoInput.spec.tsx.snap",
    "chars": 308,
    "preview": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`TodoInput should renders correctly 1`] = `\n<form\n  onSubmit={[Funct"
  },
  {
    "path": "frontend/src/pages/ReactPage/components/TodoLayout/components/TodoInput/__tests__/todoInput.spec.tsx",
    "chars": 416,
    "preview": "import React from 'react';\nimport TestRenderer from 'react-test-renderer';\n\nimport { TodoInput } from '../todoInput';\n\n/"
  },
  {
    "path": "frontend/src/pages/ReactPage/components/TodoLayout/components/TodoInput/index.tsx",
    "chars": 39,
    "preview": "export { default } from './todoInput';\n"
  },
  {
    "path": "frontend/src/pages/ReactPage/components/TodoLayout/components/TodoInput/todoInput.tsx",
    "chars": 1408,
    "preview": "import React, { Fragment } from 'react';\nimport { Translation } from 'react-i18next';\nimport { connect } from 'react-red"
  },
  {
    "path": "frontend/src/pages/ReactPage/components/TodoLayout/components/TodoList/__tests__/__snapshots__/todoList.spec.tsx.snap",
    "chars": 101,
    "preview": "// 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",
    "chars": 516,
    "preview": "import { List } from 'immutable';\nimport React from 'react';\nimport TestRenderer from 'react-test-renderer';\n\nimport { T"
  },
  {
    "path": "frontend/src/pages/ReactPage/components/TodoLayout/components/TodoList/components/Todo/__tests__/__snapshots__/todo.spec.tsx.snap",
    "chars": 263,
    "preview": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`Todo should renders correctly 1`] = `\n<a\n  className=\"collection-it"
  },
  {
    "path": "frontend/src/pages/ReactPage/components/TodoLayout/components/TodoList/components/Todo/__tests__/todo.spec.tsx",
    "chars": 487,
    "preview": "import React from 'react';\nimport TestRenderer from 'react-test-renderer';\n\nimport Todo from '../todo';\n\n// Mock dispatc"
  },
  {
    "path": "frontend/src/pages/ReactPage/components/TodoLayout/components/TodoList/components/Todo/index.tsx",
    "chars": 34,
    "preview": "export { default } from './todo';\n"
  },
  {
    "path": "frontend/src/pages/ReactPage/components/TodoLayout/components/TodoList/components/Todo/todo.scss",
    "chars": 116,
    "preview": ".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",
    "chars": 949,
    "preview": "import React from 'react';\n\nimport { ITodo } from 'services/todos/types';\n\nconst styles = require('./todo.scss');\n\ninter"
  },
  {
    "path": "frontend/src/pages/ReactPage/components/TodoLayout/components/TodoList/index.tsx",
    "chars": 38,
    "preview": "export { default } from './todoList';\n"
  },
  {
    "path": "frontend/src/pages/ReactPage/components/TodoLayout/components/TodoList/todoList.tsx",
    "chars": 2056,
    "preview": "import { List } from 'immutable';\nimport React from 'react';\nimport { connect } from 'react-redux';\nimport { AnyAction, "
  },
  {
    "path": "frontend/src/pages/ReactPage/components/TodoLayout/index.tsx",
    "chars": 40,
    "preview": "export { default } from './todoLayout';\n"
  },
  {
    "path": "frontend/src/pages/ReactPage/components/TodoLayout/todoLayout.scss",
    "chars": 142,
    "preview": "@import 'sass/variables';\n@import '../../reactPage';\n\n.todo-layout {\n  @extend .react-block;\n}\n\n.todo-title {\n  @extend "
  },
  {
    "path": "frontend/src/pages/ReactPage/components/TodoLayout/todoLayout.tsx",
    "chars": 717,
    "preview": "import React from 'react';\nimport { Translation } from 'react-i18next';\n\nimport TodoFooter from './components/TodoFooter"
  },
  {
    "path": "frontend/src/pages/ReactPage/index.tsx",
    "chars": 39,
    "preview": "export { default } from './reactPage';\n"
  },
  {
    "path": "frontend/src/pages/ReactPage/reactPage.scss",
    "chars": 713,
    "preview": "@import 'sass/variables';\n\n.react-block {\n  border: 1px lightgray solid;\n  margin: 50px auto;\n  width: 850px;\n  padding:"
  },
  {
    "path": "frontend/src/pages/ReactPage/reactPage.tsx",
    "chars": 593,
    "preview": "import React from 'react';\nimport { Translation } from 'react-i18next';\n\nimport FetchNote from './components/FetchNote';"
  },
  {
    "path": "frontend/src/reducers/index.tsx",
    "chars": 464,
    "preview": "import { connectRouter } from 'connected-react-router/immutable';\nimport { History } from 'history';\nimport { combineRed"
  },
  {
    "path": "frontend/src/router.tsx",
    "chars": 1403,
    "preview": "import React, { Fragment, lazy, Suspense } from 'react';\nimport { Route, Switch } from 'react-router';\n\nimport 'material"
  },
  {
    "path": "frontend/src/sagas/index.tsx",
    "chars": 156,
    "preview": "import { all } from 'redux-saga/effects';\n\nimport notes from 'services/notes/sagas';\n\nexport default function* sagas() {"
  },
  {
    "path": "frontend/src/sass/global.scss",
    "chars": 530,
    "preview": "@import \"variables\";\n\n:global {\n  @import \"~materialize-css/sass/materialize\";\n\n  .container {\n    height: 100%;\n  }\n\n  "
  },
  {
    "path": "frontend/src/sass/variables.scss",
    "chars": 278,
    "preview": "// This file override materialize css variables\n// https://github.com/Dogfalo/materialize/blob/master/sass/components/_v"
  },
  {
    "path": "frontend/src/services/notes/actions.tsx",
    "chars": 1074,
    "preview": "import {ADD_NOTE_REQUESTED, EDIT_NOTE_REQUESTED, FETCH_ALL_NOTES_REQUESTED, FETCH_NOTE_REQUESTED, REMOVE_NOTE_REQUESTED "
  },
  {
    "path": "frontend/src/services/notes/apis.tsx",
    "chars": 2203,
    "preview": "import axios from 'axios';\n\nimport { IActionAddNoteRequested, IActionEditNoteRequested, IActionFetchNoteRequested, IActi"
  },
  {
    "path": "frontend/src/services/notes/constants.tsx",
    "chars": 1664,
    "preview": "import { IAsyncCall } from 'types/global';\n\nexport const FETCH_ALL_NOTES_REQUESTED = 'FETCH_ALL_NOTES/REQUESTED';\nexport"
  },
  {
    "path": "frontend/src/services/notes/reducer.tsx",
    "chars": 1161,
    "preview": "import { List, Record } from 'immutable';\n\nimport { FETCH_ALL_NOTES_FAILURE, FETCH_ALL_NOTES_REQUESTED, FETCH_ALL_NOTES_"
  },
  {
    "path": "frontend/src/services/notes/sagas.tsx",
    "chars": 1119,
    "preview": "import { all, call, put, takeEvery } from 'redux-saga/effects';\n\nimport { IAsyncCall } from 'types/global';\nimport Notes"
  },
  {
    "path": "frontend/src/services/notes/types.d.ts",
    "chars": 2887,
    "preview": "import { List, Record } from 'immutable';\n\nimport { ADD_NOTE_FAILURE, ADD_NOTE_REQUESTED, ADD_NOTE_SUCCESS, EDIT_NOTE_FA"
  },
  {
    "path": "frontend/src/services/todos/__test__/actions.spec.tsx",
    "chars": 1995,
    "preview": "import { addTodo, setVisibilityFilter, toggleTodo } from '../actions';\nimport { ADD_TODO, SET_VISIBILITY_FILTER, TOGGLE_"
  },
  {
    "path": "frontend/src/services/todos/__test__/reducer.spec.tsx",
    "chars": 4237,
    "preview": "import { List } from 'immutable';\n\nimport { ADD_TODO, SET_VISIBILITY_FILTER, TOGGLE_TODO, VISIBILITY_FILTER_OPTIONS } fr"
  },
  {
    "path": "frontend/src/services/todos/actions.tsx",
    "chars": 544,
    "preview": "import { v4 } from 'uuid';\n\nimport { ADD_TODO, SET_VISIBILITY_FILTER, TOGGLE_TODO } from './constants';\nimport { IAction"
  },
  {
    "path": "frontend/src/services/todos/constants.tsx",
    "chars": 355,
    "preview": "// Todo action types\nexport const ADD_TODO = 'ADD_TODO';\nexport const TOGGLE_TODO = 'TOGGLE_TODO';\n\n// Visibility action"
  },
  {
    "path": "frontend/src/services/todos/reducer.tsx",
    "chars": 1376,
    "preview": "import { List, Record } from 'immutable';\n\nimport { ADD_TODO, SET_VISIBILITY_FILTER, TOGGLE_TODO, VISIBILITY_FILTER_OPTI"
  },
  {
    "path": "frontend/src/services/todos/types.d.ts",
    "chars": 791,
    "preview": "import { List, Record } from 'immutable';\n\nimport { ADD_TODO, SET_VISIBILITY_FILTER, TOGGLE_TODO } from './constants';\n\n"
  },
  {
    "path": "frontend/src/store/index.tsx",
    "chars": 1303,
    "preview": "import { routerMiddleware } from 'connected-react-router/immutable';\nimport { History } from 'history';\nimport Immutable"
  },
  {
    "path": "frontend/src/types/global.d.ts",
    "chars": 591,
    "preview": "import { RouterState } from 'connected-react-router/immutable';\nimport { Record } from 'immutable';\n\nimport { INotesStat"
  },
  {
    "path": "frontend/src/utils/index.tsx",
    "chars": 67,
    "preview": "export const isProduction = process.env.NODE_ENV === 'production';\n"
  },
  {
    "path": "package.json",
    "chars": 6378,
    "preview": "{\n  \"name\": \"express-webpack-react-redux-typescript-boilerplate\",\n  \"version\": \"1.0.0\",\n  \"repository\": {\n    \"type\": \"g"
  },
  {
    "path": "tsconfig.json",
    "chars": 436,
    "preview": "{\n  \"compileOnSave\": false,\n  \"compilerOptions\": {\n    \"baseUrl\": \"frontend/src\",\n    \"target\": \"es6\",\n    \"module\": \"es"
  },
  {
    "path": "tslint.json",
    "chars": 829,
    "preview": "{\n  \"extends\": [\n    \"tslint:recommended\",\n    \"tslint-react\"\n  ],\n  \"rules\": {\n    \"jsx-boolean-value\": false,\n    \"jsx"
  },
  {
    "path": "webpack.config.base.babel.js",
    "chars": 5709,
    "preview": "import 'dotenv/config'; // Allow webpack config file to use .env variables\n\nimport path from 'path';\nimport webpack from"
  },
  {
    "path": "webpack.config.dev.babel.js",
    "chars": 1333,
    "preview": "import path from 'path';\nimport merge from 'webpack-merge';\n\nimport BaseWebpackConfig from './webpack.config.base.babel'"
  },
  {
    "path": "webpack.config.dll.babel.js",
    "chars": 1873,
    "preview": "import path from 'path';\nimport webpack from 'webpack';\n\nimport ProgressBarWebpackPlugin from 'progress-bar-webpack-plug"
  },
  {
    "path": "webpack.config.prod.babel.js",
    "chars": 1177,
    "preview": "import path from 'path';\nimport merge from 'webpack-merge';\n\nimport OfflinePlugin from 'offline-plugin';\n\nimport BaseWeb"
  },
  {
    "path": "webpack.config.profile.babel.js",
    "chars": 404,
    "preview": "import merge from 'webpack-merge';\n\nimport { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer';\n\nimport ProdWebpackC"
  }
]

About this extraction

This page contains the full source code of the Armour/express-webpack-react-redux-typescript-boilerplate GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 174 files (146.8 KB), approximately 45.3k tokens, and a symbol index with 131 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!