Kaynağa Gözat

add react-java-mysql application sample

Signed-off-by: Anca Iordache <anca.iordache@docker.com>
Anca Iordache 6 yıl önce
ebeveyn
işleme
3a23aa59d2
44 değiştirilmiş dosya ile 1282 ekleme ve 0 silme
  1. 11 0
      samples/react-java-mysql/backend/Dockerfile
  2. 71 0
      samples/react-java-mysql/backend/pom.xml
  3. 16 0
      samples/react-java-mysql/backend/src/main/java/com/company/project/Application.java
  4. 16 0
      samples/react-java-mysql/backend/src/main/java/com/company/project/controllers/HomeController.java
  5. 1 0
      samples/react-java-mysql/backend/src/main/resources/application.properties
  6. 9 0
      samples/react-java-mysql/backend/src/main/resources/templates/home.ftl
  7. 1 0
      samples/react-java-mysql/db/password.txt
  8. 26 0
      samples/react-java-mysql/docker-compose.yaml
  9. 23 0
      samples/react-java-mysql/frontend/.babelrc
  10. 5 0
      samples/react-java-mysql/frontend/.browserslistrc
  11. 2 0
      samples/react-java-mysql/frontend/.dockerignore
  12. 1 0
      samples/react-java-mysql/frontend/.eslintignore
  13. 36 0
      samples/react-java-mysql/frontend/.eslintrc
  14. 4 0
      samples/react-java-mysql/frontend/.gitignore
  15. 9 0
      samples/react-java-mysql/frontend/Dockerfile
  16. 12 0
      samples/react-java-mysql/frontend/Dockerfile.production
  17. 1 0
      samples/react-java-mysql/frontend/README.md
  18. 9 0
      samples/react-java-mysql/frontend/config/nginx.conf
  19. 15 0
      samples/react-java-mysql/frontend/jest.config.js
  20. 72 0
      samples/react-java-mysql/frontend/package.json
  21. 22 0
      samples/react-java-mysql/frontend/src/app/app.jsx
  22. 15 0
      samples/react-java-mysql/frontend/src/app/components/layout/ApplicationContainer.jsx
  23. 5 0
      samples/react-java-mysql/frontend/src/app/components/layout/index.js
  24. 396 0
      samples/react-java-mysql/frontend/src/app/core/styles/normalize.scss
  25. 48 0
      samples/react-java-mysql/frontend/src/app/core/styles/reset.css
  26. 39 0
      samples/react-java-mysql/frontend/src/app/entry.jsx
  27. 26 0
      samples/react-java-mysql/frontend/src/app/pages/home/HomePage.jsx
  28. 6 0
      samples/react-java-mysql/frontend/src/app/pages/home/home.scss
  29. 7 0
      samples/react-java-mysql/frontend/src/app/pages/home/index.js
  30. 10 0
      samples/react-java-mysql/frontend/src/app/reducers.js
  31. 4 0
      samples/react-java-mysql/frontend/src/app/redux/whales/action-creators.js
  32. 4 0
      samples/react-java-mysql/frontend/src/app/redux/whales/action-types.js
  33. 3 0
      samples/react-java-mysql/frontend/src/app/redux/whales/index.js
  34. 16 0
      samples/react-java-mysql/frontend/src/app/redux/whales/reducer.js
  35. 0 0
      samples/react-java-mysql/frontend/src/app/redux/whales/selectors.js
  36. 10 0
      samples/react-java-mysql/frontend/src/app/routes.jsx
  37. 9 0
      samples/react-java-mysql/frontend/src/app/sagas.js
  38. 37 0
      samples/react-java-mysql/frontend/src/app/store.js
  39. 10 0
      samples/react-java-mysql/frontend/src/index.ejs
  40. 3 0
      samples/react-java-mysql/frontend/src/test/mocks/imageMock.js
  41. 2 0
      samples/react-java-mysql/frontend/src/test/mocks/styleMock.js
  42. 1 0
      samples/react-java-mysql/frontend/webpack.config.js
  43. 231 0
      samples/react-java-mysql/frontend/webpack/config-builder.js
  44. 38 0
      samples/react-java-mysql/frontend/webpack/dev-server.js

+ 11 - 0
samples/react-java-mysql/backend/Dockerfile

