From 48ee1a065debde5027fc17e49144d348258dc5e4 Mon Sep 17 00:00:00 2001
From: notoraptor <stevenbocco@gmail.com>
Date: Thu, 25 Jul 2019 10:59:36 -0400
Subject: [Web] Added game creation interface

- Replaced fancybox with react-confirm-alert + dialog box
- Removed unused code
- Default map can be selected with 1-click
- Added ability to select map variants
---
 diplomacy/web/package-lock.json                    |  21 ++--
 diplomacy/web/package.json                         |   3 +-
 diplomacy/web/src/diplomacy/utils/utils.js         |   4 +
 diplomacy/web/src/gui/components/fancyBox.js       |  47 +++++++++
 diplomacy/web/src/gui/components/fancybox.jsx      |  59 -----------
 diplomacy/web/src/gui/components/help.jsx          |  30 ++++--
 diplomacy/web/src/gui/components/navigation.jsx    |   2 +-
 diplomacy/web/src/gui/forms/create_form.jsx        |  95 -----------------
 .../web/src/gui/forms/select_location_form.jsx     |  19 ++--
 diplomacy/web/src/gui/forms/select_via_form.jsx    |  16 ++-
 diplomacy/web/src/gui/pages/content_game.jsx       |  74 +++++---------
 diplomacy/web/src/gui/pages/content_games.jsx      |  53 +++++-----
 diplomacy/web/src/gui/pages/page.jsx               |  33 +++---
 .../gui/wizards/gameCreation/gameCreationWizard.js | 109 ++++++++++++++++++++
 .../web/src/gui/wizards/gameCreation/mapList.js    |  41 ++++++++
 .../src/gui/wizards/gameCreation/panelChooseMap.js |  94 +++++++++++++++++
 .../gui/wizards/gameCreation/panelChoosePlayers.js |  61 +++++++++++
 .../gui/wizards/gameCreation/panelChoosePower.js   |  62 +++++++++++
 .../wizards/gameCreation/panelChooseSettings.js    | 113 +++++++++++++++++++++
 .../web/src/gui/wizards/gameCreation/panelList.js  |   6 ++
 diplomacy/web/src/index.css                        |  34 ++++++-
 diplomacy/web/src/maps                             |   1 +
 22 files changed, 699 insertions(+), 278 deletions(-)
 create mode 100644 diplomacy/web/src/gui/components/fancyBox.js
 delete mode 100644 diplomacy/web/src/gui/components/fancybox.jsx
 delete mode 100644 diplomacy/web/src/gui/forms/create_form.jsx
 create mode 100644 diplomacy/web/src/gui/wizards/gameCreation/gameCreationWizard.js
 create mode 100644 diplomacy/web/src/gui/wizards/gameCreation/mapList.js
 create mode 100644 diplomacy/web/src/gui/wizards/gameCreation/panelChooseMap.js
 create mode 100644 diplomacy/web/src/gui/wizards/gameCreation/panelChoosePlayers.js
 create mode 100644 diplomacy/web/src/gui/wizards/gameCreation/panelChoosePower.js
 create mode 100644 diplomacy/web/src/gui/wizards/gameCreation/panelChooseSettings.js
 create mode 100644 diplomacy/web/src/gui/wizards/gameCreation/panelList.js
 create mode 120000 diplomacy/web/src/maps

diff --git a/diplomacy/web/package-lock.json b/diplomacy/web/package-lock.json
index 6f65557..077f9fe 100644
--- a/diplomacy/web/package-lock.json
+++ b/diplomacy/web/package-lock.json
@@ -926,14 +926,6 @@
       "resolved": "https://registry.npmjs.org/@csstools/normalize.css/-/normalize.css-9.0.1.tgz",
       "integrity": "sha512-6It2EVfGskxZCQhuykrfnALg7oVeiI6KclWSmGDqB0AiInVrTGB9Jp9i4/Ad21u9Jde/voVQz6eFX/eSg/UsPA=="
     },
-    "@githubprimer/octicons-react": {
-      "version": "8.5.0",
-      "resolved": "https://registry.npmjs.org/@githubprimer/octicons-react/-/octicons-react-8.5.0.tgz",
-      "integrity": "sha512-Tb9Nu4usHON3EGiyCnppNQoJmSJvhUw+iD1URpOsoc69YWv96jIcxUAU4/Tw/D6ydzOubf3H9kPdLKVqz8XOnQ==",
-      "requires": {
-        "prop-types": "^15.6.1"
-      }
-    },
     "@hapi/address": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/@hapi/address/-/address-2.0.0.tgz",
@@ -1198,6 +1190,14 @@
       "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz",
       "integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw=="
     },
+    "@primer/octicons-react": {
+      "version": "9.1.1",
+      "resolved": "https://registry.npmjs.org/@primer/octicons-react/-/octicons-react-9.1.1.tgz",
+      "integrity": "sha512-+ZgALoxUOYUeEnqqN6ZqSfRP6LDRgfmErhY4ZIuGlw5Ocjj7AI87J68dD/wYqWl4IW7xE6rmLvpC3kU3iGmAfQ==",
+      "requires": {
+        "prop-types": "^15.6.1"
+      }
+    },
     "@svgr/babel-plugin-add-jsx-attribute": {
       "version": "4.2.0",
       "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-4.2.0.tgz",
@@ -10079,6 +10079,11 @@
         "whatwg-fetch": "3.0.0"
       }
     },
