From 85e5b5c1e6192a134fa69182e8c077605fdb969f Mon Sep 17 00:00:00 2001
From: Benjamin Auder <benjamin.auder@somewhere>
Date: Thu, 31 Jan 2019 19:32:09 +0100
Subject: [PATCH] Styled layout. TODO: variants page, and then index

---
 client/next_src/components/challengeList.js |  33 -----
 client/next_src/components/gameList.js      |  29 ----
 client/public/index.html                    |   6 +
 client/src/App.vue                          | 145 ++++++++++++++------
 client/src/components/ChallengeList.vue     |  36 +++++
 client/src/components/GameList.vue          |  29 ++++
 client/src/data/nbPlayers.js                |   4 +-
 client/src/main.js                          |   1 +
 client/src/stylesheets/layout.sass          |  20 ---
 client/src/translations/fr.js               |   8 +-
 client/src/views/Home.vue                   | 108 ++++++++-------
 client/src/views/Variants.vue               |  58 ++++----
 12 files changed, 269 insertions(+), 208 deletions(-)
 delete mode 100644 client/next_src/components/challengeList.js
 delete mode 100644 client/next_src/components/gameList.js
 create mode 100644 client/src/components/ChallengeList.vue
 create mode 100644 client/src/components/GameList.vue

diff --git a/client/next_src/components/challengeList.js b/client/next_src/components/challengeList.js
deleted file mode 100644
index 2c997b7e..00000000
--- a/client/next_src/components/challengeList.js
+++ /dev/null
@@ -1,33 +0,0 @@
-Vue.component("my-challenge-list", {
-	props: ["challenges"],
-	computed: {
-		showVariant: function() {
-			this.challenges.length > 0 && !!this.challenges[0].variant;
-		},
-		showNbPlayers: function() {
-			this.challenges.length > 0 && !!this.challenges[0].nbPlayers;
-		},
-	},
-	template: `
-		<table>
-			<tr>
-				<th v-if="showVariant">Variant</th>
-				<th>From</th>
-				<th>To</th>
-				<th>Cadence</th>
-				<th v-if="showNbPlayers">Number of players</th>
-			</tr>
-			<tr v-for="c in challenges" @click="$emit('click-challenge',c)">
-				<td v-if="showVariant">{{ c.variant }}</td>
-				<td>{{ c.from.name }}</td>
-				<td>
-					<span v-for="p in c.to">{{ p.name }}</span>
-				</td>
-				<td>{{ c.mainTime }} + {{ c.increment }}</td>
-				<td v-if="showNbPlayers">{{ c.nbPlayers }}</td>
-			</tr>
-		</table>
-	`,
-});
-
-// TODO: challenge format from/to ou uid/players ............
diff --git a/client/next_src/components/gameList.js b/client/next_src/components/gameList.js
deleted file mode 100644
index 9b669826..00000000
--- a/client/next_src/components/gameList.js
+++ /dev/null
@@ -1,29 +0,0 @@
-Vue.component("my-game-list", {
-	props: ["games"],
-	computed: {
-		showVariant: function() {
-			return this.games.length > 0 && !!this.games[0].vname;
-		},
-		showResult: function() {
-			return this.games.length > 0 && this.games[0].score != "*";
-		},
-	},
-	template: `
-		<table>
-			<tr>
-				<th v-if="showVariant">Variant</th>
-				<th>Players names</th>
-				<th>Cadence</th>
-				<th v-if="showResult">Result</th>
-			</tr>
-			<tr v-for="g in games" @click="$emit('show-game',g)">
-				<td v-if="showVariant">{{ g.vname }}</td>
-				<td>
-					<span v-for="p in g.players">{{ p.name }}</span>
-				</td>
-				<td>{{ g.mainTime }} + {{ g.increment }}</td>
-				<td v-if="showResult">{{ g.score }}</td>
-			</tr>
-		</table>
-	`,
-});
diff --git a/client/public/index.html b/client/public/index.html
index 6549d7a7..af506cdf 100644
--- a/client/public/index.html
+++ b/client/public/index.html
@@ -10,6 +10,12 @@
 			href="//cdnjs.cloudflare.com/ajax/libs/mini.css/3.0.0/mini-default.min.css">
 		<link rel="stylesheet"
 			href="//fonts.googleapis.com/css?family=Open+Sans:400,700">