@@ -0,0 +1,11 @@
+FROM maven:3.5-jdk-9 AS build
+COPY pom.xml .
+RUN mvn --batch-mode dependency:resolve
+COPY . .
+RUN mvn --batch-mode package
+RUN cp target/*jar target/app.jar
+
+FROM openjdk:9-jre
+VOLUME /tmp
+COPY --from=build target/app.jar app.jar
+ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]

+ 71 - 0
samples/react-java-mysql/backend/pom.xml

@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <groupId>com.company</groupId>
+    <artifactId>project</artifactId>
+    <version>0.0.1-SNAPSHOT</version>
+    <packaging>jar</packaging>
+
+    <name>New App</name>
+    <description>My new SpringBoot app</description>
+
+    <parent>
+        <groupId>org.springframework.boot</groupId>
+        <artifactId>spring-boot-starter-parent</artifactId>
+        <version>2.0.3.RELEASE</version>
+        <relativePath/>
+    </parent>
+
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+        <java.version>1.8</java.version>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-jersey</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.session</groupId>
+            <artifactId>spring-session-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-devtools</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-freemarker</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>javax.xml.bind</groupId>
+            <artifactId>jaxb-api</artifactId>
+            <version>2.3.0</version>
+        </dependency>
+    </dependencies>
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+</project>

+ 16 - 0
samples/react-java-mysql/backend/src/main/java/com/company/project/Application.java

@@ -0,0 +1,16 @@
+package com.company.project;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.annotation.ComponentScan;
+
+@SpringBootApplication
+@EnableAutoConfiguration
+@ComponentScan(basePackages = {"com.company.project"})
+public class Application {
+
+    public static void main(String[] args) {
+        SpringApplication.run(Application.class, args);
+    }
+}

+ 16 - 0
samples/react-java-mysql/backend/src/main/java/com/company/project/controllers/HomeController.java

@@ -0,0 +1,16 @@
+package com.company.project.controllers;
+
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+
+@Controller
+public class HomeController {
+
+    @GetMapping("/")
+    public String showHome(String name, Model model) {
+        return "home";
+    }
+
+}

+ 1 - 0
samples/react-java-mysql/backend/src/main/resources/application.properties

@@ -0,0 +1 @@
+

+ 9 - 0
samples/react-java-mysql/backend/src/main/resources/templates/home.ftl

@@ -0,0 +1,9 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Getting Started: Serving Web Content</title>
+  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+</head>
+<body>
+	<p>Hello from Docker!</p>
+</body>

+ 1 - 0
samples/react-java-mysql/db/password.txt

@@ -0,0 +1 @@
+db-57xsl

+ 26 - 0
samples/react-java-mysql/docker-compose.yaml

@@ -0,0 +1,26 @@
+version: "3.7"
+services:
+  backend:
+    build: backend
+  db:
+    environment:
+      MYSQL_DATABASE: example
+      MYSQL_ROOT_PASSWORD_FILE: /run/secrets/db-password
+    image: mysql:5.7
+    restart: always
+    secrets:
+    - db-password
+    volumes:
+    - db-data:/var/lib/mysql
+  frontend:
+    build: frontend
+    ports:
+    - 80:9000
+    volumes:
+    - ./frontend:/project
+    - /project/node_modules
+volumes:
+  db-data: {}
+secrets:
+  db-password:
+    file: db/password.txt

+ 23 - 0
samples/react-java-mysql/frontend/.babelrc

@@ -0,0 +1,23 @@
+{
+  "presets": [
+    [
+      "env",
+      {
+        "loose": true,
+        "modules": false
+      }
+    ],
+    "react"
+  ],
+  "plugins": [
+    "react-hot-loader/babel",
+    "transform-runtime",
+    "transform-object-rest-spread",
+    "lodash"
+  ],
+  "env": {
+    "test": {
+      "plugins": ["transform-es2015-modules-commonjs"]
+    }
+  }
+}

+ 5 - 0
samples/react-java-mysql/frontend/.browserslistrc

@@ -0,0 +1,5 @@
+> 1%
+last 3 versions
+Firefox ESR
+Opera 12.1
+IE >= 10

+ 2 - 0
samples/react-java-mysql/frontend/.dockerignore

@@ -0,0 +1,2 @@
+node_modules
+.happypack

+ 1 - 0
samples/react-java-mysql/frontend/.eslintignore

@@ -0,0 +1 @@
+webpack/*

+ 36 - 0
samples/react-java-mysql/frontend/.eslintrc

@@ -0,0 +1,36 @@
+{
+  "extends": [
+    "standard",
+    "standard-react",
+    "plugin:jsx-a11y/recommended"
+  ],
+  "env": {
+    "es6"     : true,
+    "browser" : true,
+    "node"    : true,
+    "jest"    : true
+  },
+  "plugins": [
+    "react",
+    "import"
+  ],
+  "parser": "babel-eslint",
+  "parserOptions": {
+    "sourceType": "module"
+  },
+  "rules" : {
+    "no-eq-null" : 0,
+    "quotes": 0,
+    "eol-last": 0,
+    "semi": [2, "always"],
+    "key-spacing": [1, {"beforeColon": true, "afterColon": true, "mode": "minimum", "align": "colon"}],
+    "padded-blocks": [1, { "switches": "never", "classes" : "always"}],
+    "space-before-function-paren": ["error", "never"],
+    "indent": [1, 4]
+  },
+  "globals" : {
+    "__DEV__"        : false,
+    "__filename"     : false,
+    "__dirname"      : false
+  }
+}

+ 4 - 0
samples/react-java-mysql/frontend/.gitignore

@@ -0,0 +1,4 @@
+node_modules
+.happypack
+build/*
+dist/*

+ 9 - 0
samples/react-java-mysql/frontend/Dockerfile

@@ -0,0 +1,9 @@
+FROM node:10
+
+RUN mkdir /project
+WORKDIR /project
+
+COPY . .
+
+RUN yarn install
+CMD ["yarn", "run", "start"]

+ 12 - 0
samples/react-java-mysql/frontend/Dockerfile.production

@@ -0,0 +1,12 @@
+FROM node:10 as build
+
+RUN mkdir /project
+WORKDIR /project
+COPY . .
+RUN yarn install
+RUN yarn run package
+
+FROM nginx:1.13-alpine
+
+COPY config/nginx.conf /etc/nginx/conf.d/default.conf
+COPY --from=build /project/dist /usr/share/nginx/html

+ 1 - 0
samples/react-java-mysql/frontend/README.md

@@ -0,0 +1 @@
+# Sample App

+ 9 - 0
samples/react-java-mysql/frontend/config/nginx.conf

@@ -0,0 +1,9 @@
+server {
+    listen       9000;
+    server_name  localhost;
+
+    location / {
+        root   /usr/share/nginx/html;
+        index  index.html index.htm;
+    }
+}

+ 15 - 0
samples/react-java-mysql/frontend/jest.config.js

@@ -0,0 +1,15 @@
+module.exports = {
+    "moduleFileExtensions": [
+        "js",
+        "jsx",
+        "json"
+    ],
+    "moduleDirectories": [
+        "node_modules",
+        "src"
+    ],
+    "moduleNameMapper": {
+        "^.+\\.(css|scss)$": "<rootDir>/test/mocks/styleMock.js",
+        "^.+\\.(jpg|jpeg|gif|png|svg|eot|ttf|woff|woff2|)$": "<rootDir>/test/mocks/imageMock.js"
+    }
+};

+ 72 - 0
samples/react-java-mysql/frontend/package.json

@@ -0,0 +1,72 @@
+{
+  "name": "react-app",
+  "version": "0.1.0",
+  "private": true,
+  "scripts": {
+    "clean": "rimraf ./dist/* && rimraf ./build/*",
+    "start": "cross-env NODE_ENV=development node webpack/dev-server.js",
+    "build": "cross-env NODE_ENV=development webpack --config webpack.config.js",
+    "package": "cross-env NODE_ENV=production webpack --config webpack.config.js",
+    "test": "cross-env NODE_ENV=test jest",
+    "lint:js": "eslint ./src"
+  },
+  "dependencies": {
+    "express": "4.16.3",
+    "axios": "0.18.0",
+    "classnames": "2.2.5",
+    "lodash": "4.17.5",
+    "moment": "2.20.0",
+    "history": "4.7.2",
+    "react": "^16.8.6",
+    "react-dom": "^16.8.6",
+    "react-redux": "^5.1.1",
+    "react-router-dom": "^4.3.1",
+    "react-router-redux": "5.0.0-alpha.8",
+    "redux": "^3.7.2",
+    "redux-actions": "^2.6.5",
+    "redux-saga": "0.16.0",
+    "reselect": "3.0.1",
+    "query-string": "6.0.0"
+  },
+  "devDependencies": {
+    "rimraf": "2.6.1",
+    "cross-env": "5.1.4",
+    "webpack": "4.5.0",
+    "webpack-cli": "3.3.1",
+    "webpack-dev-server": "3.1.3",
+    "html-webpack-plugin": "3.2.0",
+    "mini-css-extract-plugin": "0.4.0",
+    "react-hot-loader": "3.1.3",
+    "node-sass": "^4.11.0",
+    "babel-core": "6.26.0",
+    "babel-runtime": "6.26.0",
+    "babel-loader": "7.1.4",
+    "babel-preset-env": "^1.7.0",
+    "babel-eslint": "8.2.2",
+    "babel-preset-react": "6.24.1",
+    "babel-plugin-transform-runtime": "6.23.0",
+    "babel-plugin-transform-object-rest-spread": "6.26.0",
+    "babel-plugin-lodash": "3.3.2",
+    "css-loader": "^2.1.1",
+    "sass-loader": "6.0.7",
+    "style-loader": "0.20.3",
+    "file-loader": "1.1.11",
+    "postcss-loader": "2.1.3",
+    "autoprefixer": "8.2.0",
+    "cssnano": "4.1.10",
+    "identity-obj-proxy": "3.0.0",
+    "jest": "^20.0.4",
+    "jest-cli": "^20.0.4",
+    "babel-jest": "20.0.3",
+    "eslint": "4.19.1",
+    "eslint-config-standard": "^10.2.1",
+    "eslint-config-standard-react": "^5.0.0",
+    "eslint-loader": "^1.9.0",
+    "eslint-plugin-import": "^2.7.0",
+    "eslint-plugin-node": "^5.1.1",
+    "eslint-plugin-promise": "^3.3.0",
+    "eslint-plugin-react": "7.1.0",
+    "eslint-plugin-jsx-a11y": "6.0.2",
+    "eslint-plugin-standard": "3.0.1"
+  }
+}

+ 22 - 0
samples/react-java-mysql/frontend/src/app/app.jsx

@@ -0,0 +1,22 @@
+import * as React from "react"
+import { Switch } from "react-router-dom"
+import { renderRoutes } from "./routes"
+
+import { ApplicationContainer } from "app/components/layout"
+
+require("./core/styles/reset.css");
+require("./core/styles/normalize.scss");
+//require("./core/styles/main.scss");
+
+export class App extends React.Component {
+    render() {
+        return (
+            <ApplicationContainer>
+                <Switch>
+                    {renderRoutes()}
+                </Switch>
+            </ApplicationContainer>
+        )
+    }
+}
+

+ 15 - 0
samples/react-java-mysql/frontend/src/app/components/layout/ApplicationContainer.jsx

@@ -0,0 +1,15 @@
+import * as React from "react";
+
+export default class ApplicationContainer extends React.Component {
+
+    render() {
+        return (
+            <div className="main-container">
+                <div className="main-content">
+                    {this.props.children}
+                </div>
+            </div>
+        );
+    }
+
+}

+ 5 - 0
samples/react-java-mysql/frontend/src/app/components/layout/index.js

@@ -0,0 +1,5 @@
+import ApplicationContainer from "./ApplicationContainer";
+
+export {
+    ApplicationContainer,
+}

+ 396 - 0
samples/react-java-mysql/frontend/src/app/core/styles/normalize.scss

@@ -0,0 +1,396 @@
+/*! normalize.css v2.1.0 | MIT License | git.io/normalize */
+
+// ==========================================================================
+// HTML5 display definitions
+// ==========================================================================
+
+//
+// Correct `block` display not defined in IE 8/9.
+//
+
+article,
+aside,
+details,
+figcaption,
+figure,
+footer,
+header,
+hgroup,
+main,
+nav,
+section,
+summary {
+  display: block;
+}
+
+//
+// Correct `inline-block` display not defined in IE 8/9.
+//
+
+audio,
+canvas,
+video {
+  display: inline-block;
+}
+
+//
+// Prevent modern browsers from displaying `audio` without controls.
+// Remove excess height in iOS 5 devices.
+//
+
+audio:not([controls]) {
+  display: none;
+  height: 0;
+}
+
+//
+// Address styling not present in IE 8/9.
+//
+
+[hidden] {
+  display: none;
+}
+
+// ==========================================================================
+// Base
+// ==========================================================================
+
+//
+// 1. Set default font family to sans-serif.
+// 2. Prevent iOS text size adjust after orientation change, without disabling
+//    user zoom.
+//
+
+html {
+  font-family: sans-serif; // 1
+  -webkit-text-size-adjust: 100%; // 2
+  -ms-text-size-adjust: 100%; // 2
+}
+
+//
+// Remove default margin.
+//
+
+body {
+  margin: 0;
+}
+
+// ==========================================================================
+// Links
+// ==========================================================================
+
+//
+// Address `outline` inconsistency between Chrome and other browsers.
+//
+
+a:focus {
+  outline: thin dotted;
+}
+
+//
+// Improve readability when focused and also mouse hovered in all browsers.
+//
+
+a:active,
+a:hover {
+  outline: 0;
+}
+
+// ==========================================================================
+// Typography
+// ==========================================================================
+
+//
+// Address variable `h1` font-size and margin within `section` and `article`
+// contexts in Firefox 4+, Safari 5, and Chrome.
+//
+
+h1 {
+  font-size: 2em;
+  margin: 0.67em 0;
+}
+
+//
+// Address styling not present in IE 8/9, Safari 5, and Chrome.
+//
+
+abbr[title] {
+  border-bottom: 1px dotted;
+}
+
+//
+// Address style set to `bolder` in Firefox 4+, Safari 5, and Chrome.
+//
+
+b,
+strong {
+  font-weight: bold;
+}
+
+//
+// Address styling not present in Safari 5 and Chrome.
+//
+
+dfn {
+  font-style: italic;
+}
+
+//
+// Address differences between Firefox and other browsers.
+//
+
+hr {
+  -moz-box-sizing: content-box;
+  box-sizing: content-box;
+  height: 0;
+}
+
+//
+// Address styling not present in IE 8/9.
+//
+
+mark {
+  background: #ff0;
+  color: #000;
+}
+
+//
+// Correct font family set oddly in Safari 5 and Chrome.
+//
+
+code,
+kbd,
+pre,
+samp {
+  font-family: monospace, serif;
+  font-size: 1em;
+}
+
+//
+// Improve readability of pre-formatted text in all browsers.
+//
+
+pre {
+  white-space: pre-wrap;
+}
+
+//
+// Set consistent quote types.
+//
+
+q {
+  quotes: "\201C" "\201D" "\2018" "\2019";
+}
+
+//
+// Address inconsistent and variable font size in all browsers.
+//
+
+small {
+  font-size: 80%;
+}
+
+//
+// Prevent `sub` and `sup` affecting `line-height` in all browsers.
+//
+
+sub,
+sup {
+  font-size: 75%;
+  line-height: 0;
+  position: relative;
+  vertical-align: baseline;
+}
+
+sup {
+  top: -0.5em;
+}
+
+sub {
+  bottom: -0.25em;
+}
+
+// ==========================================================================
+// Embedded content
+// ==========================================================================
+
+//
+// Remove border when inside `a` element in IE 8/9.
+//
+
+img {
+  border: 0;
+}
+
+//
+// Correct overflow displayed oddly in IE 9.
+//
+
+svg:not(:root) {
+  overflow: hidden;
+}
+
+// ==========================================================================
+// Figures
+// ==========================================================================
+
+//
+// Address margin not present in IE 8/9 and Safari 5.
+//
+
+figure {
+  margin: 0;
+}
+
+// ==========================================================================
+// Forms
+// ==========================================================================
+
+//
+// Define consistent border, margin, and padding.
+//
+
+fieldset {
+  border: 1px solid #c0c0c0;
+  margin: 0 2px;
+  padding: 0.35em 0.625em 0.75em;
+}
+
+//
+// 1. Correct `color` not being inherited in IE 8/9.
+// 2. Remove padding so people aren't caught out if they zero out fieldsets.
+//
+
+legend {
+  border: 0; // 1
+  padding: 0; // 2
+}
+
+//
+// 1. Correct font family not being inherited in all browsers.
+// 2. Correct font size not being inherited in all browsers.
+// 3. Address margins set differently in Firefox 4+, Safari 5, and Chrome.
+//
+
+button,
+input,
+select,
+textarea {
+  font-family: inherit; // 1
+  font-size: 100%; // 2
+  margin: 0; // 3
+}
+
+//
+// Address Firefox 4+ setting `line-height` on `input` using `!important` in
+// the UA stylesheet.
+//
+
+button,
+input {
+  line-height: normal;
+}
+
+//
+// Address inconsistent `text-transform` inheritance for `button` and `select`.
+// All other form control elements do not inherit `text-transform` values.
+// Correct `button` style inheritance in Chrome, Safari 5+, and IE 8+.
+// Correct `select` style inheritance in Firefox 4+ and Opera.
+//
+
+button,
+select {
+  text-transform: none;
+}
+
+//
+// 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`
+//    and `video` controls.
+// 2. Correct inability to style clickable `input` types in iOS.
+// 3. Improve usability and consistency of cursor style between image-type
+//    `input` and others.
+//
+
+button,
+html input[type="button"], // 1
+input[type="reset"],
+input[type="submit"] {
+  -webkit-appearance: button; // 2
+  cursor: pointer; // 3
+}
+
+//
+// Re-set default cursor for disabled elements.
+//
+
+button[disabled],
+html input[disabled] {
+  cursor: default;
+}
+
+//
+// 1. Address box sizing set to `content-box` in IE 8/9.
+// 2. Remove excess padding in IE 8/9.
+//
+
+input[type="checkbox"],
+input[type="radio"] {
+  box-sizing: border-box; // 1
+  padding: 0; // 2
+}
+
+//
+// 1. Address `appearance` set to `searchfield` in Safari 5 and Chrome.
+// 2. Address `box-sizing` set to `border-box` in Safari 5 and Chrome
+//    (include `-moz` to future-proof).
+//
+
+input[type="search"] {
+  -webkit-appearance: textfield; // 1
+  -moz-box-sizing: content-box;
+  -webkit-box-sizing: content-box; // 2
+  box-sizing: content-box;
+}
+
+//
+// Remove inner padding and search cancel button in Safari 5 and Chrome
+// on OS X.
+//
+
+input[type="search"]::-webkit-search-cancel-button,
+input[type="search"]::-webkit-search-decoration {
+  -webkit-appearance: none;
+}
+
+//
+// Remove inner padding and border in Firefox 4+.
+//
+
+button::-moz-focus-inner,
+input::-moz-focus-inner {
+  border: 0;
+  padding: 0;
+}
+
+//
+// 1. Remove default vertical scrollbar in IE 8/9.
+// 2. Improve readability and alignment in all browsers.
+//
+
+textarea {
+  overflow: auto; // 1
+  vertical-align: top; // 2
+}
+
+// ==========================================================================
+// Tables
+// ==========================================================================
+
+//
+// Remove most spacing between table cells.
+//
+
+table {
+  border-collapse: collapse;
+  border-spacing: 0;
+}

