diff --git a/.babelrc b/.babelrc
index cae5209..0e8ee5e 100755
--- a/.babelrc
+++ b/.babelrc
@@ -1,3 +1,8 @@
{
- "presets": ["@babel/env", "@babel/preset-react"]
+ "presets": [
+ "@babel/preset-typescript",
+ "@babel/env",
+ "@babel/preset-react"
+ ],
+ "plugins": ["@babel/plugin-proposal-class-properties"]
}
diff --git a/package-lock.json b/package-lock.json
index 6743ab2..8d843a9 100755
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
{
"name": "kadi-frontend",
- "version": "0.1.0",
+ "version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -271,6 +271,20 @@
"semver": "^5.5.0"
}
},
+ "@babel/helper-create-class-features-plugin": {
+ "version": "7.9.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.9.6.tgz",
+ "integrity": "sha512-6N9IeuyHvMBRyjNYOMJHrhwtu4WJMrYf8hVbEHD3pbbbmNOk1kmXSQs7bA4dYDUaIx4ZEzdnvo6NwC3WHd/Qow==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-function-name": "^7.9.5",
+ "@babel/helper-member-expression-to-functions": "^7.8.3",
+ "@babel/helper-optimise-call-expression": "^7.8.3",
+ "@babel/helper-plugin-utils": "^7.8.3",
+ "@babel/helper-replace-supers": "^7.9.6",
+ "@babel/helper-split-export-declaration": "^7.8.3"
+ }
+ },
"@babel/helper-create-regexp-features-plugin": {
"version": "7.8.8",
"resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.8.8.tgz",
@@ -547,6 +561,16 @@
"@babel/plugin-syntax-async-generators": "^7.8.0"
}
},
+ "@babel/plugin-proposal-class-properties": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.8.3.tgz",
+ "integrity": "sha512-EqFhbo7IosdgPgZggHaNObkmO1kNUe3slaKu54d5OWvy+p9QIKOzK1GAEpAIsZtWVtPXUHSMcT4smvDrCfY4AA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-create-class-features-plugin": "^7.8.3",
+ "@babel/helper-plugin-utils": "^7.8.3"
+ }
+ },
"@babel/plugin-proposal-dynamic-import": {
"version": "7.8.3",
"resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.8.3.tgz",
@@ -718,6 +742,15 @@
"@babel/helper-plugin-utils": "^7.8.3"
}
},
+ "@babel/plugin-syntax-typescript": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.8.3.tgz",
+ "integrity": "sha512-GO1MQ/SGGGoiEXY0e0bSpHimJvxqB7lktLLIq2pv8xG7WZ8IMEle74jIe1FhprHBWjwjZtXHkycDLZXIWM5Wfg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.8.3"
+ }
+ },
"@babel/plugin-transform-arrow-functions": {
"version": "7.8.3",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.8.3.tgz",
@@ -1066,6 +1099,17 @@
"@babel/helper-plugin-utils": "^7.8.3"
}
},
+ "@babel/plugin-transform-typescript": {
+ "version": "7.9.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.9.6.tgz",
+ "integrity": "sha512-8OvsRdvpt3Iesf2qsAn+YdlwAJD7zJ+vhFZmDCa4b8dTp7MmHtKk5FF2mCsGxjZwuwsy/yIIay/nLmxST1ctVQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-create-class-features-plugin": "^7.9.6",
+ "@babel/helper-plugin-utils": "^7.8.3",
+ "@babel/plugin-syntax-typescript": "^7.8.3"
+ }
+ },
"@babel/plugin-transform-unicode-regex": {
"version": "7.8.3",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.8.3.tgz",
@@ -1190,6 +1234,16 @@
"@babel/plugin-transform-react-jsx-source": "^7.9.0"
}
},
+ "@babel/preset-typescript": {
+ "version": "7.9.0",
+ "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.9.0.tgz",
+ "integrity": "sha512-S4cueFnGrIbvYJgwsVFKdvOmpiL0XGw9MFW9D0vgRys5g36PBhZRL8NX8Gr2akz8XRtzq6HuDXPD/1nniagNUg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.8.3",
+ "@babel/plugin-transform-typescript": "^7.9.0"
+ }
+ },
"@babel/runtime": {
"version": "7.9.2",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.9.2.tgz",
@@ -3053,9 +3107,9 @@
"dev": true
},
"eventemitter3": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.0.tgz",
- "integrity": "sha512-qerSRB0p+UDEssxTtm6EDKcE7W4OaoisfIMl4CngyEhjpYglocpNg6UEqCvemdGhosAsg4sO2dXJOdyBifPGCg==",
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.4.tgz",
+ "integrity": "sha512-rlaVLnVxtxvoyLsQQFBx53YmXHDxRIzzTLbdfxqi4yocpSjAxXwkU0cScM5JgSKMqEhrZpnvQ2D9gjylR0AimQ==",
"dev": true
},
"events": {
@@ -3834,9 +3888,9 @@
}
},
"http-proxy": {
- "version": "1.18.0",
- "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.0.tgz",
- "integrity": "sha512-84I2iJM/n1d4Hdgc6y2+qY5mDaz2PUVjlg9znE9byl+q0uC3DeByqBGReQu5tpLK0TAqTIXScRUV+dg7+bUPpQ==",
+ "version": "1.18.1",
+ "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz",
+ "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==",
"dev": true,
"requires": {
"eventemitter3": "^4.0.0",
diff --git a/package.json b/package.json
index 6d8b65f..8d54fa3 100755
--- a/package.json
+++ b/package.json
@@ -6,33 +6,28 @@
"license": "ISC",
"author": "Daniel Ledda",
"scripts": {
- "build": "webpack --mode development",
+ "build-dev": "webpack --mode development && npm postbuild",
+ "build": "webpack --mode production",
"postbuild": "rsync -avu --delete dist/ ../kadi_backend/static/frontend",
"start": "webpack-dev-server --mode development",
"test": "echo \"Error: no test specified\" && exit 1"
},
- "dependencies": {
- "axios": "^0.19.2",
- "react": "^16.13.1",
- "react-dom": "^16.13.1",
- "react-router-dom": "^5.1.2",
- "semantic-ui-css": "^2.4.1",
- "semantic-ui-react": "^0.88.2"
- },
"devDependencies": {
"@babel/cli": "^7.8.4",
"@babel/core": "^7.9.6",
+ "@babel/plugin-proposal-class-properties": "^7.8.3",
"@babel/preset-env": "^7.9.6",
"@babel/preset-react": "^7.9.4",
+ "@babel/preset-typescript": "^7.9.0",
"@types/node": "^13.11.1",
"@types/react": "^16.9.34",
"@types/react-dom": "^16.9.7",
"@types/react-router-dom": "^5.1.5",
- "ignore-loader": "^0.1.2",
- "font-loader": "^0.1.2",
"babel-loader": "^8.1.0",
"css-loader": "^3.5.3",
"file-loader": "^6.0.0",
+ "font-loader": "^0.1.2",
+ "ignore-loader": "^0.1.2",
"react-hot-loader": "^4.12.21",
"style-loader": "^1.2.1",
"ts-loader": "^7.0.3",
@@ -42,5 +37,13 @@
"webpack": "^4.43.0",
"webpack-cli": "^3.3.11",
"webpack-dev-server": "^3.11.0"
+ },
+ "dependencies": {
+ "axios": "^0.19.2",
+ "react": "^16.13.1",
+ "react-dom": "^16.13.1",
+ "react-router-dom": "^5.1.2",
+ "semantic-ui-css": "^2.4.1",
+ "semantic-ui-react": "^0.88.2"
}
}
diff --git a/public/index.html b/public/index.html
index 3a4647d..53a926c 100755
--- a/public/index.html
+++ b/public/index.html
@@ -2,7 +2,7 @@
-
+
You need to enable JavaScript to run this app.
-
+
\ No newline at end of file
diff --git a/src/App.tsx b/src/App.tsx
index 8ecffcb..eef6711 100755
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -1,8 +1,8 @@
import React, {ReactNode} from "react";
-import {BrowserRouter as Router, Link, Route} from "react-router-dom";
-import {Redirect, Switch} from "react-router";
+import {BrowserRouter as Router, Route} from "react-router-dom";
+import {Redirect} from "react-router";
import {IntlStrings} from "./static/strings";
-import {PageId, SupportedLang} from "./enums";
+import {PageId, SupportedLang, supportedLangToIntlDTF} from "./enums";
import {pageComponentFromId} from "./pageListings";
import KadiPage from "./Components/KadiPage";
import HomePage from "./Components/HomePage";
@@ -27,6 +27,7 @@ class App extends React.Component {
username: username,
loggedIn: loggedIn,
updateUserContext: this.updateUserContext,
+ dateTimeFormatter: this.state.userContext.dateTimeFormatter,
currentLang: this.state.userContext.currentLang,
strings: this.state.userContext.strings,
changeLang: this.state.userContext.changeLang,
@@ -35,6 +36,7 @@ class App extends React.Component {
this.changeLang = (lang: SupportedLang, submit=true) => {
this.setState({userContext: {
+ dateTimeFormatter: supportedLangToIntlDTF[lang],
strings: IntlStrings[lang],
currentLang: lang,
changeLang: this.changeLang,
@@ -52,6 +54,7 @@ class App extends React.Component {
username: "",
loggedIn: false,
updateUserContext: this.updateUserContext,
+ dateTimeFormatter: supportedLangToIntlDTF[SupportedLang.gb],
currentLang: SupportedLang.gb,
strings: IntlStrings[SupportedLang.gb],
changeLang: this.changeLang,
@@ -76,7 +79,7 @@ class App extends React.Component {
}
submitLanguagePreference(lang: SupportedLang) {
- axios.post(SERVER_BASE_NAME + "/api/changeLang",
+ axios.put(SERVER_BASE_NAME + "/api/lang",
{lang: lang},
{headers: {"Content-Type": "application/json"}}
);
diff --git a/src/Components/GamesList.tsx b/src/Components/GamesList.tsx
new file mode 100644
index 0000000..36eeb95
--- /dev/null
+++ b/src/Components/GamesList.tsx
@@ -0,0 +1,31 @@
+import {List, ListItem} from "semantic-ui-react";
+import React from "react";
+import UserContext from "../Contexts/UserContext";
+
+interface GamesListProps {
+ loading: boolean;
+ gamesList: any[];
+}
+
+const GamesList: React.FunctionComponent = (props) => {
+ const {loading, gamesList} = props;
+ const Uctx = React.useContext(UserContext);
+ const listItems = gamesList.map(listing =>
+
+ Game played on: {Uctx.dateTimeFormatter.format(new Date(listing.createdAt))}
+
+ );
+ return (
+ <>
+ {loading ? (
+ {Uctx.strings.historyPage.loading}
+ ) : (
+
+ {listItems}
+
+ )}
+ >
+ );
+};
+
+export default GamesList;
\ No newline at end of file
diff --git a/src/Components/GuestList.tsx b/src/Components/GuestList.tsx
new file mode 100644
index 0000000..ba33bec
--- /dev/null
+++ b/src/Components/GuestList.tsx
@@ -0,0 +1,39 @@
+import {Header, List, ListItem} from "semantic-ui-react";
+import React from "react";
+import UserContext from "../Contexts/UserContext";
+import {Guest} from "./ProfilePage";
+import HeaderSubHeader from "semantic-ui-react/dist/commonjs/elements/Header/HeaderSubheader";
+import {SERVER_BASE_NAME} from "../index";
+
+
+interface GuestListProps {
+ loading: boolean;
+ guestList: Guest[];
+ deleteGuest: (id: string) => any;
+}
+
+const GuestList: React.FunctionComponent = (props) => {
+ const {loading, guestList, deleteGuest} = props;
+ const Uctx = React.useContext(UserContext);
+ const listItems = guestList.map(guest =>
+
+ {guest.nick} - deleteGuest(guest.id)}>{Uctx.strings.general.deleteCommand}
+
+ );
+ return (
+ <>
+
+ {Uctx.strings.profilePage.guestsHeader}
+
+ {loading ? (
+ {Uctx.strings.profilePage.loadingGuests}
+ ) : (
+
+ {listItems}
+
+ )}
+ >
+ );
+};
+
+export default GuestList;
\ No newline at end of file
diff --git a/src/Components/HistoryPage.tsx b/src/Components/HistoryPage.tsx
index 2ddc4ea..2714141 100755
--- a/src/Components/HistoryPage.tsx
+++ b/src/Components/HistoryPage.tsx
@@ -3,6 +3,7 @@ import {Header, List, ListItem} from "semantic-ui-react";
import axios from "axios";
import {SERVER_BASE_NAME} from "../index";
import UserContext from "../Contexts/UserContext";
+import GamesList from "./GamesList";
interface HistoryPageProps {
}
@@ -27,11 +28,9 @@ class HistoryPage extends React.Component {
.then(response => this.setState({gameListings: response.data.games}))
.catch(error => this.handleError(error))
.finally(() => this.setState({ loadingGames: false }));
- console.log(this.state.gameListings);
}
handleError = (error: any) => void {
-
};
render(): ReactElement {
@@ -41,22 +40,10 @@ class HistoryPage extends React.Component {
{Locale.historyPage.title}
- {
- this.state.loadingGames ? (
-
- Loading games...
-
- ) :
- (
-
- {
- this.state.gameListings.map(listing => {
- return Game played on: {listing.createdAt};
- })
- }
-
- )
- }
+
>
);
}
diff --git a/src/Components/ProfilePage.tsx b/src/Components/ProfilePage.tsx
index 79379a0..5842d5a 100755
--- a/src/Components/ProfilePage.tsx
+++ b/src/Components/ProfilePage.tsx
@@ -1,10 +1,20 @@
import React, {ReactElement} from "react";
import {Header} from "semantic-ui-react";
import UserContext from "../Contexts/UserContext";
+import axios from "axios";
+import {SERVER_BASE_NAME} from "../index";
+import GuestList from "./GuestList";
interface ProfilePageProps {}
interface ProfilePageState {
+ loadingGuests: boolean;
+ guests: Guest[];
+}
+
+export interface Guest {
+ id: string;
+ nick: string;
}
class ProfilePage extends React.Component {
@@ -12,15 +22,48 @@ class ProfilePage extends React.Component {
super(props);
this.state = {
+ loadingGuests: false,
+ guests: [],
};
}
+ componentDidMount(): void {
+ this.loadGuests();
+ }
+
+ loadGuests(): void {
+ this.setState({loadingGuests: true}, () => {
+ axios.get(SERVER_BASE_NAME + "/api/guests")
+ .then(response => this.setState({guests: response.data.guests}))
+ .catch(error => this.handleError(error))
+ .finally(() => this.setState({ loadingGuests: false }));
+ });
+ }
+
+ deleteGuest(id: string): void {
+ console.log("delete with url", SERVER_BASE_NAME + "/api/guest/" + id);
+ axios.delete(SERVER_BASE_NAME + "/api/guest/" + id)
+ .then(response => this.loadGuests())
+ .catch(error => this.handleError(error))
+ }
+
+ handleError(error: any): void {
+ console.log(error);
+ }
+
render(): ReactElement {
const Locale = this.context.strings;
return (
-
- {Locale.profilePage.title}
-
+ <>
+
+ {Locale.profilePage.title}
+
+ this.deleteGuest(id)}
+ loading={this.state.loadingGuests}
+ guestList={this.state.guests}
+ />
+ >
);
}
}
diff --git a/src/Contexts/UserContext.ts b/src/Contexts/UserContext.ts
index 7ab3c06..007288f 100755
--- a/src/Contexts/UserContext.ts
+++ b/src/Contexts/UserContext.ts
@@ -1,11 +1,12 @@
import React from "react";
-import {SupportedLang} from "../enums";
+import {supportedLangToIntlDTF, SupportedLang} from "../enums";
import {IntlStrings} from "../static/strings";
export interface IUserContext {
username: string;
loggedIn: boolean;
updateUserContext: (username: string, loggedIn: boolean) => void;
+ dateTimeFormatter: Intl.DateTimeFormat;
currentLang: SupportedLang;
strings: any;
changeLang: (lang: SupportedLang, submit?: boolean) => void;
@@ -15,6 +16,7 @@ const userDefaultVal = {
loggedIn: false,
username: "",
updateUserContext: () => {},
+ dateTimeFormatter: supportedLangToIntlDTF[SupportedLang.gb],
currentLang: SupportedLang.gb,
strings: IntlStrings[SupportedLang.gb as SupportedLang],
changeLang: () => {},
diff --git a/src/enums.ts b/src/enums.ts
index 89939a5..da5b527 100755
--- a/src/enums.ts
+++ b/src/enums.ts
@@ -11,4 +11,10 @@ export enum PageId {
stats = "stats",
home = "home",
history = "history",
-}
\ No newline at end of file
+}
+
+export const supportedLangToIntlDTF: Record = {
+ gb: Intl.DateTimeFormat('en-AU'),
+ de: Intl.DateTimeFormat('de-DE'),
+ it: Intl.DateTimeFormat('it-IT'),
+};
\ No newline at end of file
diff --git a/src/filetypes.d.ts b/src/filetypes.d.ts
old mode 100755
new mode 100644
diff --git a/src/index.tsx b/src/index.tsx
index 91f3fee..f5f0feb 100755
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -3,8 +3,7 @@ import ReactDOM from "react-dom";
import "semantic-ui-css/semantic.min.css";
import App from "./App";
import * as serviceWorker from "./serviceWorker";
-
-export const SERVER_BASE_NAME = "/kadi";
+export {homepage as SERVER_BASE_NAME} from "../package.json";
ReactDOM.render((
diff --git a/src/static/strings.ts b/src/static/strings.ts
index 43ff8bf..0ac0e75 100755
--- a/src/static/strings.ts
+++ b/src/static/strings.ts
@@ -28,6 +28,9 @@ export const LanguageNames: Record = {
export const IntlStrings = {
gb: {
+ general: {
+ deleteCommand: "Delete",
+ },
menu: {
profileTab: "Profile",
statsTab: "Stats",
@@ -44,6 +47,8 @@ export const IntlStrings = {
},
profilePage: {
title: "Profile",
+ guestsHeader: "Guests",
+ loadingGuests: "Loading guests..."
},
statsPage: {
title: "Stats",
@@ -56,9 +61,13 @@ export const IntlStrings = {
},
historyPage: {
title: "History",
+ loading: "Loading games...",
},
},
de: {
+ general: {
+ deleteCommand: "Löschen",
+ },
menu: {
profileTab: "Profil",
statsTab: "Statistiken",
@@ -75,6 +84,8 @@ export const IntlStrings = {
},
profilePage: {
title: "Profil",
+ guestsHeader: "Gäste",
+ loadingGuests: "Gäste werden geladen...",
},
statsPage: {
title: "Statistiken",
@@ -87,9 +98,13 @@ export const IntlStrings = {
},
historyPage: {
title: "Spielverlauf",
+ loading: "Spielverlauf wird geladen...",
},
},
it: {
+ general: {
+ deleteCommand: "Cancella",
+ },
menu: {
profileTab: "Profilo",
statsTab: "Statistiche",
@@ -106,6 +121,8 @@ export const IntlStrings = {
},
profilePage: {
title: "Profilo",
+ guestsHeader: "===TRANSLATE ME===",
+ loadingGuests: "===TRANSLATE ME===",
},
statsPage: {
title: "Statistiche",
@@ -118,6 +135,7 @@ export const IntlStrings = {
},
historyPage: {
title: "Storia",
+ loading: "Caricando storia giochi..."
},
},
} as const;
\ No newline at end of file
diff --git a/tslint.json b/tslint.json
index 32538c5..06739e6 100755
--- a/tslint.json
+++ b/tslint.json
@@ -25,7 +25,7 @@
],
"linterOptions": {
"exclude": [
- "build/**/*.js",
+ "dist/**/*.js",
"config/**/*.js",
"node_modules/**/*.ts"
]
diff --git a/webpack.config.js b/webpack.config.js
index 7dee35a..69dfa0c 100755
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -1,51 +1,51 @@
const path = require("path");
const webpack = require("webpack");
+const SERVER_ROOT = require("./package.json").homepage;
+
module.exports = {
- entry: "./src/index.tsx",
- mode: "development",
- module: {
- rules: [
- {
- test: /\.tsx?$/,
- exclude: /(node_modules|bower_components|\.d\.ts$)/,
- use: [
- {
- loader: "babel-loader",
- options: { presets: ["@babel/env"] },
- },
- { loader: "ts-loader" },
- ],
- },
- {
- test: /\.d\.ts$/,
- loader: 'ignore-loader'
- },
- {
- test: /\.css$/,
- use: ["style-loader", "css-loader"]
- },
- {
- test: /\.(png|jpe?g|gif|ttf|woff2?|eot|svg)$/i,
- use: [
- {
- loader: 'file-loader',
- },
- ],
- }
- ]
- },
- resolve: { extensions: [".tsx", ".ts", ".js", "*"] },
- output: {
- path: path.resolve(__dirname, "dist/"),
- publicPath: "/kadi/static/frontend/",
- filename: "bundle.js"
- },
- devServer: {
- contentBase: path.join(__dirname, "public/"),
- port: 3000,
- publicPath: "http://localhost:3000/dist/",
- hotOnly: true
- },
- plugins: [new webpack.HotModuleReplacementPlugin()]
+ entry: "./src/index.tsx",
+ mode: "production",
+ module: {
+ rules: [
+ {
+ test: /\.tsx?$/,
+ exclude: /(node_modules|bower_components|\.d\.ts$)/,
+ use: [
+ "react-hot-loader/webpack",
+ "babel-loader",
+ ],
+ },
+ {
+ test: /\.d\.ts$/,
+ loader: 'ignore-loader'
+ },
+ {
+ test: /\.css$/,
+ use: ["style-loader", "css-loader"]
+ },
+ {
+ test: /\.(png|jpe?g|gif|ttf|woff2?|eot|svg)$/i,
+ use: [
+ {
+ loader: 'file-loader',
+ },
+ ],
+ }
+ ]
+ },
+ resolve: { extensions: [".tsx", ".ts", ".js", "*"] },
+ output: {
+ path: path.resolve(__dirname, "dist/"),
+ publicPath: SERVER_ROOT + "/static/frontend/",
+ filename: "bundle.js"
+ },
+ devServer: {
+ contentBase: path.join(__dirname, "public/"),
+ contentBasePublicPath: SERVER_ROOT + "/",
+ port: 3000,
+ publicPath: "http://localhost:3000" + SERVER_ROOT + "/static/frontend/",
+ hotOnly: true
+ },
+ plugins: [new webpack.HotModuleReplacementPlugin()]
};