+    <style>
+      body {
+        --fore-color: #2c3e50;
+        --back-color: #f2f2f2;
+      }
+    </style>
   </head>
   <body>
     <div id="app"></div>
diff --git a/client/src/App.vue b/client/src/App.vue
index 2b131276..dd45b343 100644
--- a/client/src/App.vue
+++ b/client/src/App.vue
@@ -8,48 +8,46 @@
   UpsertUser
   .container
     .row(v-show="$route.path == '/'")
-      // Header (on index only ?!)
-      header
-        .col-sm-12.col-md-10.col-md-offset-1.col-lg-8.col-lg-offset-2
+      .col-sm-12.col-md-10.col-md-offset-1.col-lg-8.col-lg-offset-2
+        // Header (on index only)
+        header
           img(src="./assets/images/index/unicorn.svg")
           .info-container
             p vchess.club
           img(src="./assets/images/index/wildebeest.svg")
     .row
-      // Menu (top of page):
-      // shared: Home + flags, userMenu
-      // variant: hall, problems, rules, my games + settings
-      nav
-        label.drawer-toggle(for="drawerControl")
-        input#drawerControl.drawer(type="checkbox")
-        #menuBar
-          label.drawer-close(for="drawerControl")
-          router-link(to="/")
-            // select options all variants + filter possible (as in problems)
-            | Home
-          router-link(to="/myGames")
-            | {{ st.tr["My games"] }}
-          router-link(to="/rules")
-            // Boxes OK for rules/Atomic/ ...etc
-            | {{ st.tr["Rules"] }}
-          router-link(to="/problems")
-            | {{ st.tr["Problems"] }}
-          #userMenu.clickable.right-menu(onClick="doClick('modalUser')")
-            .info-container
-              p
-                span {{ !st.user.id ? "Login" : "Update" }}
-                span.icon-user
-          #flagMenu.clickable.right-menu(onClick="doClick('modalLang')")
-          img(src="/images/flags/" + lang + ".svg")
-        #settings.clickable(onClick="doClick('modalSettings')")
-          | Settings
-          i(data-feather="settings")
+      .col-sm-12.col-md-10.col-md-offset-1.col-lg-8.col-lg-offset-2
+        // Menu (top of page):
+        // Left: home, variants, mygames, problems
+        // Right: usermenu, settings, flag
+        nav
+          label.drawer-toggle(for="drawerControl")
+          input#drawerControl.drawer(type="checkbox")
+          #menuBar
+            label.drawer-close(for="drawerControl")
+            #leftMenu
+              router-link(to="/")
+                | {{ st.tr["Home"] }}
+              router-link(to="/variants")
+                | {{ st.tr["Variants"] }}
+              router-link(to="/mygames")
+                | {{ st.tr["My games"] }}
+              router-link(to="/problems")
+                | {{ st.tr["Problems"] }}
+            #rightMenu
+              .clickable(onClick="doClick('modalUser')")
+                | {{ !st.user.id ? "Login" : "Update" }}
+              .clickable(onClick="doClick('modalSettings')")
+                | {{ st.tr["Settings"] }}
+              .clickable(onClick="doClick('modalLang')")
+                img(v-if="!!st.lang"
+                  :src="require(`@/assets/images/flags/${st.lang}.svg`)")
     .row
       router-view
     .row
-      footer
-        .col-sm-12.col-md-10.col-md-offset-1.col-lg-8.col-lg-offset-2.text-center
-          a(href="https://github.com/yagu0/vchess") Source code
+      .col-sm-12.col-md-10.col-md-offset-1.col-lg-8.col-lg-offset-2
+        footer
+          a(href="https://github.com/yagu0/vchess") {{ st.tr["Source code"] }}
           p.clickable(onClick="doClick('modalContact')")
             | {{ st.tr["Contact form"] }}
   //my-game(:game-ref="gameRef" :mode="mode" :settings="settings" @game-over="archiveGame")