+ 48 - 0
samples/react-java-mysql/frontend/src/app/core/styles/reset.css

@@ -0,0 +1,48 @@
+/* http://meyerweb.com/eric/tools/css/reset/
+   v2.0 | 20110126
+   License: none (public domain)
+*/
+
+html, body, div, span, applet, object, iframe,
+h1, h2, h3, h4, h5, h6, p, blockquote, pre,
+a, abbr, acronym, address, big, cite, code,
+del, dfn, em, img, ins, kbd, q, s, samp,
+small, strike, strong, sub, sup, tt, var,
+b, u, i, center,
+dl, dt, dd, ol, ul, li,
+fieldset, form, label, legend,
+table, caption, tbody, tfoot, thead, tr, th, td,
+article, aside, canvas, details, embed,
+figure, figcaption, footer, header, hgroup,
+menu, nav, output, ruby, section, summary,
+time, mark, audio, video {
+  margin: 0;
+  padding: 0;
+  border: 0;
+  font-size: 100%;
+  font: inherit;
+  vertical-align: baseline;
+}
+/* HTML5 display-role reset for older browsers */
+article, aside, details, figcaption, figure,
+footer, header, hgroup, menu, nav, section {
+  display: block;
+}
+body {
+  line-height: 1;
+}
+ol, ul {
+  list-style: none;
+}
+blockquote, q {
+  quotes: none;
+}
+blockquote:before, blockquote:after,
+q:before, q:after {
+  content: '';
+  content: none;
+}
+table {
+  border-collapse: collapse;
+  border-spacing: 0;
+}