+    "react-confirm-alert": {
+      "version": "2.4.1",
+      "resolved": "https://registry.npmjs.org/react-confirm-alert/-/react-confirm-alert-2.4.1.tgz",
+      "integrity": "sha512-Sc2N1paCTCS5HWEAhik2IQa9/vwSQLAoCT5uccjPH/VyTaBAkRPZPx9sUqFTy3q5VnnGwCPsoz7fnw54x79d/w=="
+    },
     "react-dev-utils": {
       "version": "9.0.1",
       "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-9.0.1.tgz",
diff --git a/diplomacy/web/package.json b/diplomacy/web/package.json
index 5e1d505..aaf89c8 100644
--- a/diplomacy/web/package.json
+++ b/diplomacy/web/package.json
@@ -4,13 +4,14 @@
   "homepage": ".",
   "private": true,
   "dependencies": {
-    "@githubprimer/octicons-react": "^8.5.0",
+    "@primer/octicons-react": "^9.1.1",
     "bootstrap": "^4.3.1",
     "fancybox": "^3.0.1",
     "jquery": "^3.4.1",
     "popper.js": "^1.15.0",
     "prop-types": "^15.7.2",
     "react": "^16.8.6",
+    "react-confirm-alert": "^2.4.1",
     "react-dom": "^16.8.6",
     "react-helmet": "^5.2.1",
     "react-inlinesvg": "^0.8.4",
diff --git a/diplomacy/web/src/diplomacy/utils/utils.js b/diplomacy/web/src/diplomacy/utils/utils.js
index f398cd2..8e54515 100644
--- a/diplomacy/web/src/diplomacy/utils/utils.js
+++ b/diplomacy/web/src/diplomacy/utils/utils.js
@@ -37,6 +37,10 @@ export const UTILS = {
         return id;
     },
 
+    createGameID: function (username) {
+        return `${username}_${new Date().getTime().toString(10)}`;
+    },
+
     date: function () {
         const d = new Date();
         return d.toLocaleString() + '.' + d.getMilliseconds();
diff --git a/diplomacy/web/src/gui/components/fancyBox.js b/diplomacy/web/src/gui/components/fancyBox.js
new file mode 100644
index 0000000..1e8c07c
--- /dev/null
+++ b/diplomacy/web/src/gui/components/fancyBox.js
@@ -0,0 +1,47 @@
+// ==============================================================================
+// Copyright (C) 2019 - Philip Paquette, Steven Bocco
+//
+//  This program is free software: you can redistribute it and/or modify it under
+//  the terms of the GNU Affero General Public License as published by the Free
+//  Software Foundation, either version 3 of the License, or (at your option) any
+//  later version.
+//
+//  This program is distributed in the hope that it will be useful, but WITHOUT
+//  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+//  FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more
+//  details.
+//
+//  You should have received a copy of the GNU Affero General Public License along
+//  with this program.  If not, see <https://www.gnu.org/licenses/>.
+// ==============================================================================
+import React from 'react';
+import PropTypes from 'prop-types';
+import {Button} from "./button";
+
+const TIMES = '\u00D7';
+
+export class FancyBox extends React.Component {
+    render() {
+        return (
+            <div className="fancy-box">
+                <div className="fancy-bar p-1 d-flex flex-row">
+                    <div
+                        className="flex-grow-1 fancy-title d-flex flex-column justify-content-center pr-0 pr-sm-1">{this.props.title}</div>
+                    <div className="fancy-button">
+                        <Button title={TIMES} color={'danger'} onClick={this.props.onClose}/>
+                    </div>
+                </div>
+                <div>
+                    <div className="col fancy-content">{this.props.children}</div>
+                </div>
+            </div>
+        );
+    }
+}
+
+
+FancyBox.propTypes = {
+    title: PropTypes.string.isRequired,
+    onClose: PropTypes.func.isRequired,
+    children: PropTypes.any.isRequired
+};
diff --git a/diplomacy/web/src/gui/components/fancybox.jsx b/diplomacy/web/src/gui/components/fancybox.jsx
deleted file mode 100644
index 66a1efe..0000000
--- a/diplomacy/web/src/gui/components/fancybox.jsx
+++ /dev/null
@@ -1,59 +0,0 @@
-// ==============================================================================
-// Copyright (C) 2019 - Philip Paquette, Steven Bocco
-//
-//  This program is free software: you can redistribute it and/or modify it under
-//  the terms of the GNU Affero General Public License as published by the Free
-//  Software Foundation, either version 3 of the License, or (at your option) any
-//  later version.
-//
-//  This program is distributed in the hope that it will be useful, but WITHOUT
-//  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-//  FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more
-//  details.
-//
-//  You should have received a copy of the GNU Affero General Public License along
-//  with this program.  If not, see <https://www.gnu.org/licenses/>.
-// ==============================================================================
-import React from 'react';
-import PropTypes from 'prop-types';
-import {Button} from "./button";
-
-const TIMES = '\u00D7';
-
-export class FancyBox extends React.Component {
-    // open-tag (<FancyBox></FancyBox>)
-    // PROPERTIES
-    // title
-    // onClose
-    render() {
-        return (
-            <div className={'fancy-wrapper'} onClick={this.props.onClose}>
-                <div className={'fancy-box container'} onClick={(event) => {
-                    if (!event)
-                        event = window.event;
-                    if (event.hasOwnProperty('cancelBubble'))
-                        event.cancelBubble = true;
-                    if (event.stopPropagation)
-                        event.stopPropagation();
-                }}>
-                    <div className={'row fancy-bar'}>
-                        <div className={'col-11 align-self-center fancy-title'}>{this.props.title}</div>
-                        <div className={'col-1 fancy-button'}>
-                            <Button title={TIMES} color={'danger'} onClick={this.props.onClose}/>
-                        </div>
-                    </div>
-                    <div className={'row'}>
-                        <div className={'col fancy-content'}>{this.props.children}</div>
-                    </div>
-                </div>
-            </div>
-        );
-    }
-}
-
-
-FancyBox.propTypes = {
-    title: PropTypes.string.isRequired,
-    onClose: PropTypes.func.isRequired,
-    children: PropTypes.oneOfType([PropTypes.array, PropTypes.object])
-};
diff --git a/diplomacy/web/src/gui/components/help.jsx b/diplomacy/web/src/gui/components/help.jsx
index 1ec1a54..f3f469f 100644
--- a/diplomacy/web/src/gui/components/help.jsx
+++ b/diplomacy/web/src/gui/components/help.jsx
@@ -1,13 +1,23 @@
 import React from "react";
+import PropTypes from 'prop-types';
+import {FancyBox} from "./fancyBox";
 
-export function Help() {
-    return (
-        <div>
-            <p>When building an order, press <strong>ESC</strong> to reset build.</p>
-            <p>Press letter associated to an order type to start building an order of this type.
-                <br/> Order type letter is indicated in order type name after order type radio button.
-            </p>
-            <p>In Phase History tab, use keyboard left and right arrows to navigate in past phases.</p>
-        </div>
-    );
+export class Help extends React.Component {
+    render() {
+        return (
+            <FancyBox title={'Help'} onClose={this.props.onClose}>
+                <div>
+                    <p>When building an order, press <strong>ESC</strong> to reset build.</p>
+                    <p>Press letter associated to an order type to start building an order of this type.
+                        <br/> Order type letter is indicated in order type name after order type radio button.
+                    </p>
+                    <p>In Phase History tab, use keyboard left and right arrows to navigate in past phases.</p>
+                </div>
+            </FancyBox>
+        );
+    }
 }
+
+Help.propTypes = {
+    onClose: PropTypes.func.isRequired
+};
diff --git a/diplomacy/web/src/gui/components/navigation.jsx b/diplomacy/web/src/gui/components/navigation.jsx
index 5d961bc..051f8ea 100644
--- a/diplomacy/web/src/gui/components/navigation.jsx
+++ b/diplomacy/web/src/gui/components/navigation.jsx
@@ -1,5 +1,5 @@
 import React from "react";
-import Octicon, {Person} from "@githubprimer/octicons-react";
+import Octicon, {Person} from "@primer/octicons-react";
 import PropTypes from "prop-types";
 
 export class Navigation extends React.Component {
diff --git a/diplomacy/web/src/gui/forms/create_form.jsx b/diplomacy/web/src/gui/forms/create_form.jsx
deleted file mode 100644
index 6753519..0000000
--- a/diplomacy/web/src/gui/forms/create_form.jsx
+++ /dev/null
@@ -1,95 +0,0 @@
-// ==============================================================================
-// Copyright (C) 2019 - Philip Paquette, Steven Bocco
-//
-//  This program is free software: you can redistribute it and/or modify it under
-//  the terms of the GNU Affero General Public License as published by the Free
-//  Software Foundation, either version 3 of the License, or (at your option) any
-//  later version.
-//
-//  This program is distributed in the hope that it will be useful, but WITHOUT
-//  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-//  FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more
-//  details.
-//
-//  You should have received a copy of the GNU Affero General Public License along
-//  with this program.  If not, see <https://www.gnu.org/licenses/>.
-// ==============================================================================
-import React from 'react';
-import {Forms} from "../components/forms";
-import {STRINGS} from "../../diplomacy/utils/strings";
-import PropTypes from "prop-types";
-
-export class CreateForm extends React.Component {
-    constructor(props) {
-        super(props);
-        this.state = this.initState();
-    }
-
-    initState() {
-        const state = {
-            game_id: '',
-            power_name: '',
-            n_controls: 7,
-            deadline: 300,
-            registration_password: ''
-        };
-        for (let rule of STRINGS.PUBLIC_RULES)
-            state[`rule_${rule.toLowerCase()}`] = false;
-        return state;
-    }
-
-    render() {
-        const onChange = Forms.createOnChangeCallback(this, this.props.onChange);
-        const onSubmit = Forms.createOnSubmitCallback(this, this.props.onSubmit);
-        return (
-            <form>
-                {Forms.createRow(
-                    Forms.createColLabel('game_id', 'Game ID (optional)'),
-                    <input id={'game_id'} className={'form-control'} type={'text'}
-                           value={Forms.getValue(this.state, 'game_id')} onChange={onChange}/>
-                )}
-                {Forms.createRow(
-                    Forms.createColLabel('power_name', 'power:'),
-                    <select id={'power_name'} className={'form-control custom-select'}
-                            value={Forms.getValue(this.state, 'power_name')} onChange={onChange}>
-                        {Forms.createSelectOptions(STRINGS.ALL_POWER_NAMES, true)}
-                    </select>
-                )}
-                {Forms.createRow(
-                    Forms.createColLabel('n_controls', 'number of required players:'),
-                    <input id={'n_controls'} className={'form-control'} type={'number'}
-                           value={Forms.getValue(this.state, 'n_controls')} onChange={onChange}/>
-                )}
-                {Forms.createRow(
-                    Forms.createColLabel('deadline', 'deadline (in seconds)'),
-                    <input id={'deadline'} className={'form-control'} type={'number'}
-                           value={Forms.getValue(this.state, 'deadline')}
-                           onChange={onChange}/>
-                )}
-                {Forms.createRow(
-                    Forms.createColLabel('registration_password', 'registration password'),
-                    <input id={'registration_password'} className={'form-control'} type={'password'}
-                           value={Forms.getValue(this.state, 'registration_password')} onChange={onChange}/>
-                )}
-                <div><strong>RULES:</strong></div>
-                <div className={'mb-4'}>
-                    {STRINGS.PUBLIC_RULES.map((rule, index) => (
-                        <div key={index} className={'form-check-inline'}>
-                            {Forms.createCheckbox(
-                                `rule_${rule.toLowerCase()}`,
-                                rule,
-                                Forms.getValue(this.state, `rule_${rule.toLowerCase()}`),
-                                onChange)}
-                        </div>
-                    ))}
-                </div>
-                {Forms.createRow('', Forms.createSubmit('create a game', true, onSubmit))}
-            </form>
-        );
-    }
-}
-
-CreateForm.propTypes = {
-    onChange: PropTypes.func,
-    onSubmit: PropTypes.func
-};
diff --git a/diplomacy/web/src/gui/forms/select_location_form.jsx b/diplomacy/web/src/gui/forms/select_location_form.jsx
index ca7be09..1725c9b 100644
--- a/diplomacy/web/src/gui/forms/select_location_form.jsx
+++ b/diplomacy/web/src/gui/forms/select_location_form.jsx
@@ -17,20 +17,27 @@
 import React from "react";
 import PropTypes from "prop-types";
 import {Button} from "../components/button";
+import {FancyBox} from "../components/fancyBox";
 
 export class SelectLocationForm extends React.Component {
     render() {
+        const title = `Select location to continue building order: ${this.props.path.join(' ')}`;
         return (
-            <div>
-                {this.props.locations.map((location, index) => (
-                    <Button key={index} title={location} large={true} onClick={() => this.props.onSelect(location)}/>
-                ))}
-            </div>
+            <FancyBox title={title} onClose={this.props.onClose}>
+                <div>
+                    {this.props.locations.map((location, index) => (
+                        <Button key={index} title={location} large={true}
+                                onClick={() => this.props.onSelect(location)}/>
+                    ))}
+                </div>
+            </FancyBox>
         );
     }
 }
 
 SelectLocationForm.propTypes = {
     locations: PropTypes.arrayOf(PropTypes.string).isRequired,
-    onSelect: PropTypes.func.isRequired // onSelect(location)
+    onSelect: PropTypes.func.isRequired, // onSelect(location)
+    onClose: PropTypes.func.isRequired,
+    path: PropTypes.array.isRequired
 };
diff --git a/diplomacy/web/src/gui/forms/select_via_form.jsx b/diplomacy/web/src/gui/forms/select_via_form.jsx
index b779b8d..8b340af 100644
--- a/diplomacy/web/src/gui/forms/select_via_form.jsx
+++ b/diplomacy/web/src/gui/forms/select_via_form.jsx
@@ -17,19 +17,25 @@
 import React from "react";
 import PropTypes from "prop-types";
 import {Button} from "../components/button";
+import {FancyBox} from "../components/fancyBox";
 
 export class SelectViaForm extends React.Component {
     render() {
         return (
-            <div>
-                <Button title={'regular move (M)'} large={true} onClick={() => this.props.onSelect('M')}/>
-                <Button title={'move via (V)'} large={true} onClick={() => this.props.onSelect('V')}/>
-            </div>
+            <FancyBox title={`Select move type for move order: ${this.props.path.join(' ')}`}
+                      onClose={this.props.onClose}>
+                <div>
+                    <Button title={'regular move (M)'} large={true} onClick={() => this.props.onSelect('M')}/>
+                    <Button title={'move via (V)'} large={true} onClick={() => this.props.onSelect('V')}/>
+                </div>
+            </FancyBox>
         );
     }
 }
 
 SelectViaForm.propTypes = {
-    onSelect: PropTypes.func.isRequired
+    path: PropTypes.array.isRequired,
+    onSelect: PropTypes.func.isRequired,
+    onClose: PropTypes.func.isRequired
 };
 
diff --git a/diplomacy/web/src/gui/pages/content_game.jsx b/diplomacy/web/src/gui/pages/content_game.jsx
index f37bdca..457d901 100644
--- a/diplomacy/web/src/gui/pages/content_game.jsx
+++ b/diplomacy/web/src/gui/pages/content_game.jsx
@@ -33,7 +33,6 @@ import {STRINGS} from "../../diplomacy/utils/strings";
 import {Diplog} from "../../diplomacy/utils/diplog";
 import {Table} from "../components/table";
 import {PowerView} from "../utils/power_view";
-import {FancyBox} from "../components/fancybox";
 import {DipStorage} from "../utils/dipStorage";
 import Helmet from 'react-helmet';
 import {Navigation} from "../components/navigation";
@@ -116,14 +115,11 @@ export class ContentGame extends React.Component {
             orders: orders, // {power name => {loc => {local: bool, order: str}}}
             power: null,
             orderBuildingType: null,
-            orderBuildingPath: [],
-            fancy_title: null,
-            fancy_function: null,
-            on_fancy_close: null,
+            orderBuildingPath: []
         };
 
         // Bind some class methods to this instance.
-        this.closeFancyBox = this.closeFancyBox.bind(this);
+        this.clearOrderBuildingPath = this.clearOrderBuildingPath.bind(this);
         this.displayFirstPastPhase = this.displayFirstPastPhase.bind(this);
         this.displayLastPastPhase = this.displayLastPastPhase.bind(this);
         this.displayLocationOrders = this.displayLocationOrders.bind(this);
@@ -202,11 +198,8 @@ export class ContentGame extends React.Component {
         return this.context;
     }
 
-    closeFancyBox() {
+    clearOrderBuildingPath() {
         this.setState({
-            fancy_title: null,
-            fancy_function: null,
-            on_fancy_close: null,
             orderBuildingPath: []
         });
     }
@@ -220,11 +213,6 @@ export class ContentGame extends React.Component {
             powerName, orderType, orderPath, location,
             this.onOrderBuilding, this.onOrderBuilt, this.getPage().error
         );
-        this.setState({
-            fancy_title: null,
-            fancy_function: null,
-            on_fancy_close: null
-        });
     }
 
     setSelectedVia(moveType, powerName, orderPath, location) {
@@ -234,33 +222,35 @@ export class ContentGame extends React.Component {
             powerName, moveType, orderPath, location,
             this.onOrderBuilding, this.onOrderBuilt, this.getPage().error
         );
-        this.setState({
-            fancy_title: null,
-            fancy_function: null,
-            on_fancy_close: null
-        });
     }
 
     onSelectLocation(possibleLocations, powerName, orderType, orderPath) {
-        const title = `Select location to continue building order: ${orderPath.join(' ')} ... (press ESC or close button to cancel building)`;
-        const func = () => (<SelectLocationForm locations={possibleLocations}
-                                                onSelect={(location) => this.setSelectedLocation(location, powerName, orderType, orderPath)}/>);
-        this.setState({
-            fancy_title: title,
-            fancy_function: func,
-            on_fancy_close: this.closeFancyBox
-        });
+        this.getPage().dialog(onClose => (
+            <SelectLocationForm path={orderPath}
+                                locations={possibleLocations}
+                                onSelect={(location) => {
+                                    this.setSelectedLocation(location, powerName, orderType, orderPath);
+                                    onClose();
+                                }}
+                                onClose={() => {
+                                    this.clearOrderBuildingPath();
+                                    onClose();
+                                }}/>
+        ));
     }
 
     onSelectVia(location, powerName, orderPath) {
-        const title = `Select move type for move order: ${orderPath.join(' ')}`;
-        const func = () => (
-            <SelectViaForm onSelect={(moveType) => this.setSelectedVia(moveType, powerName, orderPath, location)}/>);
-        this.setState({
-            fancy_title: title,
-            fancy_function: func,
-            on_fancy_close: this.closeFancyBox
-        });
+        this.getPage().dialog(onClose => (
+            <SelectViaForm path={orderPath}
+                           onSelect={(moveType) => {
+                               this.setSelectedVia(moveType, powerName, orderPath, location);
+                               onClose();
+                           }}
+                           onClose={() => {
+                               this.clearOrderBuildingPath();
+                               onClose();
+                           }}/>
+        ));
     }
 
     // ]
@@ -682,9 +672,6 @@ export class ContentGame extends React.Component {
     onOrderBuilt(powerName, orderString) {
         const state = Object.assign({}, this.state);
         state.orderBuildingPath = [];
-        state.fancy_title = null;
-        state.fancy_function = null;
-        state.on_fancy_close = null;
         if (!orderString) {
             Diplog.warn('No order built.');
             this.setState(state);
@@ -712,9 +699,6 @@ export class ContentGame extends React.Component {
         this.setState({
             orderBuildingType: form.order_type,
             orderBuildingPath: [],
-            fancy_title: null,
-            fancy_function: null,
-            on_fancy_close: null
         });
     }
 
@@ -1162,7 +1146,7 @@ export class ContentGame extends React.Component {
         const engine = this.props.data;
         const title = ContentGame.gameTitle(engine);
         const navigation = [
-            ['Help', () => page.loadFancyBox('Help', () => <Help/>)],
+            ['Help', () => page.dialog(onClose => <Help onClose={onClose}/>)],
             ['Load a game from disk', page.loadGameFromDisk],
             ['Save game to disk', () => saveGameToDisk(engine, page.error)],
             [`${UTILS.html.UNICODE_SMALL_LEFT_ARROW} Games`, () => page.loadGames()],
@@ -1292,10 +1276,6 @@ export class ContentGame extends React.Component {
                         currentTabOrderCreation
                     )) || ''}
                 </Tabs>
-                {this.state.fancy_title && (
-                    <FancyBox title={this.state.fancy_title} onClose={this.state.on_fancy_close}>
-                        {this.state.fancy_function()}
-                    </FancyBox>)}
             </main>
         );
     }
diff --git a/diplomacy/web/src/gui/pages/content_games.jsx b/diplomacy/web/src/gui/pages/content_games.jsx
index 31bd1af..5250f03 100644
--- a/diplomacy/web/src/gui/pages/content_games.jsx
+++ b/diplomacy/web/src/gui/pages/content_games.jsx
@@ -18,15 +18,15 @@ import React from "react";
 import {Tabs} from "../components/tabs";
 import {Table} from "../components/table";
 import {FindForm} from "../forms/find_form";
-import {CreateForm} from "../forms/create_form";
 import {InlineGameView} from "../utils/inline_game_view";
-import {STRINGS} from "../../diplomacy/utils/strings";
 import {Helmet} from "react-helmet";
 import {Navigation} from "../components/navigation";
 import {PageContext} from "../components/page_context";
 import {ContentGame} from "./content_game";
 import PropTypes from 'prop-types';
 import {Tab} from "../components/tab";
+import {GameCreationWizard} from "../wizards/gameCreation/gameCreationWizard";
+import {Diplog} from "../../diplomacy/utils/diplog";
 
 const TABLE_LOCAL_GAMES = {
     game_id: ['Game ID', 0],
@@ -71,23 +71,6 @@ export class ContentGames extends React.Component {
     }
 
     onCreate(form) {
-        for (let key of Object.keys(form)) {
-            if (form[key] === '')
-                form[key] = null;
-        }
-        if (form.n_controls !== null)
-            form.n_controls = parseInt(form.n_controls, 10);
-        if (form.deadline !== null)
-            form.deadline = parseInt(form.deadline, 10);
-        form.rules = ['POWER_CHOICE'];
-        for (let rule of STRINGS.PUBLIC_RULES) {
-            const rule_id = `rule_${rule.toLowerCase()}`;
-            if (form.hasOwnProperty(rule_id)) {
-                if (form[rule_id])
-                    form.rules.push(rule);
-                delete form[rule_id];
-            }
-        }
         let networkGame = null;
         this.getPage().channel.createGame(form)
             .then((game) => {
@@ -116,6 +99,28 @@ export class ContentGames extends React.Component {
         return new InlineGameView(this.getPage(), gameData);
     }
 
+    gameCreationButton() {
+        return (
+            <button type="button"
+                    className="btn btn-danger btn-sm mx-0 mx-sm-4"
+                    onClick={() => this.getPage().dialog(onClose => (
+                        <GameCreationWizard availableMaps={this.getPage().availableMaps}
+                                            onCancel={onClose}
+                                            username={this.getPage().channel.username}
+                                            onSubmit={(form) => {
+                                                onClose();
+                                                Diplog.info(`Creating game:`);
+                                                for (let entry of Object.entries(form)) {
+                                                    Diplog.info(`${entry[0]}: ${entry[1] ? entry[1].toString() : entry[1]}`);
+                                                }
+                                                this.onCreate(form);
+                                            }}/>
+                    ))}>
+                <strong>create a game</strong>
+            </button>
+        );
+    }
+
     render() {
         const title = 'Games';
         const page = this.getPage();
@@ -133,14 +138,10 @@ export class ContentGames extends React.Component {
                 <Helmet>
                     <title>{title} | Diplomacy</title>
                 </Helmet>
-                <Navigation title={title} username={page.channel.username} navigation={navigation}/>
-                <Tabs menu={['create', 'find', 'my-games']} titles={['Create', 'Find', 'My Games']}
+                <Navigation title={title} afterTitle={this.gameCreationButton()}
+                            username={page.channel.username} navigation={navigation}/>
+                <Tabs menu={['find', 'my-games']} titles={['Find', 'My Games']}
                       onChange={this.changeTab} active={tab}>
-                    {tab === 'create' ? (
-                        <Tab id="tab-games-create" display={true}>
-                            <CreateForm onSubmit={this.onCreate}/>
-                        </Tab>
-                    ) : ''}
                     {tab === 'find' ? (
                         <Tab id="tab-games-find" display={true}>
                             <FindForm onSubmit={this.onFind}/>
diff --git a/diplomacy/web/src/gui/pages/page.jsx b/diplomacy/web/src/gui/pages/page.jsx
index cd36f6c..a9ff9ac 100644
--- a/diplomacy/web/src/gui/pages/page.jsx
+++ b/diplomacy/web/src/gui/pages/page.jsx
@@ -20,12 +20,13 @@ import React from "react";
 import {ContentConnection} from "./content_connection";
 import {UTILS} from "../../diplomacy/utils/utils";
 import {Diplog} from "../../diplomacy/utils/diplog";
-import {FancyBox} from "../components/fancybox";
 import {DipStorage} from "../utils/dipStorage";
 import {PageContext} from "../components/page_context";
 import {ContentGames} from "./content_games";
 import {loadGameFromDisk} from "../utils/load_game_from_disk";
 import {ContentGame} from "./content_game";
+import {confirmAlert} from 'react-confirm-alert';
+import 'react-confirm-alert/src/react-confirm-alert.css';
 
 export class Page extends React.Component {
 
@@ -35,9 +36,6 @@ export class Page extends React.Component {
         this.channel = null;
         this.availableMaps = null;
         this.state = {
-            // fancybox,
-            fancyTitle: null,
-            onFancyBox: null,
             // Page messages
             error: null,
             info: null,
@@ -54,7 +52,6 @@ export class Page extends React.Component {
         this.success = this.success.bind(this);
         this.logout = this.logout.bind(this);
         this.loadGameFromDisk = this.loadGameFromDisk.bind(this);
-        this.unloadFancyBox = this.unloadFancyBox.bind(this);
         this._post_remove = this._post_remove.bind(this);
         this._add_to_my_games = this._add_to_my_games.bind(this);
         this._remove_from_my_games = this._remove_from_my_games.bind(this);
@@ -80,14 +77,22 @@ export class Page extends React.Component {
         this.__disconnect(error);
     }
 
-    //// Methods to load a global fancybox.
+    /**
+     * @callback OnClose
+     */
 
-    loadFancyBox(title, callback) {
-        this.setState({fancyTitle: title, onFancyBox: callback});
-    }
+    /**
+     * @callback DialogBuilder
+     * @param {OnClose} onClose
+     */
 
-    unloadFancyBox() {
-        this.setState({fancyTitle: null, onFancyBox: null});
+    /**
+     * open a dialog box
+     * @param {DialogBuilder} builder - a callback to generate dialog GUI. Will be executed with a `onClose` callback
+     * parameter to call when dialog must be closed: `builder(onClose)`.
+     */
+    dialog(builder) {
+        confirmAlert({customUI: ({onClose}) => builder(onClose)});
     }
 
     //// Methods to load a page.
@@ -275,7 +280,6 @@ export class Page extends React.Component {
         }
     }
 
-
     disconnectGame(gameID) {
         const game = this.getGame(gameID);
         if (game) {
@@ -363,11 +367,6 @@ export class Page extends React.Component {
                         </div>
                     </div>
                     {this.state.body || Page.defaultPage()}
-                    {this.state.onFancyBox && (
-                        <FancyBox title={this.state.fancyTitle} onClose={this.unloadFancyBox}>
-                            {this.state.onFancyBox()}
-                        </FancyBox>
-                    )}
                 </div>
             </PageContext.Provider>
         );
diff --git a/diplomacy/web/src/gui/wizards/gameCreation/gameCreationWizard.js b/diplomacy/web/src/gui/wizards/gameCreation/gameCreationWizard.js
new file mode 100644
index 0000000..daaa461
--- /dev/null
+++ b/diplomacy/web/src/gui/wizards/gameCreation/gameCreationWizard.js
@@ -0,0 +1,109 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import {Panels} from "./panelList";
+import {PanelChooseMap} from "./panelChooseMap";
+import {PanelChoosePlayers} from "./panelChoosePlayers";
+import {PanelChoosePower} from "./panelChoosePower";
+import {PanelChooseSettings} from "./panelChooseSettings";
+import {Maps} from "./mapList";
+import {UTILS} from "../../../diplomacy/utils/utils";
+
+export class GameCreationWizard extends React.Component {
+    constructor(props) {
+        super(props);
+        this.state = {
+            panel: Panels.CHOOSE_MAP,
+            game_id: UTILS.createGameID(this.props.username),
+            power_name: null,
+            n_controls: -1,
+            deadline: 0,
+            registration_password: '',
+
+            map: Maps[0],
+            no_press: false
+        };
+        this.backward = this.backward.bind(this);
+        this.forward = this.forward.bind(this);
+        this.updateParams = this.updateParams.bind(this);
+    }
+
+    updateParams(params) {
+        this.setState(params);
+    }
+
+    goToPanel(panelID) {
+        if (panelID < Panels.CHOOSE_MAP)
+            this.props.onCancel();
+        else if (panelID > Panels.CHOOSE_SETTINGS) {
+            const rules = ['POWER_CHOICE'];
+            if (this.state.no_press)
+                rules.push('NO_PRESS');
+            if (!this.state.deadline) {
+                rules.push('NO_DEADLINE');
+                rules.push('REAL_TIME');
+            }
+            this.props.onSubmit({
+                game_id: this.state.game_id,
+                map_name: this.state.map.name,
+                power_name: this.state.power_name,
+                n_controls: this.state.n_controls,
+                deadline: this.state.deadline,
+                registration_password: this.state.registration_password || null,
+                rules: rules
+            });
+        } else
+            this.setState({panel: panelID, registration_password: ''});
+    }
+
+    backward(step) {
+        this.goToPanel(this.state.panel - (step ? step : 1));
+    }
+
+    forward(step) {
+        this.goToPanel(this.state.panel + (step ? step : 1));
+    }
+
+    renderPanel() {
+        switch (this.state.panel) {
+            case Panels.CHOOSE_MAP:
+                return <PanelChooseMap forward={this.forward}
+                                       params={this.state}
+                                       onUpdateParams={this.updateParams}
+                                       cancel={this.props.onCancel}/>;
+            case Panels.CHOOSE_PLAYERS:
+                return <PanelChoosePlayers backward={this.backward}
+                                           forward={this.forward}
+                                           onUpdateParams={this.updateParams}
+                                           nbPowers={this.props.availableMaps[this.state.map.name].powers.length}
+                                           cancel={this.props.onCancel}/>;
+            case Panels.CHOOSE_POWER:
+                return <PanelChoosePower backward={this.backward}
+                                         forward={this.forward}
+                                         onUpdateParams={this.updateParams}
+                                         powers={this.props.availableMaps[this.state.map.name].powers}
+                                         cancel={this.props.onCancel}/>;
+            case Panels.CHOOSE_SETTINGS:
+                return <PanelChooseSettings backward={this.backward}
+                                            forward={this.forward}
+                                            onUpdateParams={this.updateParams}
+                                            username={this.props.username}
+                                            params={this.state}
+                                            cancel={this.props.onCancel}/>;
+            default:
+                return '';
+        }
+    }
+
+    render() {
+        return (
+            <div className="game-creation-wizard">{this.renderPanel()}</div>
+        );
+    }
+}
+
+GameCreationWizard.propTypes = {
+    onCancel: PropTypes.func.isRequired,
+    onSubmit: PropTypes.func.isRequired,
+    availableMaps: PropTypes.object.isRequired,
+    username: PropTypes.string.isRequired
+};
diff --git a/diplomacy/web/src/gui/wizards/gameCreation/mapList.js b/diplomacy/web/src/gui/wizards/gameCreation/mapList.js
new file mode 100644
index 0000000..5d2c00a
--- /dev/null
+++ b/diplomacy/web/src/gui/wizards/gameCreation/mapList.js
@@ -0,0 +1,41 @@
+class VariantInfo {
+    constructor(variantName, variantTitle) {
+        this.name = variantName;
+        this.title = variantTitle;
+        this.map = null;
+    }
+
+    svgName() {
+        return this.map.name;
+    }
+}
+
+class MapInfo {
+    constructor(mapName, mapTitle, variants) {
+        this.name = mapName;
+        this.title = mapTitle;
+        this.variants = null;
+        if (variants) {
+            this.variants = [];
+            for (let variant of variants) {
+                variant.map = this;
+                this.variants.push(variant);
+            }
+        }
+    }
+
+    svgName() {
+        return this.name;
+    }
+}
+
+export const Maps = [
+    new MapInfo('standard', 'Standard', [
+        new VariantInfo('standard', 'Default'),
+        new VariantInfo('standard_age_of_empires', 'Age of empires'),
+        new VariantInfo('standard_age_of_empires_2', 'Age of empires II'),
+        new VariantInfo('standard_fleet_rome', 'Fleet at Rome'),
+        new VariantInfo('standard_france_austria', 'France VS Austria'),
+        new VariantInfo('standard_germany_italy', 'Germany VS Italy')
+    ]),
+];
diff --git a/diplomacy/web/src/gui/wizards/gameCreation/panelChooseMap.js b/diplomacy/web/src/gui/wizards/gameCreation/panelChooseMap.js
new file mode 100644
index 0000000..5c40f1c
--- /dev/null
+++ b/diplomacy/web/src/gui/wizards/gameCreation/panelChooseMap.js
@@ -0,0 +1,94 @@
+import React from "react";
+import {Maps} from "./mapList";
+import {FancyBox} from "../../components/fancyBox";
+import PropTypes from "prop-types";
+
+export class PanelChooseMap extends React.Component {
+    render() {
+        const mapImg = require(`../../../maps/svg/${this.props.params.map.svgName()}.svg`);
+        const mapEntries = [];
+        let count = 0;
+        for (let mapInfo of Maps) {
+            ++count;
+            if (!mapInfo.variants) {
+                mapEntries.push(
+                    <div key={count} className="mb-1">
+                        <button type="button"
+                                className="btn btn-secondary btn-sm btn-block"
+                                onMouseOver={() => this.props.onUpdateParams({map: mapInfo})}
+                                onClick={() => this.props.forward()}>
+                            {mapInfo.title}
+                        </button>
+                    </div>
+                );
+            } else {
+                const dropDownID = `collapse-${count}-${mapInfo.name}`;
+                const variants = mapInfo.variants.slice();
+                const defaultVariant = variants[0];
+                mapEntries.push(
+                    <div key={count}>
+                        <div className="mb-1 d-flex flex-row justify-content-center">
+                            <button type="button"
+                                    className="btn btn-secondary btn-sm flex-grow-1 mr-1"
+                                    onMouseOver={() => this.props.onUpdateParams({map: defaultVariant})}
+                                    onClick={() => this.props.forward()}>
+                                {mapInfo.title} ({defaultVariant.title})
+                            </button>
+                            <button type="button"
+                                    className="btn btn-outline-secondary btn-sm collapsed"
+                                    data-toggle="collapse"
+                                    data-target={`#${dropDownID}`}
+                                    aria-expanded={false}
+                                    aria-controls={dropDownID}>
+                                <span className="unroll"><strong>+</strong></span>
+                                <span className="roll"><strong>-</strong></span>
+                            </button>
+                        </div>
+                        <div className="collapse" id={dropDownID}>
+                            <div>
+                                {(() => {
+                                    const views = [];
+                                    for (let i = 1; i < variants.length; ++i) {
+                                        const variantInfo = variants[i];
+                                        views.push(
+                                            <div key={variantInfo.name} className="mb-1">
+                                                <button type="button"
+                                                        className="btn btn-outline-secondary btn-sm btn-block"
+                                                        onMouseOver={() => this.props.onUpdateParams({map: variantInfo})}
+                                                        onClick={() => this.props.forward()}>
+                                                    {variantInfo.title}
+                                                </button>
+                                            </div>
+                                        );
+                                    }
+                                    return views;
+                                })()}
+                            </div>
+                        </div>
+                    </div>
+                );
+            }
+        }
+        return (
+            <FancyBox title={'Choose a map'} onClose={this.props.cancel}>
+                <div className="row panel-choose-map">
+                    <div className="col-md">
+                        <div className="map-list p-1 ml-0 ml-sm-1">
+                            {mapEntries}
+                        </div>
+                    </div>
+                    <div className="col-md">
+                        <img className="img-fluid" src={mapImg} alt={this.props.params.map.title}/>
+                    </div>
+                </div>
+            </FancyBox>
+        );
+    }
+}
+
+PanelChooseMap.propTypes = {
+    forward: PropTypes.func.isRequired,
+    cancel: PropTypes.func.isRequired,
+    params: PropTypes.object.isRequired,
+    onUpdateParams: PropTypes.func.isRequired
+};
diff --git a/diplomacy/web/src/gui/wizards/gameCreation/panelChoosePlayers.js b/diplomacy/web/src/gui/wizards/gameCreation/panelChoosePlayers.js
new file mode 100644
index 0000000..84a47a0
--- /dev/null
+++ b/diplomacy/web/src/gui/wizards/gameCreation/panelChoosePlayers.js
@@ -0,0 +1,61 @@
+import React from "react";
+import {FancyBox} from "../../components/fancyBox";
+import PropTypes from "prop-types";
+import Octicon, {ArrowLeft} from "@primer/octicons-react";
+
+export class PanelChoosePlayers extends React.Component {
+    render() {
+        return (
+            <FancyBox title={'Number of human players'} onClose={this.props.cancel}>
+                <div className="row">
+                    <div className="col-sm">
+                        <button type="button" className="btn btn-secondary btn-sm btn-block inline" onClick={() => {
+                            this.props.onUpdateParams({n_controls: 0});
+                            this.props.forward(2);
+                        }}>None - just bots
+                        </button>
+                    </div>
+                    <div className="col-sm">
+                        <button type="button" className="btn btn-secondary btn-sm btn-block inline" onClick={() => {
+                            this.props.onUpdateParams({n_controls: this.props.nbPowers});
+                            this.props.forward();
+                        }}>All humans - no bots
+                        </button>
+                    </div>
+                </div>
+                <div className="d-flex flex-row justify-content-center my-2">
+                    {(() => {
+                        const choice = [];
+                        for (let i = 0; i < this.props.nbPowers; ++i) {
+                            choice.push(
+                                <button key={i} type="button"
+                                        className={`btn btn-secondary btn-sm flex-grow-1 ${i === 0 ? '' : 'ml-sm-1'}`}
+                                        onClick={() => {
+                                            this.props.onUpdateParams({n_controls: i + 1});
+                                            this.props.forward();
+                                        }}>
+                                    {i + 1}
+                                </button>
+                            );
+                        }
+                        return choice;
+                    })()}
+                </div>
+                <div>
+                    <button type="button" className="btn btn-secondary btn-sm px-3"
+                            onClick={() => this.props.backward()}>
+                        <Octicon icon={ArrowLeft}/>
+                    </button>
+                </div>
+            </FancyBox>
+        );
+    }
+}
+
+PanelChoosePlayers.propTypes = {
+    backward: PropTypes.func.isRequired,
+    forward: PropTypes.func.isRequired,
+    cancel: PropTypes.func.isRequired,
+    onUpdateParams: PropTypes.func.isRequired,
+    nbPowers: PropTypes.number.isRequired
+};
diff --git a/diplomacy/web/src/gui/wizards/gameCreation/panelChoosePower.js b/diplomacy/web/src/gui/wizards/gameCreation/panelChoosePower.js
new file mode 100644
index 0000000..dc400bd
--- /dev/null
+++ b/diplomacy/web/src/gui/wizards/gameCreation/panelChoosePower.js
@@ -0,0 +1,62 @@
+import React from "react";
+import {FancyBox} from "../../components/fancyBox";
+import PropTypes from "prop-types";
+import Octicon, {ArrowLeft} from "@primer/octicons-react";
+
+export class PanelChoosePower extends React.Component {
+    render() {
+        return (
+            <FancyBox title={'Choose your power'} onClose={this.props.cancel}>
+                <div className="row">
+                    <div className="col-sm">
+                        <button type="button" className="btn btn-secondary btn-sm btn-block inline" onClick={() => {
+                            this.props.onUpdateParams({power_name: null});
+                            this.props.forward();
+                        }}>I just want to observe
+                        </button>
+                    </div>
+                    <div className="col-sm">
+                        <button type="button" className="btn btn-secondary btn-sm btn-block inline" onClick={() => {
+                            const powerName = this.props.powers[Math.floor(Math.random() * this.props.powers.length)];
+                            this.props.onUpdateParams({power_name: powerName});
+                            this.props.forward();
+                        }}>Choose randomly for me
+                        </button>
+                    </div>
+                </div>
+                <div className="d-flex flex-row justify-content-center my-2">
+                    {(() => {
+                        const choice = [];
+                        for (let i = 0; i < this.props.powers.length; ++i) {
+                            choice.push(
+                                <button key={i} type="button"
+                                        className={`btn btn-secondary btn-sm flex-grow-1 ${i === 0 ? '' : 'ml-sm-1'}`}
+                                        onClick={() => {
+                                            this.props.onUpdateParams({power_name: this.props.powers[i]});
+                                            this.props.forward();
+                                        }}>
+                                    {this.props.powers[i]}
+                                </button>
+                            );
+                        }
+                        return choice;
+                    })()}
+                </div>
+                <div>
+                    <button type="button" className="btn btn-secondary btn-sm px-3"
+                            onClick={() => this.props.backward()}>
+                        <Octicon icon={ArrowLeft}/>
+                    </button>
+                </div>
+            </FancyBox>
+        );
+    }
+}
+
+PanelChoosePower.propTypes = {
+    backward: PropTypes.func.isRequired,
+    forward: PropTypes.func.isRequired,
+    cancel: PropTypes.func.isRequired,
+    onUpdateParams: PropTypes.func.isRequired,
+    powers: PropTypes.arrayOf(PropTypes.string).isRequired
+};
diff --git a/diplomacy/web/src/gui/wizards/gameCreation/panelChooseSettings.js b/diplomacy/web/src/gui/wizards/gameCreation/panelChooseSettings.js
new file mode 100644
index 0000000..e509158
--- /dev/null
+++ b/diplomacy/web/src/gui/wizards/gameCreation/panelChooseSettings.js
@@ -0,0 +1,113 @@
+import React from "react";
+import {FancyBox} from "../../components/fancyBox";
+import PropTypes from "prop-types";
+import {UTILS} from "../../../diplomacy/utils/utils";
+import Octicon, {ArrowLeft} from "@primer/octicons-react";
+
+const DEADLINES = [
+    [0, '(no deadline)'],
+    [60, '1 min'],
+    [60 * 5, '5 min'],
+    [60 * 30, '30 min'],
+    [60 * 60 * 2, '2 hrs'],
+    [60 * 60 * 24, '24 hrs'],
+];
+
+export class PanelChooseSettings extends React.Component {
+    constructor(props) {
+        super(props);
+        this.onCheckNoPress = this.onCheckNoPress.bind(this);
+        this.onSelectDeadline = this.onSelectDeadline.bind(this);
+        this.onSetRegistrationPassword = this.onSetRegistrationPassword.bind(this);
+        this.onSetGameID = this.onSetGameID.bind(this);
+    }
+
+    onCheckNoPress(event) {
+        this.props.onUpdateParams({no_press: event.target.checked});
+    }
+
+    onSelectDeadline(event) {
+        this.props.onUpdateParams({deadline: parseInt(event.target.value)});
+    }
+
+    onSetRegistrationPassword(event) {
+        this.props.onUpdateParams({registration_password: event.target.value});
+    }
+
+    onSetGameID(event) {
+        let gameID = event.target.value;
+        if (!gameID)
+            gameID = UTILS.createGameID(this.props.username);
+        this.props.onUpdateParams({game_id: gameID});
+    }
+
+    render() {
+        return (
+            <FancyBox title={'Other settings'} onClose={this.props.cancel}>
+                <div>
+                    <form>
+                        <div className="form-group row align-items-center mb-2">
+                            <label className="col-md col-form-label" htmlFor="deadline">Deadline</label>
+                            <div className="col-md">
+                                <select id="deadline" className="custom-select custom-select-sm"
+                                        value={this.props.params.deadline}
+                                        onChange={this.onSelectDeadline}>
+                                    {DEADLINES.map((deadline, index) => (
+                                        <option key={index} value={deadline[0]}>{deadline[1]}</option>
+                                    ))}
+                                </select>
+                            </div>
+                        </div>
+                        <div className="form-group row mb-2">
+                            <label className="col-md col-form-label" htmlFor="registration-password">Login
+                                password</label>
+                            <div className="col-md">
+                                <input type="password" className="form-control form-control-sm"
+                                       id="registration-password"
+                                       value={this.props.params.registration_password}
+                                       onChange={this.onSetRegistrationPassword} placeholder="(no password)"/>
+                            </div>
+                        </div>
+                        <div className="form-group row mb-2">
+                            <label className="col-md col-form-label" htmlFor="game-id">Game ID</label>
+                            <div className="col-md">
+                                <input type="text" className="form-control form-control-sm"
+                                       id="game-id"
+                                       value={this.props.params.game_id}
+                                       onChange={this.onSetGameID}/>
+                            </div>
+                        </div>
+                        <div className="custom-control custom-checkbox mb-5">
+                            <input type="checkbox" className="custom-control-input" id="no-press"
+                                   checked={this.props.params.no_press} onChange={this.onCheckNoPress}/>
+                            <label className="custom-control-label" htmlFor="no-press">No messages allowed</label>
+                        </div>
+                    </form>
+                </div>
+                <div className="row">
+                    <div className="col-sm">
+                        <button type="button" className="btn btn-secondary btn-sm btn-block"
+                                onClick={() => this.props.backward()}>
+                            <Octicon icon={ArrowLeft}/>
+                        </button>
+                    </div>
+                    <div className="col-sm">
+                        <button type="button" className="btn btn-success btn-sm btn-block inline"
+                                onClick={() => this.props.forward()}>
+                            <strong>create the game</strong>
+                        </button>
+                    </div>
+                </div>
+            </FancyBox>
+        );
+    }
+}
+
+PanelChooseSettings.propTypes = {
+    backward: PropTypes.func.isRequired,
+    forward: PropTypes.func.isRequired,
+    cancel: PropTypes.func.isRequired,
+    params: PropTypes.object.isRequired,
+    onUpdateParams: PropTypes.func.isRequired,
+    username: PropTypes.string.isRequired
+};
diff --git a/diplomacy/web/src/gui/wizards/gameCreation/panelList.js b/diplomacy/web/src/gui/wizards/gameCreation/panelList.js
new file mode 100644
index 0000000..0b6100c
--- /dev/null
+++ b/diplomacy/web/src/gui/wizards/gameCreation/panelList.js
@@ -0,0 +1,6 @@
+export const Panels = {
+    CHOOSE_MAP: 0,
+    CHOOSE_PLAYERS: 1,
+    CHOOSE_POWER: 2,
+    CHOOSE_SETTINGS: 3
+};
diff --git a/diplomacy/web/src/index.css b/diplomacy/web/src/index.css
index f270135..8b14a97 100644
--- a/diplomacy/web/src/index.css
+++ b/diplomacy/web/src/index.css
@@ -261,12 +261,9 @@ span.power-name {
 .fancy-box .fancy-bar {
     background-color: rgb(240, 240, 240);
     border-bottom: 1px solid silver;
-    padding-top: 10px;
-    padding-bottom: 10px;
 }
 
 .fancy-box .fancy-button {
-    text-align: right;
 }
 
 .fancy-box .fancy-content {
@@ -419,6 +416,37 @@ span.power-name {
     background-color: rgb(230, 230, 230);
 }
 
+button .unroll {
+    display: none;
+}
+
+button .roll {
+    display: inline;
+}
+
+button.collapsed .unroll {
+    display: inline;
+}
+
+button.collapsed .roll {
+    display: none;
+}
+
+.panel-choose-map .map-list {
+    position: absolute;
+    top: 0;
+    left: 0;
+    bottom: 0;
+    right: 0;
+    overflow: auto;
+    border: 1px solid gray;
+}
+
+.inline {
+    white-space: nowrap;
+}
+
+
 /** Page login. **/
 /** Page games. **/
 /** Page game. **/
diff --git a/diplomacy/web/src/maps b/diplomacy/web/src/maps
new file mode 120000
index 0000000..0aaf789
--- /dev/null
+++ b/diplomacy/web/src/maps
@@ -0,0 +1 @@
+../../maps/
\ No newline at end of file
-- 
cgit v1.2.3