@@ -84,14 +82,75 @@ export default {
   font-family: "Avenir", Helvetica, Arial, sans-serif
   -webkit-font-smoothing: antialiased
   -moz-osx-font-smoothing: grayscale
-  text-align: center
-  color: #2c3e50
 
-#nav
-  padding: 30px
-  a
-    font-weight: bold
-    color: #2c3e50
-    &.router-link-exact-active
-      color: #42b983
+.container
+  @media screen and (max-width: 767px)
+    padding: 0
+
+header
+  width: 100%
+  display: flex
+  align-items: center
+  justify-content: center
+  margin: 0 auto
+  & > img
+    width: 30px
+    height: 30px
+
+.clickable
+  cursor: pointer
+
+nav
+  width: 100%
+  padding: 0
+  & > #menuBar
+    width: 100%
+    padding: 0
+    & > #leftMenu
+      padding: 0
+      width: 50%
+      display: inline-flex
+      align-items: center
+      justify-content: flex-start
+      & > a
+        display: inline-block
+        color: #2c3e50
+        &.router-link-exact-active
+          color: #42b983
+    & > #rightMenu
+      padding: 0
+      width: 50%
+      display: inline-flex
+      align-items: center
+      justify-content: flex-end
+      & > div
+        display: inline-block
+        & > img
+          padding: 0
+          width: 30px
+          height: 30px
+
+// TODO: drawer, until 600px wide OK (seemingly)
+// After, zone where left and right just go on top of another
+// Then, on narrow screen put everything on one line
+[type="checkbox"].drawer+*
+  right: -767px
+
+footer
+  //background-color: #000033
+  font-size: 1rem
+  width: 100%
+  display: inline-flex
+  align-items: center
+  justify-content: center
+  & > a
+    display: inline-block
+    margin: 0 10px 0 0
+    &:link
+      color: #2c3e50
+    &:hover
+      text-decoration: none
+  & > p
+    display: inline-block
+    margin: 0 0 0 10px
 </style>
diff --git a/client/src/components/ChallengeList.vue b/client/src/components/ChallengeList.vue
new file mode 100644
index 00000000..a52e0cae
--- /dev/null
+++ b/client/src/components/ChallengeList.vue
@@ -0,0 +1,36 @@
+<template lang="pug">
+table
+  tr
+    th(v-if="showVariant") Variant
+    th From
+    th To
+    th Cadence
+    th(v-if="showNbPlayers") Number of players
+  tr(v-for="c in challenges" @click="$emit('click-challenge',c)")
+    td(v-if="showVariant") {{ c.variant }}
+    td {{ c.from.name }}
+    td
+      span(v-for="p in c.to") {{ p.name }}
+      td {{ c.mainTime }} + {{ c.increment }}
+      td(v-if="showNbPlayers") {{ c.nbPlayers }}
+</template>
+
+<script>
+export default {
+  name: "my-challenge-list",
+	props: ["challenges"],
+	computed: {
+		showVariant: function() {
+			this.challenges.length > 0 && !!this.challenges[0].variant;
+		},
+		showNbPlayers: function() {
+			this.challenges.length > 0 && !!this.challenges[0].nbPlayers;
+		},
+	},
+};
+// TODO: challenge format from/to ou uid/players ............
+</script>
+
+<style lang="sass">
+// TODO: affichage bizarre sur petits écrans <=767px
+</style>
diff --git a/client/src/components/GameList.vue b/client/src/components/GameList.vue
new file mode 100644
index 00000000..c7f661ca
--- /dev/null
+++ b/client/src/components/GameList.vue
@@ -0,0 +1,29 @@
+<template lang="pug">
+table
+  tr
+    th(v-if="showVariant") Variant
+    th Players names
+    th Cadence
+    th(v-if="showResult") Result
+  tr(v-for="g in games" @click="$emit('show-game',g)")
+    td(v-if="showVariant") {{ g.vname }}
+    td
+      span(v-for="p in g.players") {{ p.name }}
+    td {{ g.mainTime }} + {{ g.increment }}
+    td(v-if="showResult") {{ g.score }}
+</template>
+
+<script>
+export default {
+  name: "my-game-list",
+	props: ["games"],
+	computed: {
+		showVariant: function() {
+			return this.games.length > 0 && !!this.games[0].vname;
+		},
+		showResult: function() {
+			return this.games.length > 0 && this.games[0].score != "*";
+		},
+	},
+};
+</script>
diff --git a/client/src/data/nbPlayers.js b/client/src/data/nbPlayers.js
index 8c0cc864..2f58d168 100644
--- a/client/src/data/nbPlayers.js
+++ b/client/src/data/nbPlayers.js
@@ -1,4 +1,4 @@
-const NbPlayers =
+export const NbPlayers =
 {
 	"Alice": [2,3,4],
 	"Antiking": [2,3,4],
@@ -19,5 +19,3 @@ const NbPlayers =
 	"Wildebeest": [2],
 	"Zen": [2,3,4],
 };
-
-try { module.exports = NbPlayers; } catch (e) { } //for server
diff --git a/client/src/main.js b/client/src/main.js
index 97bad754..703ff742 100644
--- a/client/src/main.js
+++ b/client/src/main.js
@@ -38,6 +38,7 @@ new Vue({
 //  mounted: function() {
 //    feather.replace();
 //  },
+  // "mounted" and not "created", because modalWelcome must be filled
   mounted: function() {
     store.initialize();
   },
diff --git a/client/src/stylesheets/layout.sass b/client/src/stylesheets/layout.sass
index 5639c7b2..b7446750 100644
--- a/client/src/stylesheets/layout.sass
+++ b/client/src/stylesheets/layout.sass
@@ -55,26 +55,6 @@ a.right-menu
   color: blue
   display: none
 
-footer
-  height: 77px
-  background-color: #000033
-  div
-    line-height: 77px
-    a
-      margin: 0 10px 0 0
-      display: inline-block
-      &:visited, &:link
-        color: white
-    p
-      margin: 0 0 0 10px
-      display: inline-block
-      color: white
-      text-decoration: underline
-  @media screen and (max-width: 767px)
-    height: 43px
-    div
-      line-height: 43px
-
 a
   text-decoration: underline
 
diff --git a/client/src/translations/fr.js b/client/src/translations/fr.js
index ee65449d..4d242c2c 100644
--- a/client/src/translations/fr.js
+++ b/client/src/translations/fr.js
@@ -1,5 +1,12 @@
 export const translations =
 {
+  "Home": "Accueil",
+  "Variants": "Variantes",
+  "My games": "Mes parties",
+  "Problems": "Problèmes",
+  "Contact form": "Formulaire de contact",
+  "Source code": "Code source",
+
   "Language": "Langue",
 
   // Index page:
@@ -31,7 +38,6 @@ export const translations =
   "Waiting for opponent...": "En attente d'un adversaire...",
   "Rules": "Règles",
   "Play": "Jouer",
-  "Problems": "Problèmes",
   "White win": "Les blancs gagnent",
   "Black win": "Les noirs gagnent",
   "Draw": "Match nul",
diff --git a/client/src/views/Home.vue b/client/src/views/Home.vue
index c2c5b442..f0f6a5c7 100644
--- a/client/src/views/Home.vue
+++ b/client/src/views/Home.vue
@@ -7,7 +7,7 @@ div
       fieldset
         label(for="selectVariant") {{ st.tr["Variant"] }}
         select#selectVariant(v-model="newgameInfo.vid")
-          option(v-for="v in variants" :value="v.id") {{ v.name }}
+          option(v-for="v in st.variants" :value="v.id") {{ v.name }}
       fieldset
         label(for="selectNbPlayers") {{ st.tr["Number of players"] }}
         select#selectNbPlayers(v-model="newgameInfo.nbPlayers")
@@ -31,14 +31,14 @@ div
             v-model="newgameInfo.players[2].name")
       fieldset
         label(for="inputFen")
-          {{ st.tr["FEN (ignored if players fields are blank)"] }}
+          | {{ st.tr["FEN (ignored if players fields are blank)"] }}
         input#inputFen(type="text" v-model="newgameInfo.fen")
       button(@click="newGame") Launch game
       p TODO: cadence, adversaire (pre-filled if click on name)
       p cadence 2m+12s ou 7d+1d (m,s ou d,d) --> main, increment
       p Note: leave FEN blank for random; FEN only for targeted challenge
   div
-    my-challenge-list(:challenges="challenges" @click-challenge="clickChallenge")
+    ChallengeList(:challenges="challenges" @click-challenge="clickChallenge")
     div(style="border:1px solid black")
       h3 Online players
       div(v-for="p in players" @click="challenge(p)") {{ p.name }}
@@ -47,9 +47,9 @@ div
     .button-group
       button(@click="gdisplay='live'") Live games
       button(@click="gdisplay='corr'") Correspondance games
-    my-game-list(v-show="gdisplay=='live'" :games="liveGames"
+    GameList(v-show="gdisplay=='live'" :games="liveGames"
       @show-game="showGame")
-    my-game-list(v-show="gdisplay=='corr'" :games="corrGames"
+    GameList(v-show="gdisplay=='corr'" :games="corrGames"
       @show-game="showGame")
 </template>
 
@@ -65,6 +65,7 @@ fin de partie corr: supprimer partie du serveur au bout de 7 jours (arbitraire)
 // TODO: au moins l'échange des coups en P2P ? et game chat ?
 // TODO: objet game, objet challenge ? et player ?
 import { store } from "@/store";
+import { NbPlayers } from "@/data/nbPlayers";
 import GameList from "@/components/GameList.vue";
 import ChallengeList from "@/components/ChallengeList.vue";
 export default {
@@ -92,46 +93,48 @@ export default {
 			},
 		};
 	},
-	created: function() {
-		// TODO: ask server for current corr games (all but mines: names, ID, time control)
-		const socketMessageListener = msg => {
-			const data = JSON.parse(msg.data);
-			switch (data.code)
-			{
-				case "newgame":
-					// TODO: new game just started: data contain all informations
-					// (id, players, time control, fenStart ...)
-					break;
-				// TODO: also receive live games summaries (update)
-				// (just players names, time control, and ID + player ID)
-				case "acceptchallenge":
-					// oppid: opponent socket ID (or DB id if registered)
-					if (true) //TODO: if challenge is full
-						this.newGame(data.challenge, data.user); //user.id et user.name
-					break;
-				case "withdrawchallenge":
-					// TODO
-					break;
-				case "cancelchallenge":
-					// TODO
-					break;
-				// TODO: distinguish these (dis)connect events from their analogs in game.js
-				case "connect":
-					this.players.push({name:data.name, id:data.uid});
-					break;
-				case "disconnect":
-					const pIdx = this.players.findIndex(p => p.id == data.uid);
-					this.players.splice(pIdx);
-					break;
-			}
-		};
-		const socketCloseListener = () => {
-			this.st.conn.addEventListener('message', socketMessageListener);
-			this.st.conn.addEventListener('close', socketCloseListener);
-		};
-		this.st.conn.onmessage = socketMessageListener;
-		this.st.conn.onclose = socketCloseListener;
-	},
+  watch: {
+    "st.conn": function() {
+      // TODO: ask server for current corr games (all but mines: names, ID, time control)
+      const socketMessageListener = msg => {
+        const data = JSON.parse(msg.data);
+        switch (data.code)
+        {
+          case "newgame":
+            // TODO: new game just started: data contain all informations
+            // (id, players, time control, fenStart ...)
+            break;
+          // TODO: also receive live games summaries (update)
+          // (just players names, time control, and ID + player ID)
+          case "acceptchallenge":
+            // oppid: opponent socket ID (or DB id if registered)
+            if (true) //TODO: if challenge is full
+              this.newGame(data.challenge, data.user); //user.id et user.name
+            break;
+          case "withdrawchallenge":
+            // TODO
+            break;
+          case "cancelchallenge":
+            // TODO
+            break;
+          // TODO: distinguish these (dis)connect events from their analogs in game.js
+          case "connect":
+            this.players.push({name:data.name, id:data.uid});
+            break;
+          case "disconnect":
+            const pIdx = this.players.findIndex(p => p.id == data.uid);
+            this.players.splice(pIdx);
+            break;
+        }
+      };
+      const socketCloseListener = () => {
+        this.st.conn.addEventListener('message', socketMessageListener);
+        this.st.conn.addEventListener('close', socketCloseListener);
+      };
+      this.st.conn.onmessage = socketMessageListener;
+      this.st.conn.onclose = socketCloseListener;
+    },
+  },
 	methods: {
 		showGame: function(game) {
       // NOTE: if we are an observer, the game will be found in main games list
@@ -207,8 +210,8 @@ export default {
 								uid: user.id,
 								added: Date.now(),
 								vname: vname,
-							},
-						this.challenges.push(response.challenge);
+							});
+						this.challenges.push(chall);
 					}
 				);
         // TODO: else, if live game: send infos (socket), and...
@@ -233,10 +236,15 @@ export default {
 		possibleNbplayers: function(nbp) {
 			if (this.newgameInfo.vid == 0)
 				return false;
+      const variants = this.st.variants;
 			const idxInVariants =
-				variantArray.findIndex(v => v.id == this.newgameInfo.vid);
-			return NbPlayers[variantArray[idxInVariants].name].includes(nbp);
+				variants.findIndex(v => v.id == this.newgameInfo.vid);
+			return NbPlayers[variants[idxInVariants].name].includes(nbp);
 		},
 	},
-});
+};
 </script>