+ 39 - 0
samples/react-java-mysql/frontend/src/app/entry.jsx

@@ -0,0 +1,39 @@
+import * as React from "react";
+import { render, unmountComponentAtNode } from "react-dom"
+import { AppContainer } from "react-hot-loader"
+import { Provider } from 'react-redux';
+import { ConnectedRouter } from "react-router-redux"
+import { createBrowserHistory } from "history"
+
+import { configureStore, sagaMiddleware } from "./store"
+import { runApplicationSagas } from "./sagas"
+
+const history = createBrowserHistory();
+const store = configureStore(history);
+
+const getAppContainer = () => document.getElementById('app-container');
+
+const renderApp = () => {
+    const App = require('./app').App;
+    render(
+        <AppContainer>
+            <Provider store={store}>
+                <ConnectedRouter history={history}>
+                    <App/>
+                </ConnectedRouter>
+            </Provider>
+        </AppContainer>
+        , getAppContainer());
+};
+
+if (__DEV__ && module.hot) {
+    const hotReloadApp = () => renderApp();
+    module.hot.accept('./app', () => {
+        // Preventing the hot reloading error from react-router
+        unmountComponentAtNode(getAppContainer());
+        hotReloadApp();
+    })
+}
+
+// runApplicationSagas(sagaMiddleware);
+renderApp();

