Browse Source

started to clean up code.

Tomi Cvetic 7 years ago
parent
commit
805e86980c

+ 3 - 2
package.json

@@ -5,14 +5,15 @@
   "dependencies": {
     "blob": "^0.0.4",
     "file-saver": "^1.3.3",
-    "html-inline": "^1.2.0",
     "moment": "^2.18.1",
     "react": "^15.5.4",
     "react-dom": "^15.5.4",
     "xlsx": "^0.10.4"
   },
   "devDependencies": {
-    "react-scripts": "^1.0.7"
+    "html-inline": "^1.2.0",
+    "react-scripts": "^1.0.7",
+    "react-bootstrap": "^0.31.0"
   },
   "scripts": {
     "start": "react-scripts start",

+ 4 - 2
public/index.html

@@ -1,5 +1,5 @@
 <!doctype html>
-<html lang="en">
+<html lang="de-ch">
   <head>
     <meta charset="utf-8">
     <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
@@ -19,7 +19,9 @@
       work correctly both with client-side routing and a non-root public URL.
       Learn how to configure a non-root public URL by running `npm run build`.
     -->
-    <title>React App</title>
+    <!-- Latest compiled and minified CSS -->
+    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/latest/css/bootstrap.min.css">
+    <title>SZTM Excel</title>
   </head>
   <body>
     <noscript>

+ 138 - 106
src/App.js

@@ -1,14 +1,46 @@
 import React from 'react'           // React to manage the GUI
-import XLSX from 'xlsx'             // xlsx to read and write Excel XLSX Workbooks
 import Player from './player'       // Everything that has to do with players
 import Match from './match'         // Everything that has to do with matches
 import Excel from './excel'         // Helper files to create Excel files
+import { date2s, time2s } from './helpers'
+
+/**
+ * General Application Design
+ *
+ * 4 Components:
+ * - PlayerList (representing the PlayerList Excel file)
+ * - Calendar (representing the Calendar Excel file)
+ * - MatchList (representing the Spielliste Excel file)
+ * - PaymentList (representing the Zahlliste Excel file)
+ *
+ * PlayerList
+ * - Shows the relevant information from the file
+ * - Shows calculated information from combination with Calendar
+ * - Points out potential problems
+ * - Allows access to player information
+ *
+ * Calendar
+ * - Shows the relevant information from the file
+ * - Shows calculated information from combination with PlayerList
+ * - Points out potential problems
+ * - Allows access to match information
+ *
+ * MatchList
+ * - Shows the calculated match lists
+ * - Points out problems
+ *
+ * PaymentList
+ * - Shows the calculated payment lists
+ * - Points out problems
+ *
+ *
+ */
 
 const FILTER_OFF = 'Alle'
 
 /** Main application */
 class App extends React.Component {
-  /** 
+  /**
    * Constructor
    * Defines the state and binds all 'this' in all functions to the object.
    */
@@ -22,11 +54,6 @@ class App extends React.Component {
       worksheets: {
         'PlayerList': null,
         'Calendar': null
-      },
-      filters: {
-        date: null,
-        place: null,
-        category: null
       }
     }
 
@@ -35,11 +62,10 @@ class App extends React.Component {
     this.handleCalendar = this.handleCalendar.bind(this)
     this.generatePlayerList = this.generatePlayerList.bind(this)
     this.generateCalendar = this.generateCalendar.bind(this)
-    this.filterData = this.filterData.bind(this)
+    this.filterMatches = this.filterMatches.bind(this)
+    this.filterPlayers = this.filterPlayers.bind(this)
     this.generateSchedule = this.generateSchedule.bind(this)
     this.generatePayTable = this.generatePayTable.bind(this)
-
-    this.unpaid = 'off'
   }
 
   calculateMatchFilters () {
@@ -47,8 +73,9 @@ class App extends React.Component {
     const places = []
     const categories = []
     this.state.match.matches.forEach((item) => {
-      if (!!item.DatumString & !dates.hasOwnProperty(item.DatumString)) {
-        dates[item.DatumString] = item.Datum
+      const dateString = date2s(item.Datum)
+      if (!!item.Datum & !dates.hasOwnProperty(dateString)) {
+        dates[dateString] = item.Datum
       }
       if (!!item.Ort & !places.includes(item.Ort)) {
         places.push(item.Ort)
@@ -61,29 +88,25 @@ class App extends React.Component {
     this.setState({ match })
   }
 
+  calculatePlayerFilters () {
+    const categories = []
+    this.state.player.players.forEach((item) => {
+      if (!!item.Konkurrenz & !categories.includes(item.Konkurrenz)) {
+        categories.push(item.Ort)
+      }
+    })
+    const player = { ...this.state.player, categories }
+    this.setState({ player })
+  }
+
   handlePlayerList (event) {
     const file = this.playerList.files[0]
-    this.readWorkbook(file, this.generatePlayerList)
+    Excel.readWorkbook(file, this.generatePlayerList)
   }
 
   handleCalendar (event) {
     const file = this.calendar.files[0]
-    this.readWorkbook(file, this.generateCalendar)
-  }
-
-  readWorkbook (file, callback) {
-    const reader = new FileReader()
-    reader.onload = (e) => {
-      const data = e.target.result
-      const workbook = XLSX.read(data, {type: 'binary', cellDates: true})
-      console.log('Workbook after read:', workbook)
-      if (workbook.SheetNames.length !== 1) {
-        throw Error(`Expected only one worksheet in the file, but found ${workbook.SheetNames.length}.`)
-      }
-      const worksheet = XLSX.utils.sheet_to_json(workbook.Sheets[workbook.SheetNames[0]], { header: 1, raw: true })
-      callback(worksheet)
-    }
-    reader.readAsBinaryString(file)
+    Excel.readWorkbook(file, this.generateCalendar)
   }
 
   generatePlayerList (worksheet) {
@@ -99,7 +122,8 @@ class App extends React.Component {
     const player = { ...this.state.player }
     player.players = players
     this.setState({ player })
-    this.filterData()
+    this.calculatePayDate()
+    this.filterPlayers()
     console.log('State after generating player list:', this.state)
   }
 
@@ -117,18 +141,18 @@ class App extends React.Component {
     match.matches = matches
     this.setState({ match })
     this.calculateMatchFilters()
-    this.filterData()
+    this.calculatePayDate()
+    this.filterMatches()
     console.log('State after generating calendar:', this.state)
   }
 
-  filterData () {
+  filterMatches () {
     const filters = {
-      date: this.date.value !== FILTER_OFF ? this.date.value : null,
-      place: this.place.value !== FILTER_OFF ? this.place.value : null,
-      category: this.category.value !== FILTER_OFF ? this.category.value : null,
+      date: this.matchDate.value !== FILTER_OFF ? this.matchDate.value : null,
+      place: this.matchPlace.value !== FILTER_OFF ? this.matchPlace.value : null,
+      category: this.matchCategory.value !== FILTER_OFF ? this.matchCategory.value : null
     }
     console.log('New filter settings:', filters)
-    this.setState({ filters })
 
     const match = { ...this.state.match }
     match.filtered = match.matches.filter((match) => {
@@ -142,7 +166,20 @@ class App extends React.Component {
     })
     this.setState({ match })
 
-    const player = { ...this.state.player }
+    const player = { ...this.state.player, filters }
+    player.filtered = player.players
+    this.setState({ player })
+  }
+
+  filterPlayers () {
+    const filters = {
+      junior: this.playerJunior.checked,
+      paid: this.playerPaid.checked,
+      category: this.playerCategory.value !== FILTER_OFF ? this.playerCategory.value : null
+    }
+    console.log('New filter settings:', filters)
+
+    const player = { ...this.state.player, filters }
     player.filtered = player.players
     this.setState({ player })
   }
@@ -161,7 +198,7 @@ class App extends React.Component {
       placeArray = placeArray.concat([FILTER_OFF])
     }
     const date = Object.keys(this.state.match.dates).find((key) =>
-      String(this.state.match.dates[key]) === this.date.value
+      String(this.state.match.dates[key]) === this.matchDate.value
     )
 
     placeArray.forEach((place) => {
@@ -171,7 +208,7 @@ class App extends React.Component {
         ['Ort', 'Zeit', 'Kategorie', 'Spieler 1', '', 'Spieler 2', '', '1. Satz', '2. Satz', '3. Satz', 'WO Grund']
       ]
       let matchListPerPlace = this.state.match.filtered.filter((match) => (match.Ort === place | place === FILTER_OFF)).map((match) =>
-        [match.Ort, match.ZeitString, match.Konkurrenz, match.Spieler1, match.Spieler1Klassierung, match.Spieler2, match.Spieler2Klassierung]
+        [match.Ort, time2s(match.Datum), match.Konkurrenz, match.Spieler1, match.Spieler1Klassierung, match.Spieler2, match.Spieler2Klassierung]
       )
       console.log('Generated match list per place:', matchListPerPlace)
       worksheets[place] = Excel.SheetFromArray(header.concat(matchListPerPlace))
@@ -182,6 +219,29 @@ class App extends React.Component {
     Excel.saveAs(matchlist, 'Spielliste.xlsx')
   }
 
+  calculatePayDate () {
+    if ((this.state.player.players.length === 0) | (this.state.match.matches.length === 0)) {
+      return
+    }
+
+    this.state.match.matches.forEach((match) => {
+      [match.Spieler1, match.Spieler2].forEach((matchPlayer) => {
+        if (matchPlayer) {
+          let foundPlayer = this.state.player.players.find((player) =>
+            (player.name === matchPlayer) & (player.Konkurrenz === match.Konkurrenz)
+          )
+          if (!foundPlayer) {
+            console.log('Debug payerlist:', foundPlayer, match)
+            throw Error('Match player not found in player list. This is an error!')
+          }
+          if (!foundPlayer.BezahltAm) {
+            foundPlayer.BezahltAm = match.Datum
+          }
+        }
+      })
+    })
+  }
+
   generatePayTable (event) {
     event.preventDefault()
 
@@ -191,40 +251,12 @@ class App extends React.Component {
 
     const worksheets = {}
 
-    const payerList = {}
-    this.state.match.matches.forEach((match) => {
-      const id1 = `${match.Konkurrenz} ${match.Spieler1}`
-      if (!!match.Spieler1 & !payerList.hasOwnProperty(id1)) {
-        let foundPlayer = this.state.player.players.find((player) =>
-          (player.name() === match.Spieler1) & (player.Konkurrenz === match.Konkurrenz)
-        )
-        if (!foundPlayer) {
-          console.log('Debug payerlist:', foundPlayer, match)
-          throw Error('Player 1 not found. This is an error!')
-        }
-        payerList[id1] = { ...match, Bezahlt: foundPlayer.Bezahlt }
-      }
-
-      const id2 = `${match.Konkurrenz} ${match.Spieler2}`
-      if (!!match.Spieler2 & !payerList.hasOwnProperty(id2)) {
-        let foundPlayer = this.state.player.players.find((player) =>
-          (player.name() === match.Spieler2) & (player.Konkurrenz === match.Konkurrenz)
-        )
-        if (!foundPlayer) {
-          console.log('Debug payerlist:', foundPlayer, match)
-          throw Error('Player 2 not found. This is an error!')
-        }
-        payerList[id2] = { ...match, Bezahlt: foundPlayer.Bezahlt }
-      }
-    })
-    console.log('Generated payerList', payerList)
-
     let placeArray = this.state.match.places
     if (placeArray.length > 1) {
       placeArray = placeArray.concat([FILTER_OFF])
     }
     const date = Object.keys(this.state.match.dates).find((key) =>
-      String(this.state.match.dates[key]) === this.date.value
+      String(this.state.match.dates[key]) === this.matchDate.value
     )
 
     placeArray.forEach((place) => {
@@ -236,37 +268,27 @@ class App extends React.Component {
 
       let payListPerPlace = []
       this.state.match.filtered.forEach((match) => {
-        if (!!match.Spieler1 & (match.Ort === place | FILTER_OFF === place)) {
-          const id1 = `${match.Konkurrenz} ${match.Spieler1}`
-          const player = payerList[id1]
-          let paid = null
-          if (player.Datum < this.date.value) {
-            paid = player.DatumString
-          }
-          if (player.Bezahlt) {
-            paid = 'OK'
-          }
-          payListPerPlace.push([ paid, match.Konkurrenz, match.ZeitString, match.Spieler1 ])
-        }
-        if (!!match.Spieler2 & (match.Ort === place | FILTER_OFF === place)) {
-          const id2 = `${match.Konkurrenz} ${match.Spieler2}`
-          const player = payerList[id2]
-          let paid = null
-          if (player.Datum < this.date.value) {
-            paid = player.Datum
-          }
-          if (player.Bezahlt) {
-            paid = 'OK'
+        [match.Spieler1, match.Spieler2].forEach((matchPlayer) => {
+          if (!!matchPlayer & (match.Ort === place | FILTER_OFF === place)) {
+            const player = this.state.player.players.find((player) =>
+              (player.Konkurrenz === match.Konkurrenz) & (player.name === matchPlayer)
+            )
+            let paid = null
+            if (player.BezahltAm < this.matchDate.value) {
+              paid = date2s(player.BezahltAm)
+            }
+            if (player.Bezahlt) {
+              paid = 'OK'
+            }
+            payListPerPlace.push([ paid, match.Konkurrenz, time2s(match.Datum), matchPlayer ])
           }
-          payListPerPlace.push([ paid, match.Konkurrenz, match.ZeitString, match.Spieler2 ])
-        }
+        })
+        console.log('Generated pay list per place:', payListPerPlace)
+        worksheets[place] = Excel.SheetFromArray(header.concat(payListPerPlace))
+        paylist.SheetNames.push(place)
+        paylist.Sheets[place] = worksheets[place]
       })
-      console.log('Generated pay list per place:', payListPerPlace)
-      worksheets[place] = Excel.SheetFromArray(header.concat(payListPerPlace))
-      paylist.SheetNames.push(place)
-      paylist.Sheets[place] = worksheets[place]
     })
-
     Excel.saveAs(paylist, 'Zahlliste.xlsx')
   }
 
@@ -276,38 +298,48 @@ class App extends React.Component {
 
     const places = this.state.match.places
     const dates = this.state.match.dates
-    const categories = this.state.match.categories
- 
+    const matchCategories = this.state.match.categories
+    const playerCategories = this.state.player.categories
+
     return (
-      <div className='App'>
+      <div className='container'>
         <form>
           <label htmlFor='PlayerList'>Swisstennis PlayerList.xls</label>
           <input type='file' id='PlayerList' ref={(input) => { this.playerList = input }} accept='.xls' placeholder='PlayerList File' onChange={this.handlePlayerList} />
           <label htmlFor='Calendar'>Swisstennis Calendar.xls</label>
           <input type='file' id='Calendar' ref={(input) => { this.calendar = input }} accept='.xls' placeholder='Calendar File' onChange={this.handleCalendar} />
           <label htmlFor='Date'>Datum</label>
-          <select id='Date' ref={(input) => { this.date = input }} onChange={this.filterData}>
+          <select id='Date' ref={(input) => { this.matchDate = input }} onChange={this.filterMatches}>
             <option>{FILTER_OFF}</option>
             {Object.keys(dates).map((key) => (
               <option key={key} value={dates[key]}>{key}</option>
             ))}
           </select>
           <label htmlFor='Place'>Ort</label>
-          <select id='Place' ref={(input) => { this.place = input }} onChange={this.filterData}>
+          <select id='Place' ref={(input) => { this.matchPlace = input }} onChange={this.filterMatches}>
             <option>{FILTER_OFF}</option>
             {places.map((place, key) => (
               <option key={key}>{place}</option>
             ))}
           </select>
-          <label htmlFor='Category'>Konkurrenz</label>
-          <select id='Category' ref={(input) => { this.category = input }} onChange={this.filterData}>
+          <label htmlFor='MatchCategory'>Match Konkurrenz</label>
+          <select id='MatchCategory' ref={(input) => { this.matchCategory = input }} onChange={this.filterMatches}>
+            <option>{FILTER_OFF}</option>
+            {matchCategories.map((category, key) => (
+              <option key={key}>{category}</option>
+            ))}
+          </select>
+          <label htmlFor='Junior'>Junior</label>
+          <input id='Junior' ref={(input) => { this.playerJunior = input }} type='checkbox' onChange={this.filterPlayers} />
+          <label htmlFor='Paid'>Bezahlt</label>
+          <input id='Paid' ref={(input) => { this.playerPaid = input }} type='checkbox' onChange={this.filterPlayers} />
+          <label htmlFor='PlayerCategory'>Spieler Konkurrenz</label>
+          <select id='PlayerCategory' ref={(input) => { this.playerCategory = input }} onChange={this.filterPlayers}>
             <option>{FILTER_OFF}</option>
-            {categories.map((category, key) => (
+            {playerCategories.map((category, key) => (
               <option key={key}>{category}</option>
             ))}
           </select>
-          <label htmlFor='Unpaid'>Nur unbezahlte</label>
-          <input type='checkbox' id='Unpaid' ref={(input) => { this.unpaid = input }} onChange={this.filterDate} />
           <button onClick={this.generateSchedule} disabled={!this.state.match.filtered.length}>Spielliste generieren</button>
           <button onClick={this.generatePayTable} disabled={(!this.state.match.filtered.length | !this.state.player.filtered.length)}>Zahlliste generieren</button>
         </form>

+ 16 - 1
src/excel/index.js

@@ -54,4 +54,19 @@ function saveAs (workbook, filename) {
   FileSaver.saveAs(new Blob([s2ab(wbout)], {type: ''}), filename)
 }
 
-export default { Workbook, SheetFromArray, saveAs }
+function readWorkbook (file, callback) {
+  const reader = new FileReader()
+  reader.onload = (e) => {
+    const data = e.target.result
+    const workbook = XLSX.read(data, {type: 'binary', cellDates: true})
+    console.log('Workbook after read:', workbook)
+    if (workbook.SheetNames.length !== 1) {
+      throw Error(`Expected only one worksheet in the file, but found ${workbook.SheetNames.length}.`)
+    }
+    const worksheet = XLSX.utils.sheet_to_json(workbook.Sheets[workbook.SheetNames[0]], { header: 1, raw: true })
+    callback(worksheet)
+  }
+  reader.readAsBinaryString(file)
+}
+
+export default { Workbook, SheetFromArray, saveAs, readWorkbook }

+ 23 - 0
src/helpers.js

@@ -0,0 +1,23 @@
+import moment from 'moment'
+
+function date2s (date) {
+  return moment(date).format('DD.MM.')
+}
+
+function time2s (date) {
+  return moment(date).format('HH:mm')
+}
+
+function datetime2s (date) {
+  return moment(date).format('DD.MM. HH:mm')
+}
+
+function sortTable (array, columns) {
+  function compare (item1, item2) {
+    if (item1 instanceof Date) {
+
+    }
+  }
+}
+
+export { date2s, time2s, datetime2s }

+ 2 - 0
src/index.js

@@ -1,5 +1,7 @@
+import Dotenv from 'dotenv'
 import React from 'react'
 import { render } from 'react-dom'
 import App from './App'
 
+Dotenv.config()
 render(<App />, document.getElementById('root'))

+ 9 - 8
src/match/components/match-disp.js

@@ -1,17 +1,18 @@
 import React from 'react'
+import { date2s, time2s } from '../../helpers.js'
 
 class MatchDisp extends React.Component {
   render () {
     const match = this.props.match
     return (
-      <li>
-        {match.Ort || <strong>Kein Platz zugeteilt</strong>},
-        {match.DatumString || <strong>Kein Datum zugeteilt</strong>},
-        {match.ZeitString || <strong>Keine Zeit zugeteilt</strong>},
-        {match.Konkurrenz},
-        {match.Spieler1},
-        {match.Spieler2}
-      </li>
+      <tr>
+        <td>{match.Ort || <strong>Kein Platz zugeteilt</strong>}</td>
+        <td>{match.Datum ? date2s(match.Datum) : <strong>Kein Datum zugeteilt</strong>}</td>
+        <td>{match.Datum ? time2s(match.Datum) : <strong>Keine Zeit zugeteilt</strong>}</td>
+        <td>{match.Konkurrenz}</td>
+        <td>{match.Spieler1}</td>
+        <td>{match.Spieler2}</td>
+      </tr>
     )
   }
 }

+ 7 - 6
src/match/components/match-list.js

@@ -2,7 +2,6 @@ import React from 'react'
 import MatchDisp from './match-disp'
 
 class MatchList extends React.Component {
-
   render () {
     const matches = this.props.match.matches
     const filtered = this.props.match.filtered
@@ -10,11 +9,13 @@ class MatchList extends React.Component {
     return (
       <div>
         <h2>Matches ({filtered.length}/{matches.length})</h2>
-        <ul>
-          {filtered.map((match, key) => (
-            <MatchDisp key={key} match={match} />
-          ))}
-        </ul>
+        <table>
+          <tbody>
+            {filtered.map((match, key) =>
+              <MatchDisp key={key} match={match} />
+            )}
+          </tbody>
+        </table>
       </div>
     )
   }

+ 2 - 4
src/match/index.js

@@ -1,5 +1,4 @@
 import components from './components'
-import moment from 'moment'
 
 function normalize (item, type) {
   return item ? String(item).replace(/\s+/g, ' ').trim() : null
@@ -15,8 +14,6 @@ class MatchClass {
   constructor (data) {
     this.Ort = normalize(data[0])
     this.Datum = data[1]
-    this.DatumString = data[1] ? moment(data[1]).format('DD.MM.') : null
-    this.ZeitString = data[1] ? moment(data[1]).format('HH:mm') : null
     this.Konkurrenz = normalize(data[3])
     this.Spieler1 = normalize(data[4])
     this.Spieler1Klassierung = normalize(data[5])
@@ -34,7 +31,8 @@ const state = {
   filtered: [],
   places: [],
   dates: [],
-  categories: []
+  categories: [],
+  filters: {}
 }
 
 export default { MatchClass, components, state }

+ 12 - 3
src/player/components/player-disp.js

@@ -1,12 +1,21 @@
 import React from 'react'
+import { date2s } from '../../helpers'
 
 class PlayerDisp extends React.Component {
   render () {
     const player = this.props.player
     return (
-      <li>
-        <i>{player.Konkurrenz}</i> <b>{player.Name}</b> {player.Vorname}
-      </li>
+      <tr>
+        <td><i>{player.Konkurrenz}</i></td>
+        <td><b>{player.Name}</b></td>
+        <td>{player.Vorname}</td>
+        <td><b>{player.NameDP}</b></td>
+        <td>{player.VornameDP}</td>
+        <td>{player.Bezahlt ? 'Ja' : 'Nein'}</td>
+        <td>{player.BezahltAm ? date2s(player.BezahltAm) : 'Unbekannt'}</td>
+        <td>{player.isJunior ? 'Junior' : ''}</td>
+        <td>{player.isJuniorDP ? 'DP Junior' : ''}</td>
+      </tr>
     )
   }
 }

+ 13 - 0
src/player/components/player-filters.js

@@ -0,0 +1,13 @@
+import React from 'react'
+import { date2s } from '../../helpers'
+
+class PlayerFilters extends React.Component {
+  render () {
+    const filters = this.props.filters
+    return (
+      <form />
+    )
+  }
+}
+
+export default PlayerFilters

+ 20 - 6
src/player/components/player-list.js

@@ -2,18 +2,32 @@ import React from 'react'
 import PlayerDisp from './player-disp'
 
 class PlayerList extends React.Component {
-
   render () {
     const filtered = this.props.player.filtered
 
     return (
       <div>
         <h2>Spielerliste ({filtered.length})</h2>
-        <ul>
-          {filtered.map((player, key) => (
-            <PlayerDisp key={key} player={player} />
-          ))}
-        </ul>
+        <table>
+          <thead>
+            <tr>
+              <th><i>Konkurrenz</i></th>
+              <th><b>Name</b></th>
+              <th>Vorname</th>
+              <th><b>Name DP</b></th>
+              <th>Vorname DP</th>
+              <th>Bezahlt</th>
+              <th>Bezahlt Am</th>
+              <th>Junior</th>
+              <th>DP Junior</th>
+            </tr>
+          </thead>
+          <tbody>
+            {filtered.map((player, key) =>
+              <PlayerDisp key={key} player={player} />
+            )}
+          </tbody>
+        </table>
       </div>
     )
   }

+ 8 - 21
src/player/index.js

@@ -7,7 +7,8 @@ function normalize (item, type) {
 const state = {
   players: [],
   filtered: [],
-  categories: []
+  categories: [],
+  filters: {}
 }
 
 /** Class representing a player */
@@ -34,26 +35,12 @@ class Player {
     this.KlassierungDP = normalize(data[27])
     this.Bestaetigt = data[29]
     this.Bezahlt = data[31]
-  }
-
-  isDoubles () {
-    return this.Konkurrenz.match(/DM.*|[MW]D.*/)
-  }
-
-  isJunior () {
-    return this.Geburtsdatum >= new Date(new Date().getFullYear())
-  }
-
-  isJuniorDP () {
-    return this.GeburtsdatumDP >= new Date(new Date().getFullYear())
-  }
-
-  name () {
-    if (this.isDoubles()) {
-      return `${this.Name} ${this.Vorname} / ${this.NameDP} ${this.VornameDP}`
-    } else {
-      return `${this.Name} ${this.Vorname}`
-    }
+    this.BezahltAm = null
+    this.Matches = []
+    this.isDoubles = this.Konkurrenz.match(/DM.*|[MW]D.*/)
+    this.isJunior = (this.Geburtsdatum) ? this.Geburtsdatum.getTime() >= (new Date((new Date()).getFullYear() - 18, 11, 31, 23, 59, 59, 999)).getTime() : false
+    this.isJuniorDP = (this.isDoubles & !!this.GeburtsdatumDP) ? this.GeburtsdatumDP.getTime() >= (new Date((new Date()).getFullYear() - 18, 11, 31, 23, 59, 59, 999)).getTime() : false
+    this.name = this.isDoubles ? `${this.Name} ${this.Vorname} / ${this.NameDP} ${this.VornameDP}` : `${this.Name} ${this.Vorname}`
   }
 }
 

+ 58 - 3
yarn.lock

@@ -908,7 +908,7 @@ babel-register@^6.24.1:
     mkdirp "^0.5.1"
     source-map-support "^0.4.2"
 
-babel-runtime@6.23.0, babel-runtime@^6.18.0, babel-runtime@^6.22.0:
+babel-runtime@6.23.0, babel-runtime@^6.11.6, babel-runtime@^6.18.0, babel-runtime@^6.22.0:
   version "6.23.0"
   resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.23.0.tgz#0a9489f144de70efb3ce4300accdb329e2fc543b"
   dependencies:
@@ -1273,6 +1273,10 @@ clap@^1.0.9:
   dependencies:
     chalk "^1.1.3"
 
+classnames@^2.2.5:
+  version "2.2.5"
+  resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.5.tgz#fb3801d453467649ef3603c7d61a02bd129bde6d"
+
 clean-css@4.1.x:
   version "4.1.3"
   resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.1.3.tgz#07cfe8980edb20d455ddc23aadcf1e04c6e509ce"
@@ -1842,6 +1846,10 @@ dom-converter@~0.1:
   dependencies:
     utila "~0.3"
 
+dom-helpers@^3.2.0:
+  version "3.2.1"
+  resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-3.2.1.tgz#3203e07fed217bd1f424b019735582fc37b2825a"
+
 dom-serializer@0:
   version "0.1.0"
   resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.0.tgz#073c697546ce0780ce23be4a28e293e40bc30c82"
@@ -3032,7 +3040,7 @@ interpret@^1.0.0:
   version "1.0.3"
   resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.0.3.tgz#cbc35c62eeee73f19ab7b10a801511401afc0f90"
 
-invariant@^2.2.0, invariant@^2.2.2:
+invariant@^2.1.0, invariant@^2.2.0, invariant@^2.2.1, invariant@^2.2.2:
   version "2.2.2"
   resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.2.tgz#9e1f56ac0acdb6bf303306f338be3b204ae60360"
   dependencies:
@@ -3667,6 +3675,10 @@ jsx-ast-utils@^1.3.4, jsx-ast-utils@^1.4.0:
   version "1.4.1"
   resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-1.4.1.tgz#3867213e8dd79bf1e8f2300c0cfc1efb182c0df1"
 
+keycode@^2.1.2:
+  version "2.1.9"
+  resolved "https://registry.yarnpkg.com/keycode/-/keycode-2.1.9.tgz#964a23c54e4889405b4861a5c9f0480d45141dfa"
+
 kind-of@^3.0.2:
   version "3.2.2"
   resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64"
@@ -4770,7 +4782,7 @@ promise@7.1.1, promise@^7.1.1:
   dependencies:
     asap "~2.0.3"
 
-prop-types@^15.5.7, prop-types@~15.5.7:
+prop-types@^15.5.6, prop-types@^15.5.7, prop-types@^15.5.8, prop-types@~15.5.7:
   version "15.5.10"
   resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.5.10.tgz#2797dfc3126182e3a95e3dfbb2e893ddd7456154"
   dependencies:
@@ -4867,6 +4879,21 @@ rc@^1.0.1, rc@^1.1.6, rc@^1.1.7:
     minimist "^1.2.0"
     strip-json-comments "~2.0.1"
 
+react-bootstrap@^0.31.0:
+  version "0.31.0"
+  resolved "https://registry.yarnpkg.com/react-bootstrap/-/react-bootstrap-0.31.0.tgz#bbca804c0404d9c640102b2b656ae4cd5bea35c8"
+  dependencies:
+    babel-runtime "^6.11.6"
+    classnames "^2.2.5"
+    dom-helpers "^3.2.0"
+    invariant "^2.2.1"
+    keycode "^2.1.2"
+    prop-types "^15.5.6"
+    react-overlays "^0.7.0"
+    react-prop-types "^0.4.0"
+    uncontrollable "^4.1.0"
+    warning "^3.0.0"
+
 react-dev-utils@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/react-dev-utils/-/react-dev-utils-3.0.0.tgz#3677f37718ba0cae892ba9c01fe54d1622e6ef7c"
@@ -4909,6 +4936,22 @@ react-error-overlay@^1.0.7:
     settle-promise "1.0.0"
     source-map "0.5.6"
 
+react-overlays@^0.7.0:
+  version "0.7.0"
+  resolved "https://registry.yarnpkg.com/react-overlays/-/react-overlays-0.7.0.tgz#531898ff566c7e5c7226ead2863b8cf9fbb5a981"
+  dependencies:
+    classnames "^2.2.5"
+    dom-helpers "^3.2.0"
+    prop-types "^15.5.8"
+    react-prop-types "^0.4.0"
+    warning "^3.0.0"
+
+react-prop-types@^0.4.0:
+  version "0.4.0"
+  resolved "https://registry.yarnpkg.com/react-prop-types/-/react-prop-types-0.4.0.tgz#f99b0bfb4006929c9af2051e7c1414a5c75b93d0"
+  dependencies:
+    warning "^3.0.0"
+
 react-scripts@^1.0.7:
   version "1.0.7"
   resolved "https://registry.yarnpkg.com/react-scripts/-/react-scripts-1.0.7.tgz#fe1436dda03bb45465c76d097cfea4f32eb7cbbb"
@@ -5934,6 +5977,12 @@ uid-number@^0.0.6:
   version "0.0.6"
   resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81"
 
+uncontrollable@^4.1.0:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/uncontrollable/-/uncontrollable-4.1.0.tgz#e0358291252e1865222d90939b19f2f49f81c1a9"
+  dependencies:
+    invariant "^2.1.0"
+
 uniq@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff"
@@ -6088,6 +6137,12 @@ walker@~1.0.5:
   dependencies:
     makeerror "1.0.x"
 
+warning@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/warning/-/warning-3.0.0.tgz#32e5377cb572de4ab04753bdf8821c01ed605b7c"
+  dependencies:
+    loose-envify "^1.0.0"
+
 watch@~0.10.0:
   version "0.10.0"
   resolved "https://registry.yarnpkg.com/watch/-/watch-0.10.0.tgz#77798b2da0f9910d595f1ace5b0c2258521f21dc"