+
+<style lang="sass">
+// TODO
+</style>
diff --git a/client/src/views/Variants.vue b/client/src/views/Variants.vue
index 03903572..f5214296 100644
--- a/client/src/views/Variants.vue
+++ b/client/src/views/Variants.vue
@@ -1,15 +1,15 @@
 <template lang="pug">
 div
-	.col-sm-12.col-md-10.col-md-offset-1.col-lg-8.col-lg-offset-2
-		label(for="prefixFilter") Type first letters...
-		input#prefixFilter(v-model="curPrefix")
-	.variant.col-sm-12.col-md-5.col-lg-4(
-		v-for="(v,idx) in filteredVariants"
-		:class="{'col-md-offset-1': idx%2==0, 'col-lg-offset-2': idx%2==0}"
-	)
-		router-link(:to="getLink(v.name)")
-			h4.boxtitle.text-center {{ v.name }}
-			p.description.text-center {{ st.tr(v.desc) }}
+  .col-sm-12.col-md-10.col-md-offset-1.col-lg-8.col-lg-offset-2
+    label(for="prefixFilter") Type first letters...
+    input#prefixFilter(v-model="curPrefix")
+  .variant.col-sm-12.col-md-5.col-lg-4(
+    v-for="(v,idx) in filteredVariants"
+    :class="{'col-md-offset-1': idx%2==0, 'col-lg-offset-2': idx%2==0}"
+  )
+    router-link(:to="getLink(v.name)")
+      h4.boxtitle.text-center {{ v.name }}
+      p.description.text-center {{ st.tr[v.desc] }}
 </template>
 
 <script>
@@ -18,29 +18,29 @@ export default {
   name: "variants",
   data: function() {
     return {
-		  curPrefix: "",
+      curPrefix: "",
       st: store.state,
     };
-	},
-	computed: {
-		filteredVariants: function () {
-			const capitalizedPrefix = this.curPrefix.replace(/^\w/, c => c.toUpperCase());
-			const variants = this.st.variants
-			.filter( v => {
-				return v.name.startsWith(capitalizedPrefix);
-			})
-			.map( v => {
-				return {
-					name: v.name,
-					desc: v.description,
-				};
-			})
+  },
+  computed: {
+    filteredVariants: function () {
+      const capitalizedPrefix = this.curPrefix.replace(/^\w/, c => c.toUpperCase());
+      const variants = this.st.variants
+      .filter( v => {
+        return v.name.startsWith(capitalizedPrefix);
+      })
+      .map( v => {
+        return {
+          name: v.name,
+          desc: v.description,
+        };
+      })
       .sort((a,b) => {
-				return a.name.localeCompare(b.name);
-			});
+        return a.name.localeCompare(b.name);
+      });
       return variants;
-		},
-	},
+    },
+  },
   methods: {
     getLink: function(vname) {
       return "/variants/" + vname;
-- 
2.44.0