+ 26 - 0
samples/react-java-mysql/frontend/src/app/pages/home/HomePage.jsx

@@ -0,0 +1,26 @@
+import * as React from "react";
+import { connect } from "react-redux";
+
+require("./home.scss");
+
+const mapStateToProps = (state, props) => {
+    return {};
+};
+
+const mapDispatchToProps = (dispatch) => {
+    return {};
+};
+
+class HomePage extends React.Component {
+
+    render() {
+        return (
+            <div className="home-page page">
+                <h1>My New React App</h1>
+            </div>
+        )
+    }
+
+}
+
+export default connect(mapStateToProps, mapDispatchToProps)(HomePage)

+ 6 - 0
samples/react-java-mysql/frontend/src/app/pages/home/home.scss

@@ -0,0 +1,6 @@
+
+
+.home-page {
+  background-color: blue;
+  color: red;
+}

+ 7 - 0
samples/react-java-mysql/frontend/src/app/pages/home/index.js

@@ -0,0 +1,7 @@
+import * as React from "react";
+import { Route } from "react-router-dom";
+import HomePage from './HomePage';
+
+const route = <Route path="/" exact key="home" component={HomePage}/>;
+
+export default route;

+ 10 - 0
samples/react-java-mysql/frontend/src/app/reducers.js

@@ -0,0 +1,10 @@
+import { combineReducers } from 'redux';
+import { routerReducer } from 'react-router-redux';
+
+import whalesReducer from 'app/redux/whales';
+
+export const rootReducer = combineReducers({
+    router: routerReducer,
+
+    whales: whalesReducer,
+});

+ 4 - 0
samples/react-java-mysql/frontend/src/app/redux/whales/action-creators.js

@@ -0,0 +1,4 @@
+import { createAction } from 'redux-actions';
+import * as ActionTypes from "./action-types";
+
+export const fetchWhales = createAction(ActionTypes.FETCH_WHALES);

+ 4 - 0
samples/react-java-mysql/frontend/src/app/redux/whales/action-types.js

@@ -0,0 +1,4 @@
+
+const prefix = 'WHALES';
+
+export const FETCH_WHALES = `${prefix}/FETCH`;

+ 3 - 0
samples/react-java-mysql/frontend/src/app/redux/whales/index.js

@@ -0,0 +1,3 @@
+import Reducer from './reducer';
+
+export default Reducer;

+ 16 - 0
samples/react-java-mysql/frontend/src/app/redux/whales/reducer.js

@@ -0,0 +1,16 @@
+import * as ActionTypes from "./action-types";
+import { handleActions } from "redux-actions";
+
+const defaultState = {
+    list: [],
+};
+
+const handleFetchWhales = (state, {payload}) => {
+    return state;
+};
+
+const reducer = handleActions({
+    [ActionTypes.FETCH_WHALES] : handleFetchWhales
+}, defaultState);
+
+export default reducer;

+ 0 - 0
samples/react-java-mysql/frontend/src/app/redux/whales/selectors.js


+ 10 - 0
samples/react-java-mysql/frontend/src/app/routes.jsx

@@ -0,0 +1,10 @@
+import * as React from "react";
+import Home from "./pages/home"
+
+const routes = [
+    Home,
+];
+
+export const renderRoutes = () => {
+    return routes;
+};

+ 9 - 0
samples/react-java-mysql/frontend/src/app/sagas.js

@@ -0,0 +1,9 @@
+//import { templatesSaga } from "./data/templates/sagas";
+//import { appWizardSaga } from "./data/app-wizard/sagas";
+
+const startupSagas = [
+];
+
+export const runApplicationSagas = (sagaMiddleware) => {
+    startupSagas.forEach(sagaMiddleware.run);
+};

+ 37 - 0
samples/react-java-mysql/frontend/src/app/store.js

@@ -0,0 +1,37 @@
+import { routerMiddleware } from 'react-router-redux';
+import { createStore, applyMiddleware, compose } from 'redux';
+import createSagaMiddleware from "redux-saga";
+import { rootReducer } from "./reducers";
+
+export const sagaMiddleware = createSagaMiddleware();
+
+export const configureStore = (history, initialState = {}) => {
+
+    const middlewares = [
+        routerMiddleware(history),
+        sagaMiddleware
+    ];
+
+    const enhancers = [
+        applyMiddleware(...middlewares),
+    ];
+
+    if(__DEV__) {
+        const devToolEnhancer = () => {
+            return typeof window === 'object' && typeof window.devToolsExtension !== 'undefined'
+                ? window.devToolsExtension() : f => f;
+        };
+        enhancers.push(devToolEnhancer())
+    }
+
+    const store = createStore(rootReducer, initialState, compose(...enhancers));
+
+    if(__DEV__ && module.hot) {
+        module.hot.accept('./reducers', () => {
+            const nextReducer = require('./reducers').default;
+            store.replaceReducer(nextReducer);
+        })
+    }
+
+    return store;
+};

+ 10 - 0
samples/react-java-mysql/frontend/src/index.ejs

@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="UTF-8">
+    <title>Sample application</title>
+  </head>
+  <body>
+    <div id="app-container"></div>
+  </body>
+</html>

+ 3 - 0
samples/react-java-mysql/frontend/src/test/mocks/imageMock.js

@@ -0,0 +1,3 @@
+// Return an empty string or other mock path to emulate the url that
+// Webpack provides via the file-loader
+module.exports = 'mocked-image.jpg';

+ 2 - 0
samples/react-java-mysql/frontend/src/test/mocks/styleMock.js

@@ -0,0 +1,2 @@
+// Return a Proxy to emulate css modules (if you are using them)
+module.exports = require('identity-obj-proxy');

+ 1 - 0
samples/react-java-mysql/frontend/webpack.config.js

@@ -0,0 +1 @@
+module.exports = require("./webpack/config-builder")(process.env.NODE_ENV || 'production');

+ 231 - 0
samples/react-java-mysql/frontend/webpack/config-builder.js

@@ -0,0 +1,231 @@
+'use strict';
+
+const path = require('path'),
+    webpack = require('webpack'),
+    HtmlWebpackPlugin = require('html-webpack-plugin'),
+    MiniCssExtractPlugin = require("mini-css-extract-plugin");
+
+const PROJECT_ROOT = path.resolve(__dirname, "..");
+
+module.exports = (env) => {
+    const isDev = env === 'development';
+    const isTest = env === 'test';
+    const isProd = !isDev && !isTest;
+
+    const getAppEntry = () => {
+        const appEntry = path.resolve(PROJECT_ROOT, "src/app/entry.jsx");
+        if(isDev) {
+            return [
+                'react-hot-loader/patch',
+                'webpack-dev-server/client?http://localhost:9000',
+                'webpack/hot/only-dev-server',
+                appEntry
+            ]
+        } else {
+            return [appEntry]
+        }
+    };
+
+    const getPlugins = () => {
+
+        // common plugins
+        let plugins = [
+            // Global variables
+            new webpack.DefinePlugin({
+                'process.env.NODE_ENV': JSON.stringify(env),
+                '__DEV__': isDev,
+                '__PROD__': isProd,
+                '__TEST__': isTest,
+            }),
+            // ignore moment locale files
+            new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
+            // extract styles to css file
+            new MiniCssExtractPlugin({
+                filename: "[name].[contenthash].css",
+                chunkFilename: "[id].css",
+                disable: isDev,
+            }),
+            // makes index.html
+            new HtmlWebpackPlugin({
+                template: path.resolve(PROJECT_ROOT, 'src/index.ejs'),
+            })
+        ];
+
+        // development plugins
+        if(isDev) {
+            plugins.push(
+                // Hot Reload (HMR)
+                new webpack.HotModuleReplacementPlugin(),
+                // Named Modules
+                new webpack.NamedModulesPlugin()
+            );
+        }
+
+        // production plugins
+        if(isProd) {
+            plugins.push(
+                new webpack.optimize.ModuleConcatenationPlugin()
+            );
+        }
+
+        return plugins;
+    };
+
+    return {
+        target: 'web',
+        mode: isProd ? "production" : "development",
+        context: PROJECT_ROOT,
+
+        entry: {
+            app: getAppEntry()
+        },
+
+        output: {
+            path: path.resolve(PROJECT_ROOT, isProd ? 'dist' : 'build'),
+            filename: isProd ? '[name].[chunkhash:8].js' : '[name].js',
+            publicPath: '/',
+            sourceMapFilename: '[file].map',
+            chunkFilename: isProd ? '[name].[chunkhash:8].js' : '[name].js',
+            pathinfo: isDev
+
+        },
+
+        devtool: isProd ? "hidden-sourcemap" : 'eval',
+
+        resolve: {
+            extensions: ['.js', '.jsx', '.ts', '.tsx'],
+            modules: ["node_modules", "src"],
+            alias: {}
+        },
+
+        module: {
+            rules: [
+                // JS / JSX files
+                {
+                    test: /\.(js|jsx)$/,
+                    exclude: /(node_modules)/,
+                    use: [
+                        {
+                            loader: 'babel-loader',
+                            options: {
+                                cacheDirectory: true
+                            }
+                        }
+                    ]
+                },
+                // SASS files
+                {
+                    test: /\.scss$/,
+                    exclude: /(node_modules|bower_components)/,
+                    use: [
+                        isDev ? "style-loader" : MiniCssExtractPlugin.loader,
+                        {
+                            loader: "css-loader",
+                            options: {
+                                importLoaders: 2,
+                                url: true,
+                                import: false,
+                                sourceMap: isDev
+                            }
+                        },
+                        {
+                            loader: "postcss-loader",
+                            options: {
+                                sourceMap: isDev,
+                                plugins: isDev ? [] : [
+                                    require("autoprefixer"),
+                                    require("cssnano")({
+                                        safe: true,
+                                        zindex: false,
+                                        discardComments: {
+                                            removeAll: true
+                                        }
+                                    })
+                                ]
+                            }
+                        },
+                        {
+                            loader: "sass-loader",
+                            options: {
+                                sourceMap: isDev,
+                                includePaths: [".", path.join(process.cwd(), "src/app/core/styles")]
+                            }
+                        }
+                    ]
+                },
+                // Plain CSS files
+                {
+                    test: /\.css$/,
+                    use: [
+                        isDev ? "style-loader" : MiniCssExtractPlugin.loader,
+                        {
+                            loader: "css-loader",
+                            options: {
+                                importLoaders: 1,
+                                url: true,
+                                import: false,
+                                sourceMap: isDev
+                            }
+                        },
+                        {
+                            loader: "postcss-loader",
+                            options: {
+                                sourceMap: isDev,
+                                plugins: isDev ? [] : [
+                                    require("autoprefixer"),
+                                    require("cssnano")({
+                                        safe: true,
+                                        zindex: false,
+                                        discardComments: {
+                                            removeAll: true
+                                        }
+                                    })
+                                ]
+                            }
+                        }
+                    ]
+                },
+                // images loader
+                {
+                    test: /\.(png|jpe?g|gif|svg|ico)(\?.*)?$/,
+                    use: [
+                        {
+                            loader: 'file-loader',
+                            options: {
+                                name: "[name].[ext]",
+                                outputPath: isProd ? "../images/" : "images/"
+                            }
+                        }
+                    ]
+                },
+                // font loader
+                {
+                    test: /\.(woff|woff2|ttf|eot)(\?.*)?$/,
+                    use: [
+                        {
+                            loader: 'file-loader',
+                            options: {
+                                name: "[name].[ext]",
+                                publicPath: isProd ? "" : "/webpack/",
+                                useRelativePath: isProd,
+                                outputPath: isProd ? "../fonts/" : "fonts/"
+                            }
+                        }
+                    ]
+                }
+            ]
+        },
+
+        plugins: getPlugins(),
+
+        node: {
+            __filename: true,
+            __dirname: true,
+            fs: 'empty',
+            vm: 'empty',
+            net: 'empty',
+            tls: 'empty',
+        }
+
+    };
+};

+ 38 - 0
samples/react-java-mysql/frontend/webpack/dev-server.js

@@ -0,0 +1,38 @@
+'use strict';
+
+const webpack = require('webpack'),
+      WebpackDevServer = require('webpack-dev-server'),
+      makeConfig = require("./config-builder");
+
+const startWebpackServer = () => {
+    const config = makeConfig('development');
+
+    const SERVER_PORT = 9000;
+
+    new WebpackDevServer(webpack(config), {
+        publicPath          : config.output.publicPath,
+        hot                 : true,
+        historyApiFallback  : true,
+        contentBase         : "./build/",
+
+        watchOptions: { // no file events on D4W
+            aggregateTimeout: 300,
+            poll: 1000
+        },
+
+        proxy : {
+            "/api/*"       : "http://127.0.0.1:8080"  // proxy to backend
+        },
+
+        before : function(app) {
+            // manually configure app `app.use(...)`
+        }
+    }).listen(SERVER_PORT, '0.0.0.0', function (err, result) {
+        if (err) {
+            console.log(err);
+        }
+        console.log('Webpack dev server listening at localhost:' + SERVER_PORT);
+    });
+};
+
+startWebpackServer();