Forráskód Böngészése

before season 2021.

Tomi Cvetic 3 éve
szülő
commit
7b25d96223

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 16308 - 1
client/package-lock.json


+ 1 - 0
client/package.json

@@ -8,6 +8,7 @@
     "babel-polyfill": "^6.23.0",
     "babel-preset-env": "^1.7.0",
     "babel-register": "^6.24.1",
+    "bcrypt": "^5.0.1",
     "bcryptjs": "^2.4.3",
     "body-parser": "^1.17.2",
     "bootstrap": "4.1.1",

+ 89 - 23
client/src/swisstennis/components/Matches.js

@@ -8,7 +8,7 @@ class Matches extends React.Component {
     this.handleChange = this.handleChange.bind(this)
   }
 
-  handleChange (event) {
+  handleChange(event) {
     event.preventDefault()
     const { filterMatches } = this.props.swisstennisActions
     const nextFilter = {
@@ -17,63 +17,129 @@ class Matches extends React.Component {
       m_time: this.time.value,
       m_place: this.place.value,
       m_player: this.player.value,
-      m_result: this.result.value
+      m_result: this.result.value,
     }
     filterMatches(nextFilter)
   }
 
-  render () {
+  render() {
     const state = this.props.swisstennis
     const { setRecipients } = this.props.smsActions
     const { calendar, filteredMatches, filter } = state
     const participatingPlayers = []
-    filteredMatches.forEach(match => {
+    filteredMatches.forEach((match) => {
       if (match.player1) participatingPlayers.push(match.player1)
       if (match.player2) participatingPlayers.push(match.player2)
     })
 
     return (
       <div>
-        <p>{filteredMatches.length}/{calendar && calendar.matches.length} Spiele, {participatingPlayers.length} Spieler > <Link to="/sms" onClick={() => setRecipients(participatingPlayers)}>SMS</Link></p>
+        <p>
+          {filteredMatches.length}/{calendar && calendar.matches.length} Spiele,{' '}
+          {participatingPlayers.length} Spieler >{' '}
+          <Link to='/sms' onClick={() => setRecipients(participatingPlayers)}>
+            SMS
+          </Link>
+        </p>
         <form>
           <table className='table table-bordered table-striped'>
             <thead>
               <tr>
-                <th>Kategorie</th><th>Datum</th><th>Zeit</th><th>Ort</th><th>Spieler 1</th><th>Spieler 2</th><th>Resultat</th>
+                <th>Kategorie</th>
+                <th>Datum</th>
+                <th>Zeit</th>
+                <th>Ort</th>
+                <th>Spieler 1</th>
+                <th>Spieler 2</th>
+                <th>Resultat</th>
               </tr>
               <tr>
                 <td>
-                  <input type="input" ref={(input) => {this.category = input}} id="category" value={filter.category} placeholder="Kategorie" onChange={this.handleChange}></input>
+                  <input
+                    type='input'
+                    ref={(input) => {
+                      this.category = input
+                    }}
+                    id='category'
+                    value={filter.category}
+                    placeholder='Kategorie'
+                    onChange={this.handleChange}
+                  ></input>
                 </td>
                 <td>
-                  <input type="input" ref={(input) => {this.date = input}} id="date" value={filter.date} placeholder="Datum" onChange={this.handleChange}></input>
+                  <input
+                    type='input'
+                    ref={(input) => {
+                      this.date = input
+                    }}
+                    id='date'
+                    value={filter.date}
+                    placeholder='Datum'
+                    onChange={this.handleChange}
+                  ></input>
                 </td>
                 <td>
-                  <input type="input" ref={(input) => {this.time = input}} id="time" value={filter.time} placeholder="Zeit" onChange={this.handleChange}></input>
+                  <input
+                    type='input'
+                    ref={(input) => {
+                      this.time = input
+                    }}
+                    id='time'
+                    value={filter.time}
+                    placeholder='Zeit'
+                    onChange={this.handleChange}
+                  ></input>
                 </td>
                 <td>
-                  <input type="input" ref={(input) => {this.place = input}} id="place" value={filter.place} placeholder="Ort" onChange={this.handleChange}></input>
+                  <input
+                    type='input'
+                    ref={(input) => {
+                      this.place = input
+                    }}
+                    id='place'
+                    value={filter.place}
+                    placeholder='Ort'
+                    onChange={this.handleChange}
+                  ></input>
                 </td>
                 <td colSpan={2}>
-                  <input type="input" ref={(input) => {this.player = input}} id="player" value={filter.player} placeholder="Spieler" onChange={this.handleChange}></input>
+                  <input
+                    type='input'
+                    ref={(input) => {
+                      this.player = input
+                    }}
+                    id='player'
+                    value={filter.player}
+                    placeholder='Spieler'
+                    onChange={this.handleChange}
+                  ></input>
                 </td>
                 <td>
-                  <input type="input" ref={(input) => {this.result = input}} id="result" value={filter.result} placeholder="Resultat" onChange={this.handleChange}></input>
+                  <input
+                    type='input'
+                    ref={(input) => {
+                      this.result = input
+                    }}
+                    id='result'
+                    value={filter.result}
+                    placeholder='Resultat'
+                    onChange={this.handleChange}
+                  ></input>
                 </td>
               </tr>
             </thead>
             <tbody>
-              {filteredMatches.map((match, key) => 
-              <tr key={key}>
-                <td>{match.category}</td>
-                <td>{moment(match.date).format('DD.MM.YYYY')}</td>
-                <td>{moment(match.date).format('HH:mm')}</td>
-                <td>{match.place}</td>
-                <td>{match.player1 ? match.player1.fullName : ''}</td>
-                <td>{match.player2 ? match.player2.fullName : ''}</td>
-                <td>{match.result}</td>
-              </tr>
-              )}
+              {filteredMatches.map((match, key) => (
+                <tr key={key}>
+                  <td>{match.category}</td>
+                  <td>{moment.utc(match.date).format('DD.MM.YYYY')}</td>
+                  <td>{moment.utc(match.date).format('HH:mm')}</td>
+                  <td>{match.place}</td>
+                  <td>{match.player1 ? match.player1.fullName : ''}</td>
+                  <td>{match.player2 ? match.player2.fullName : ''}</td>
+                  <td>{match.result}</td>
+                </tr>
+              ))}
             </tbody>
           </table>
         </form>

+ 113 - 26
client/src/swisstennis/components/Players.js

@@ -7,7 +7,7 @@ class Players extends React.Component {
     this.handleChange = this.handleChange.bind(this)
   }
 
-  handleChange (event) {
+  handleChange(event) {
     event.preventDefault()
     const { filterPlayers } = this.props.swisstennisActions
     const nextFilter = {
@@ -16,13 +16,13 @@ class Players extends React.Component {
       p_ranking: this.ranking.value,
       p_phone: this.phone.value,
       p_email: this.email.value,
-      p_junior: this.junior.checked,
-      p_paid: this.paid.checked,
+      p_junior: this.junior.value,
+      p_paid: this.paid.value,
     }
     filterPlayers(nextFilter)
   }
 
-  render () {
+  render() {
     const state = this.props.swisstennis
     const { setRecipients } = this.props.smsActions
     const { playerList, filteredPlayers, filter } = state
@@ -30,50 +30,137 @@ class Players extends React.Component {
     return (
       <div>
         <form>
-          <p>{filteredPlayers.length}/{playerList && playerList.players.length} Spieler > <Link to="/sms" onClick={() => setRecipients(filteredPlayers)}>SMS</Link></p>
-          <label htmlFor="matchfilter">Match Filter benutzen</label>
-          <input type="checkbox" ref={(input) => {this.matchfilter = input}} id="matchfilter" checked={filter.matchfilter} onChange={this.handleChange} />
+          <p>
+            {filteredPlayers.length}/{playerList && playerList.players.length}{' '}
+            Spieler >{' '}
+            <Link to='/sms' onClick={() => setRecipients(filteredPlayers)}>
+              SMS
+            </Link>
+          </p>
+          <label htmlFor='matchfilter'>Match Filter benutzen</label>
+          <input
+            type='checkbox'
+            ref={(input) => {
+              this.matchfilter = input
+            }}
+            id='matchfilter'
+            checked={filter.matchfilter}
+            onChange={this.handleChange}
+          />
           <table className='table table-bordered table-striped'>
             <thead>
               <tr>
-                <th>Name</th><th>Klassierung</th><th>Kategorie</th><th>Telefon</th><th>E-Mail</th><th>Junior</th><th>Bezahlt</th>
+                <th>Name</th>
+                <th>Klassierung</th>
+                <th>Kategorie</th>
+                <th>Telefon</th>
+                <th>E-Mail</th>
+                <th>Junior</th>
+                <th>Bezahlt</th>
               </tr>
               <tr>
                 <td>
-                  <input type="input" ref={(input) => {this.name = input}} id="name" value={filter.name} placeholder="Name" onChange={this.handleChange}></input>
+                  <input
+                    type='input'
+                    ref={(input) => {
+                      this.name = input
+                    }}
+                    id='name'
+                    value={filter.name}
+                    placeholder='Name'
+                    onChange={this.handleChange}
+                  ></input>
                 </td>
                 <td>
-                  <input type="input" ref={(input) => {this.ranking = input}} id="ranking" value={filter.ranking} placeholder="Name" onChange={this.handleChange}></input>
+                  <input
+                    type='input'
+                    ref={(input) => {
+                      this.ranking = input
+                    }}
+                    id='ranking'
+                    value={filter.ranking}
+                    placeholder='Klassierung'
+                    onChange={this.handleChange}
+                  ></input>
                 </td>
                 <td>
-                  <input type="input" ref={(input) => {this.category = input}} id="category" value={filter.category} placeholder="Datum" onChange={this.handleChange}></input>
+                  <input
+                    type='input'
+                    ref={(input) => {
+                      this.category = input
+                    }}
+                    id='category'
+                    value={filter.category}
+                    placeholder='Kategorie'
+                    onChange={this.handleChange}
+                  ></input>
                 </td>
                 <td>
-                  <input type="input" ref={(input) => {this.phone = input}} id="phone" value={filter.phone} placeholder="Datum" onChange={this.handleChange}></input>
+                  <input
+                    type='input'
+                    ref={(input) => {
+                      this.phone = input
+                    }}
+                    id='phone'
+                    value={filter.phone}
+                    placeholder='Telefon'
+                    onChange={this.handleChange}
+                  ></input>
                 </td>
                 <td>
-                  <input type="input" ref={(input) => {this.email = input}} id="email" value={filter.email} placeholder="Datum" onChange={this.handleChange}></input>
+                  <input
+                    type='input'
+                    ref={(input) => {
+                      this.email = input
+                    }}
+                    id='email'
+                    value={filter.email}
+                    placeholder='E-Mail'
+                    onChange={this.handleChange}
+                  ></input>
                 </td>
                 <td>
-                  <input type="checkbox" ref={(input) => {this.junior = input}} id="junior" checked={filter.junior} placeholder="Spieler" onChange={this.handleChange}></input>
+                  <select
+                    ref={(input) => {
+                      this.junior = input
+                    }}
+                    id='junior'
+                    value={filter.junior}
+                    onChange={this.handleChange}
+                  >
+                    <option value='-1'>-</option>
+                    <option value='1'>Junior</option>
+                    <option value='0'>Erwachsen</option>
+                  </select>
                 </td>
                 <td>
-                  <input type="checkbox" ref={(input) => {this.paid = input}} id="paid" checked={filter.paid} placeholder="Resultat" onChange={this.handleChange}></input>
+                  <select
+                    ref={(input) => {
+                      this.paid = input
+                    }}
+                    id='paid'
+                    value={filter.paid}
+                    onChange={this.handleChange}
+                  >
+                    <option value='-1'>-</option>
+                    <option value='1'>bezahlt</option>
+                    <option value='0'>offen</option>
+                  </select>
                 </td>
               </tr>
             </thead>
             <tbody>
-              {filteredPlayers.map((player, key) => 
-              <tr key={key}>
-                <td>{player.fullName}</td>
-                <td>{player.ranking}</td>
-                <td>{player.category}</td>
-                <td>{player.phone}</td>
-                <td>{player.email}</td>
-                <td>{player.junior ? '\u2713' : '\u2717'}</td>
-                <td>{player.paid ? '\u2713' : '\u2717'}</td>
-              </tr>
-              )}
+              {filteredPlayers.map((player, key) => (
+                <tr key={key}>
+                  <td>{player.fullName}</td>
+                  <td>{player.ranking}</td>
+                  <td>{player.category}</td>
+                  <td>{player.phone}</td>
+                  <td>{player.email}</td>
+                  <td>{player.junior ? '\u2713' : '\u2717'}</td>
+                  <td>{player.paid ? '\u2713' : '\u2717'}</td>
+                </tr>
+              ))}
             </tbody>
           </table>
         </form>

+ 332 - 184
client/src/swisstennis/state.js

@@ -15,31 +15,31 @@ export const actions = {
   matchesSetFilter: (data) => {
     return {
       type: 'SWISSTENNIS/SET_FILTER',
-      data
+      data,
     }
   },
   filterMatches: (data) => {
     return {
       type: 'SWISSTENNIS/FILTER_MATCHES',
-      data
+      data,
     }
   },
   filterMatchesDone: (data) => {
     return {
       type: 'SWISSTENNIS/FILTER_MATCHES_DONE',
-      data
+      data,
     }
   },
   filterPlayers: (data) => {
     return {
       type: 'SWISSTENNIS/FILTER_PLAYERS',
-      data
+      data,
     }
   },
   filterPlayersDone: (data) => {
     return {
       type: 'SWISSTENNIS/FILTER_PLAYERS_DONE',
-      data
+      data,
     }
   },
   fileListRequest: () => {
@@ -47,212 +47,212 @@ export const actions = {
       type: 'SWISSTENNIS/FILE_LIST_REQUEST',
     }
   },
-  fileListSuccess: data => {
+  fileListSuccess: (data) => {
     return {
       type: 'SWISSTENNIS/FILE_LIST_SUCCESS',
-      data
+      data,
     }
   },
-  fileListFailure: error => {
+  fileListFailure: (error) => {
     return {
       type: 'SWISSTENNIS/FILE_LIST_FAILURE',
-      error
+      error,
     }
   },
   loginRequest: (data) => {
     return {
       type: 'SWISSTENNIS/LOGIN_REQUEST',
-      data
+      data,
     }
   },
   loginSuccess: (data) => {
     return {
       type: 'SWISSTENNIS/LOGIN_SUCCESS',
-      data
+      data,
     }
   },
   loginFailure: (error) => {
     return {
       type: 'SWISSTENNIS/LOGIN_FAILURE',
-      error
+      error,
     }
   },
   downloadPlayerListRequest: (data) => {
     return {
       type: 'SWISSTENNIS/DOWNLOAD_PLAYERLIST_REQUEST',
-      data
+      data,
     }
   },
   downloadPlayerListSuccess: (data) => {
     return {
       type: 'SWISSTENNIS/DOWNLOAD_PLAYERLIST_SUCCESS',
-      data
+      data,
     }
   },
-  downloadPlayerListFailure: error => {
+  downloadPlayerListFailure: (error) => {
     return {
       type: 'SWISSTENNIS/DOWNLOAD_PLAYERLIST_FAILURE',
-      error
+      error,
     }
   },
   parsePlayerListRequest: (data) => {
     return {
       type: 'SWISSTENNIS/PARSE_PLAYERLIST_REQUEST',
-      data
+      data,
     }
   },
   parsePlayerListSuccess: (data) => {
     return {
       type: 'SWISSTENNIS/PARSE_PLAYERLIST_SUCCESS',
-      data
+      data,
     }
   },
-  parsePlayerListFailure: error => {
+  parsePlayerListFailure: (error) => {
     return {
       type: 'SWISSTENNIS/PARSE_PLAYERLIST_FAILURE',
-      error
+      error,
     }
   },
   deletePlayerListRequest: (data) => {
     return {
       type: 'SWISSTENNIS/DELETE_PLAYERLIST_REQUEST',
-      data
+      data,
     }
   },
   deletePlayerListSuccess: (data) => {
     return {
       type: 'SWISSTENNIS/DELETE_PLAYERLIST_SUCCESS',
-      data
+      data,
     }
   },
-  deletePlayerListFailure: error => {
+  deletePlayerListFailure: (error) => {
     return {
       type: 'SWISSTENNIS/DELETE_PLAYERLIST_FAILURE',
-      error
+      error,
     }
   },
   downloadCalendarRequest: (data) => {
     return {
       type: 'SWISSTENNIS/DOWNLOAD_CALENDAR_REQUEST',
-      data
+      data,
     }
   },
   downloadCalendarSuccess: (data) => {
     return {
       type: 'SWISSTENNIS/DOWNLOAD_CALENDAR_SUCCESS',
-      data
+      data,
     }
   },
-  downloadCalendarFailure: error => {
+  downloadCalendarFailure: (error) => {
     return {
       type: 'SWISSTENNIS/DOWNLOAD_CALENDAR_FAILURE',
-      error
+      error,
     }
   },
-  parseCalendarRequest: data => {
+  parseCalendarRequest: (data) => {
     return {
       type: 'SWISSTENNIS/PARSE_CALENDAR_REQUEST',
-      data
+      data,
     }
   },
-  parseCalendarSuccess: data => {
+  parseCalendarSuccess: (data) => {
     return {
       type: 'SWISSTENNIS/PARSE_CALENDAR_SUCCESS',
-      data
+      data,
     }
   },
-  parseCalendarFailure: error => {
+  parseCalendarFailure: (error) => {
     return {
       type: 'SWISSTENNIS/PARSE_CALENDAR_FAILURE',
-      error
+      error,
     }
   },
-  deleteCalendarRequest: data => {
+  deleteCalendarRequest: (data) => {
     return {
       type: 'SWISSTENNIS/DELETE_CALENDAR_REQUEST',
-      data
+      data,
     }
   },
-  deleteCalendarSuccess: data => {
+  deleteCalendarSuccess: (data) => {
     return {
       type: 'SWISSTENNIS/DELETE_CALENDAR_SUCCESS',
-      data
+      data,
     }
   },
-  deleteCalendarFailure: error => {
+  deleteCalendarFailure: (error) => {
     return {
       type: 'SWISSTENNIS/DELETE_CALENDAR_FAILURE',
-      error
+      error,
     }
   },
   getPlayerListRequest: () => {
     return {
-      type: 'SWISSTENNIS/GET_PLAYERLIST_REQUEST'
+      type: 'SWISSTENNIS/GET_PLAYERLIST_REQUEST',
     }
   },
   getPlayerListSuccess: (data) => {
     return {
       type: 'SWISSTENNIS/GET_PLAYERLIST_SUCCESS',
-      data
+      data,
     }
   },
-  getPlayerListFailure: error => {
+  getPlayerListFailure: (error) => {
     return {
       type: 'SWISSTENNIS/GET_PLAYERLIST_FAILURE',
-      error
+      error,
     }
   },
   getPlayerListsRequest: () => {
     return {
-      type: 'SWISSTENNIS/GET_PLAYERLISTS_REQUEST'
+      type: 'SWISSTENNIS/GET_PLAYERLISTS_REQUEST',
     }
   },
   getPlayerListsSuccess: (data) => {
     return {
       type: 'SWISSTENNIS/GET_PLAYERLISTS_SUCCESS',
-      data
+      data,
     }
   },
-  getPlayerListsFailure: error => {
+  getPlayerListsFailure: (error) => {
     return {
       type: 'SWISSTENNIS/GET_PLAYERLISTS_FAILURE',
-      error
+      error,
     }
   },
   getCalendarRequest: (data) => {
     return {
       type: 'SWISSTENNIS/GET_CALENDAR_REQUEST',
-      data
+      data,
     }
   },
   getCalendarSuccess: (data) => {
     return {
       type: 'SWISSTENNIS/GET_CALENDAR_SUCCESS',
-      data
+      data,
     }
   },
-  getCalendarFailure: error => {
+  getCalendarFailure: (error) => {
     return {
       type: 'SWISSTENNIS/GET_CALENDAR_FAILURE',
-      error
+      error,
     }
   },
   getCalendarsRequest: (data) => {
     return {
       type: 'SWISSTENNIS/GET_CALENDARS_REQUEST',
-      data
+      data,
     }
   },
   getCalendarsSuccess: (data) => {
     return {
       type: 'SWISSTENNIS/GET_CALENDARS_SUCCESS',
-      data
+      data,
     }
   },
-  getCalendarsFailure: error => {
+  getCalendarsFailure: (error) => {
     return {
       type: 'SWISSTENNIS/GET_CALENDARS_FAILURE',
-      error
+      error,
     }
   },
 }
@@ -271,8 +271,9 @@ const emptyFilter = {
   p_category: '',
   p_ranking: '',
   p_phone: '',
-  p_junior: 'off',
-  p_paid: 'off',
+  p_email: '',
+  p_junior: '-1',
+  p_paid: '-1',
   p_matches: false,
 }
 
@@ -301,7 +302,7 @@ export const state = {
 console.log('State state', state)
 
 /** reducer is called by the redux dispatcher and handles all component actions */
-export function reducer (state = [], action) {
+export function reducer(state = [], action) {
   let index
   switch (action.type) {
     case 'SWISSTENNIS/SET_FILTER':
@@ -321,13 +322,22 @@ export function reducer (state = [], action) {
     case 'SWISSTENNIS/FILE_LIST_FAILURE':
       return { ...state, fileListStatus: 'failure' }
     case 'SWISSTENNIS/LOGIN_REQUEST':
-      return { ...state, 
-        loginStatus: 'request', 
-        playerListDownloadStatus: action.data.sequence ? 'uninitialized' : state.playerListDownloadStatus,
-        playerListParseStatus: action.data.sequence ? 'uninitialized' : state.playerListParseStatus,
-        calendarDownloadStatus: action.data.sequence ? 'uninitialized' : state.calendarDownloadStatus,
-        calendarParseStatus: action.data.sequence ? 'uninitialized' : state.calendarParseStatus,
-         }
+      return {
+        ...state,
+        loginStatus: 'request',
+        playerListDownloadStatus: action.data.sequence
+          ? 'uninitialized'
+          : state.playerListDownloadStatus,
+        playerListParseStatus: action.data.sequence
+          ? 'uninitialized'
+          : state.playerListParseStatus,
+        calendarDownloadStatus: action.data.sequence
+          ? 'uninitialized'
+          : state.calendarDownloadStatus,
+        calendarParseStatus: action.data.sequence
+          ? 'uninitialized'
+          : state.calendarParseStatus,
+      }
     case 'SWISSTENNIS/LOGIN_SUCCESS':
       return { ...state, loginStatus: 'success' }
     case 'SWISSTENNIS/LOGIN_FAILURE':
@@ -335,39 +345,73 @@ export function reducer (state = [], action) {
     case 'SWISSTENNIS/DOWNLOAD_PLAYERLIST_REQUEST':
       return { ...state, playerListDownloadStatus: 'request' }
     case 'SWISSTENNIS/DOWNLOAD_PLAYERLIST_SUCCESS':
-      return { ...state, playerListDownloadStatus: 'success', files: [ ...state.files, action.data ] }
+      return {
+        ...state,
+        playerListDownloadStatus: 'success',
+        files: [...state.files, action.data],
+      }
     case 'SWISSTENNIS/DOWNLOAD_PLAYERLIST_FAILURE':
       return { ...state, playerListDownloadStatus: 'failure' }
     case 'SWISSTENNIS/PARSE_PLAYERLIST_REQUEST':
       return { ...state, playerListParseStatus: 'request' }
     case 'SWISSTENNIS/PARSE_PLAYERLIST_SUCCESS':
-      return { ...state, playerListParseStatus: 'success', playerLists: [ ...state.playerLists, action.data.playerList ] }
+      return {
+        ...state,
+        playerListParseStatus: 'success',
+        playerLists: [...state.playerLists, action.data.playerList],
+      }
     case 'SWISSTENNIS/PARSE_PLAYERLIST_FAILURE':
       return { ...state, playerListParseStatus: 'failure' }
     case 'SWISSTENNIS/DELETE_PLAYERLIST_REQUEST':
       return { ...state, playerListDeleteStatus: 'request' }
     case 'SWISSTENNIS/DELETE_PLAYERLIST_SUCCESS':
-      index = state.playerLists.findIndex(playerList => playerList._id === action.data._id)
-      return { ...state, playerListDeleteStatus: 'success', playerLists: [ ...state.playerLists.slice(0, index), ...state.playerLists.slice(index + 1) ] }
+      index = state.playerLists.findIndex(
+        (playerList) => playerList._id === action.data._id
+      )
+      return {
+        ...state,
+        playerListDeleteStatus: 'success',
+        playerLists: [
+          ...state.playerLists.slice(0, index),
+          ...state.playerLists.slice(index + 1),
+        ],
+      }
     case 'SWISSTENNIS/DELETE_PLAYERLIST_FAILURE':
       return { ...state, playerListDeleteStatus: 'failure' }
     case 'SWISSTENNIS/DOWNLOAD_CALENDAR_REQUEST':
       return { ...state, calendarDownloadStatus: 'request' }
     case 'SWISSTENNIS/DOWNLOAD_CALENDAR_SUCCESS':
-      return { ...state, calendarDownloadStatus: 'success', files: [ ...state.files, action.data ] }
+      return {
+        ...state,
+        calendarDownloadStatus: 'success',
+        files: [...state.files, action.data],
+      }
     case 'SWISSTENNIS/DOWNLOAD_CALENDAR_FAILURE':
       return { ...state, calendarDownloadStatus: 'failure' }
     case 'SWISSTENNIS/PARSE_CALENDAR_REQUEST':
       return { ...state, calendarParseStatus: 'request' }
     case 'SWISSTENNIS/PARSE_CALENDAR_SUCCESS':
-      return { ...state, calendarParseStatus: 'success', calendars: [ ...state.calendars, action.data.matchList ] }
+      return {
+        ...state,
+        calendarParseStatus: 'success',
+        calendars: [...state.calendars, action.data.matchList],
+      }
     case 'SWISSTENNIS/PARSE_CALENDAR_FAILURE':
       return { ...state, calendarParseStatus: 'failure' }
     case 'SWISSTENNIS/DELETE_CALENDAR_REQUEST':
       return { ...state, calendarDeleteStatus: 'request' }
     case 'SWISSTENNIS/DELETE_CALENDAR_SUCCESS':
-      index = state.calendars.findIndex(calendar => calendar._id === action.data._id)
-      return { ...state, calendarDeleteStatus: 'success', calendars: [ ...state.calendars.slice(0, index), ...state.calendars.slice(index + 1) ] }
+      index = state.calendars.findIndex(
+        (calendar) => calendar._id === action.data._id
+      )
+      return {
+        ...state,
+        calendarDeleteStatus: 'success',
+        calendars: [
+          ...state.calendars.slice(0, index),
+          ...state.calendars.slice(index + 1),
+        ],
+      }
     case 'SWISSTENNIS/DELETE_CALENDAR_FAILURE':
       return { ...state, calendarDeleteStatus: 'failure' }
     case 'SWISSTENNIS/GET_CALENDAR_REQUEST':
@@ -399,52 +443,108 @@ export function reducer (state = [], action) {
   }
 }
 
-function * filterPlayers (action) {
+function* filterPlayers(action) {
   const state = yield select()
   const { playerList } = state.swisstennis
   const filter = action.data
-  const filteredPlayers = playerList ? playerList.players.filter(player => {
-    return (
-      (filter.p_name ? player.fullName && player.fullName.toLowerCase().includes(filter.p_name.toLowerCase()) : true) && 
-      (filter.p_category ? player.category && player.category.toLowerCase().includes(filter.p_category.toLowerCase()) : true) && 
-      (filter.p_ranking ? player.ranking && player.ranking.toLowerCase().includes(filter.p_ranking.toLowerCase()) : true) && 
-      (filter.p_phone ? player.phone && player.phone.toLowerCase().includes(filter.p_phone.toLowerCase()) : true) &&
-      /*(filter.p_junior !== 'off' ? player.junior === (filter.p_junior === 'Ja') : true) &&
-      (filter.p_paid !== 'off' ? player.paid === (filter.p_paid === 'Ja') : true)*/ true
-    )
-  }) : []
-  yield put(actions.filterPlayersDone({filteredPlayers}))
+  console.log('filter', filter)
+  const filteredPlayers = playerList
+    ? playerList.players.filter((player) => {
+        return (
+          (filter.p_name
+            ? player.fullName &&
+              player.fullName
+                .toLowerCase()
+                .includes(filter.p_name.toLowerCase())
+            : true) &&
+          (filter.p_category
+            ? player.category &&
+              player.category
+                .toLowerCase()
+                .includes(filter.p_category.toLowerCase())
+            : true) &&
+          (filter.p_ranking
+            ? player.ranking &&
+              player.ranking
+                .toLowerCase()
+                .includes(filter.p_ranking.toLowerCase())
+            : true) &&
+          (filter.p_phone
+            ? player.phone &&
+              player.phone.toLowerCase().includes(filter.p_phone.toLowerCase())
+            : true) &&
+          (filter.p_email
+            ? player.email &&
+              player.email.toLowerCase().includes(filter.p_email.toLowerCase())
+            : true) &&
+          (filter.p_junior === '-1'
+            ? true
+            : player.junior === (filter.p_junior === '1')) &&
+          (filter.p_paid === '-1'
+            ? true
+            : player.paid === (filter.p_paid === '1'))
+        )
+      })
+    : []
+  yield put(actions.filterPlayersDone({ filteredPlayers }))
 }
 
-function * filterMatches (action) {
+function* filterMatches(action) {
   const state = yield select()
   const { calendar } = state.swisstennis
   const filter = action.data
-  const filteredMatches = calendar ? calendar.matches.filter(match => {
-    return (
-      (filter.m_category ? match.category && match.category.toLowerCase().includes(filter.m_category.toLowerCase()) : true) && 
-      (filter.m_place ? match.place && match.place.toLowerCase().includes(filter.m_place.toLowerCase()) : true) && 
-      (filter.m_date ? match.date && moment(match.date).format('DD.MM.YYYY').includes(filter.m_date) : true) &&
-      (filter.m_time ? match.date && moment(match.date).format('HH:mm').includes(filter.m_time) : true) &&
-      (filter.m_player ? 
-        (match.player1 && match.player1.fullName.toLowerCase().includes(filter.m_player.toLowerCase())) || (match.player2 && match.player2.fullName.toLowerCase().includes(filter.m_player.toLowerCase())) :
-        true) &&
-      (filter.m_result ? match.result.toLowerCase().includes(filter.m_result) : true)
-    )
-  }) : []
-  yield put(actions.filterMatchesDone({filteredMatches}))
+  const filteredMatches = calendar
+    ? calendar.matches.filter((match) => {
+        return (
+          (filter.m_category
+            ? match.category &&
+              match.category
+                .toLowerCase()
+                .includes(filter.m_category.toLowerCase())
+            : true) &&
+          (filter.m_place
+            ? match.place &&
+              match.place.toLowerCase().includes(filter.m_place.toLowerCase())
+            : true) &&
+          (filter.m_date
+            ? match.date &&
+              moment(match.date).format('DD.MM.YYYY').includes(filter.m_date)
+            : true) &&
+          (filter.m_time
+            ? match.date &&
+              moment(match.date).format('HH:mm').includes(filter.m_time)
+            : true) &&
+          (filter.m_player
+            ? (match.player1 &&
+                match.player1.fullName
+                  .toLowerCase()
+                  .includes(filter.m_player.toLowerCase())) ||
+              (match.player2 &&
+                match.player2.fullName
+                  .toLowerCase()
+                  .includes(filter.m_player.toLowerCase()))
+            : true) &&
+          (filter.m_result
+            ? match.result.toLowerCase().includes(filter.m_result)
+            : true)
+        )
+      })
+    : []
+  yield put(actions.filterMatchesDone({ filteredMatches }))
 }
 
-function * fileList (action) {
-  yield put(api.actions.apiRequest({
-    path: `api/swisstennis/files`,
-    method: 'GET',
-    onSuccess: actions.fileListSuccess,
-    onFailure: actions.fileListFailure
-  }))
+function* fileList(action) {
+  yield put(
+    api.actions.apiRequest({
+      path: `api/swisstennis/files`,
+      method: 'GET',
+      onSuccess: actions.fileListSuccess,
+      onFailure: actions.fileListFailure,
+    })
+  )
 }
 
-function * login (action) {
+function* login(action) {
   try {
     const token = localStorage.getItem('accessToken')
     console.log('Swisstennis login requested', action, token)
@@ -452,16 +552,17 @@ function * login (action) {
       method: 'POST',
       headers: {
         'Content-Type': 'application/json',
-        'x-access-token': token
-      }
-    }) 
+        'x-access-token': token,
+      },
+    })
     if (response.status !== 200) {
       console.log(response)
       throw Error(response.status)
     } else {
       const responseJson = yield response.json()
       yield put(actions.loginSuccess(responseJson))
-      if (action.data.sequence) yield put(actions.downloadPlayerListRequest({sequence: true}))
+      if (action.data.sequence)
+        yield put(actions.downloadPlayerListRequest({ sequence: true }))
     }
   } catch (error) {
     console.log('Config failure!', actions.loginFailure(error))
@@ -469,41 +570,59 @@ function * login (action) {
   }
 }
 
-function * getCalendars (action) {
-  yield put(api.actions.apiRequest({
-    path: `api/swisstennis/calendars`,
-    method: 'GET',
-    onSuccess: actions.getCalendarsSuccess,
-    onFailure: actions.getCalendarsFailure
-  }))
+function* getCalendars(action) {
+  yield put(
+    api.actions.apiRequest({
+      path: `api/swisstennis/calendars`,
+      method: 'GET',
+      onSuccess: actions.getCalendarsSuccess,
+      onFailure: actions.getCalendarsFailure,
+    })
+  )
 }
 
-function * getCalendar (action) {
-  yield put(api.actions.apiRequest({
-    path: `api/swisstennis/calendar${(action.data && action.data.calendarId) ? `?calendarId=${action.data.calendarId}` : ''}`,
-    method: 'GET',
-    onSuccess: [actions.getCalendarSuccess, actions.filterMatches],
-    onFailure: actions.getCalendarFailure
-  }))
+function* getCalendar(action) {
+  yield put(
+    api.actions.apiRequest({
+      path: `api/swisstennis/calendar${
+        action.data && action.data.calendarId
+          ? `?calendarId=${action.data.calendarId}`
+          : ''
+      }`,
+      method: 'GET',
+      onSuccess: [actions.getCalendarSuccess, actions.filterMatches],
+      onFailure: actions.getCalendarFailure,
+    })
+  )
 }
 
-function * downloadCalendar (action) {
+function* downloadCalendar(action) {
   try {
     const token = localStorage.getItem('accessToken')
     console.log('Download calendar requested', action, token)
-    const response = yield call(fetch, `${SZTM_API}/api/swisstennis/calendar/download`, {
-      method: 'GET',
-      headers: {
-        'Content-Type': 'application/json',
-        'x-access-token': token
+    const response = yield call(
+      fetch,
+      `${SZTM_API}/api/swisstennis/calendar/download`,
+      {
+        method: 'GET',
+        headers: {
+          'Content-Type': 'application/json',
+          'x-access-token': token,
+        },
       }
-    }) 
+    )
     if (response.status !== 200) {
       throw Error(response.status)
     } else {
       const responseJson = yield response.json()
       yield put(actions.downloadCalendarSuccess(responseJson))
-      if (action.data.sequence) yield put(actions.parseCalendarRequest({filename: responseJson.filename, sequence: true}))
+      if (action.data.sequence)
+        yield put(
+          actions.parseCalendarRequest({
+            filename: responseJson.filename,
+            sequence: true,
+          })
+        )
     }
   } catch (error) {
     console.log('Config failure!', actions.downloadCalendarFailure(error))
@@ -511,58 +630,80 @@ function * downloadCalendar (action) {
   }
 }
 
-function * parseCalendar (action) {
-  yield put(api.actions.apiRequest({
-    path: `api/swisstennis/calendar/parse/${action.data.filename}`,
-    method: 'GET',
-    onSuccess: actions.parseCalendarSuccess,
-    onFailure: actions.parseCalendarFailure
-  }))
+function* parseCalendar(action) {
+  yield put(
+    api.actions.apiRequest({
+      path: `api/swisstennis/calendar/parse/${action.data.filename}`,
+      method: 'GET',
+      onSuccess: actions.parseCalendarSuccess,
+      onFailure: actions.parseCalendarFailure,
+    })
+  )
 }
 
-function * deleteCalendar (action) {
-  yield put(api.actions.apiRequest({
-    path: `api/swisstennis/calendar/${action.data.calendarId}`,
-    method: 'DELETE',
-    onSuccess: actions.deleteCalendarSuccess,
-    onFailure: actions.deleteCalendarFailure
-  }))
+function* deleteCalendar(action) {
+  yield put(
+    api.actions.apiRequest({
+      path: `api/swisstennis/calendar/${action.data.calendarId}`,
+      method: 'DELETE',
+      onSuccess: actions.deleteCalendarSuccess,
+      onFailure: actions.deleteCalendarFailure,
+    })
+  )
 }
 
-function * getPlayerLists (action) {
-  yield put(api.actions.apiRequest({
-    path: `api/swisstennis/playerlists`,
-    method: 'GET',
-    onSuccess: actions.getPlayerListsSuccess,
-    onFailure: actions.getPlayerListsFailure
-  }))
+function* getPlayerLists(action) {
+  yield put(
+    api.actions.apiRequest({
+      path: `api/swisstennis/playerlists`,
+      method: 'GET',
+      onSuccess: actions.getPlayerListsSuccess,
+      onFailure: actions.getPlayerListsFailure,
+    })
+  )
 }
 
-function * getPlayerList (action) {
-  yield put(api.actions.apiRequest({
-    path: `api/swisstennis/playerlist${(action.data && action.data.playerlistId) ? `?playerlistId=${action.data.playerlistId}` : ''}`,
-    method: 'GET',
-    onSuccess: [actions.getPlayerListSuccess, actions.filterPlayers],
-    onFailure: actions.getPlayerListFailure
-  }))
+function* getPlayerList(action) {
+  yield put(
+    api.actions.apiRequest({
+      path: `api/swisstennis/playerlist${
+        action.data && action.data.playerlistId
+          ? `?playerlistId=${action.data.playerlistId}`
+          : ''
+      }`,
+      method: 'GET',
+      onSuccess: [actions.getPlayerListSuccess, actions.filterPlayers],
+      onFailure: actions.getPlayerListFailure,
+    })
+  )
 }
 
-function * downloadPlayerList (action) {
+function* downloadPlayerList(action) {
   try {
     const token = localStorage.getItem('accessToken')
     console.log('Playerlist download requested', action, token)
-    const response = yield call(fetch, `${SZTM_API}/api/swisstennis/playerlist/download`, {
-      method: 'GET',
-      headers: {
-        'x-access-token': token
+    const response = yield call(
+      fetch,
+      `${SZTM_API}/api/swisstennis/playerlist/download`,
+      {
+        method: 'GET',
+        headers: {
+          'x-access-token': token,
+        },
       }
-    }) 
+    )
     if (response.status !== 200) {
       throw Error(response.status)
     } else {
       const responseJson = yield response.json()
       yield put(actions.downloadPlayerListSuccess(responseJson))
-      if (action.data.sequence) yield put(actions.parsePlayerListRequest({filename: responseJson.filename, sequence: true}))
+      if (action.data.sequence)
+        yield put(
+          actions.parsePlayerListRequest({
+            filename: responseJson.filename,
+            sequence: true,
+          })
+        )
     }
   } catch (error) {
     console.log('Config failure!', actions.downloadPlayerListFailure(error))
@@ -570,22 +711,27 @@ function * downloadPlayerList (action) {
   }
 }
 
-function * parsePlayerList (action) {
+function* parsePlayerList(action) {
   try {
     const token = localStorage.getItem('accessToken')
     console.log('PlayerList parse requested', action, token)
-    const response = yield call(fetch, `${SZTM_API}/api/swisstennis/playerlist/parse/${action.data.filename}`, {
-      method: 'GET',
-      headers: {
-        'x-access-token': token
+    const response = yield call(
+      fetch,
+      `${SZTM_API}/api/swisstennis/playerlist/parse/${action.data.filename}`,
+      {
+        method: 'GET',
+        headers: {
+          'x-access-token': token,
+        },
       }
-    })
+    )
     if (response.status !== 200) {
       throw Error(response.status)
     } else {
       const responseJson = yield response.json()
       yield put(actions.parsePlayerListSuccess(responseJson))
-      if (action.data.sequence) yield put(actions.downloadCalendarRequest({sequence: true}))
+      if (action.data.sequence)
+        yield put(actions.downloadCalendarRequest({ sequence: true }))
     }
   } catch (error) {
     console.log('Config failure!', actions.parsePlayerListFailure(error))
@@ -593,17 +739,19 @@ function * parsePlayerList (action) {
   }
 }
 
-function * deletePlayerList (action) {
-  yield put(api.actions.apiRequest({
-    path: `api/swisstennis/playerlist/${action.data.playerlistId}`,
-    method: 'DELETE',
-    onSuccess: actions.deletePlayerListSuccess,
-    onFailure: actions.deletePlayerListFailure
-  }))
+function* deletePlayerList(action) {
+  yield put(
+    api.actions.apiRequest({
+      path: `api/swisstennis/playerlist/${action.data.playerlistId}`,
+      method: 'DELETE',
+      onSuccess: actions.deletePlayerListSuccess,
+      onFailure: actions.deletePlayerListFailure,
+    })
+  )
 }
 
 /** sagas are asynchronous workers (JS generators) to handle the state. */
-export function * saga () {
+export function* saga() {
   console.log('Config saga started.')
   yield takeEvery('SWISSTENNIS/FILE_LIST_REQUEST', fileList)
   yield takeEvery('SWISSTENNIS/DOWNLOAD_PLAYERLIST_REQUEST', downloadPlayerList)

+ 9 - 6
docker-compose.yml

@@ -1,7 +1,6 @@
-version: "2"
+version: '2'
 
 services:
-
   restserver:
     container_name: restserver
     build: ./server/
@@ -10,6 +9,8 @@ services:
       - '/usr/src/node_modules'
     ports:
       - '3002:3002'
+    depends_on:
+      - mongodb
 
   reactclient:
     container_name: reactclient
@@ -20,14 +21,16 @@ services:
     ports:
       - '3000:3000'
 
-  public:
-    container_name: public
+  proxy:
+    container_name: proxy
     image: nginx
     ports:
       - '8080:8080'
     volumes:
-      - './nginx/default.conf:/etc/nginx/conf.d/default.conf'
-      - './client/build:/usr/share/nginx/html'
+      - './nginx/nginx.conf:/etc/nginx/nginx.conf'
+    depends_on:
+      - reactclient
+      - restserver
 
   mongodb:
     container_name: database

+ 0 - 14
nginx/default.conf

@@ -1,14 +0,0 @@
-server {
-    listen       8080;
-    server_name  localhost;
-
-    location / {
-        root /usr/share/nginx/html;
-        try_files $uri /index.html;
-    }
-
-    error_page   500 502 503 504  /50x.html;
-    location = /50x.html {
-        root   /usr/share/nginx/html;
-    }
-}

+ 34 - 0
nginx/nginx.conf

@@ -0,0 +1,34 @@
+worker_processes 1;
+
+events {
+  worker_connections 1024;
+}
+
+http{
+  sendfile on;
+
+  server {
+    listen 8080;
+
+    location / {
+      proxy_pass_header  Set-Cookie;
+      proxy_set_header   Host $host;
+      proxy_set_header   X-Real-IP $remote_addr;
+      proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
+      proxy_set_header   X-Forwarded-Host $server_name;
+      proxy_pass      http://reactclient:3000;
+      proxy_redirect  off;
+    }
+
+    location /api {
+      proxy_pass_header  Set-Cookie;
+      proxy_set_header   Host $host;
+      proxy_set_header   X-Real-IP $remote_addr;
+      proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
+      proxy_set_header   X-Forwarded-Host $server_name;
+      proxy_pass      http://restserver:3002;
+      proxy_redirect  off;
+      client_max_body_size	100m;
+    }
+  }
+}

+ 1 - 0
server/.env

@@ -1,2 +1,3 @@
 DEBUG=SZTM*
 PORT=3002
+PASSWORD=q9oz2s26msl

+ 1 - 1
server/package.json

@@ -10,7 +10,7 @@
     "babel-preset-env": "^1.7.0",
     "babel-register": "^6.24.1",
     "base-64": "^0.1.0",
-    "bcrypt": "^2.0.0",
+    "bcrypt": "^5.0.1",
     "bhttp": "^1.2.4",
     "blob": "^0.0.4",
     "body-parser": "^1.17.2",

+ 15 - 14
server/src/restServer/config/sztm.js

@@ -1,21 +1,22 @@
 const places = {
-    AU: "auswärts",
-    UE: "TC Uetliberg",
-    EN: "TC Engematt",
-    BA: "TC Bachtobel",
-    FR: "TC Credit Suisse Frauenthal",
-    HA: "TC Hakoah",
-    UI: "TC Uitikon"
+  AU: 'auswärts',
+  Se: 'TC Seeblick',
+  Be: 'TC Belvoir',
+  Le: 'TC Leimbach',
+  So: 'TC Sonnenberg',
+  Oe: 'TC Oerlikon',
+  Fa: 'TC Fairplay',
+  Fr: 'Frauenthal',
 }
 
 const emails = {
-    UE: "info@sztm.ch",
-    EN: "engematt@sztm.ch",
-    BA: "bachtobel@sztm.ch",
-    FR: "cs@sztm.ch",
-    HA: "hakoah@sztm.ch",
-    UI: "uitikon@sztm.ch"
+  UE: 'info@sztm.ch',
+  EN: 'engematt@sztm.ch',
+  BA: 'bachtobel@sztm.ch',
+  FR: 'cs@sztm.ch',
+  HA: 'hakoah@sztm.ch',
+  UI: 'uitikon@sztm.ch',
 }
 
 export { places, emails }
-export default { places, emails }
+export default { places, emails }

+ 47 - 44
server/src/restServer/routes/config.js

@@ -4,60 +4,63 @@ import Config from '../models/config'
 const config = express.Router()
 
 config.post('/:key', async (req, res, next) => {
-    const { key } = req.params
-    const { value, description } = req.body
-    if (!value) {
-        const error = Error('Parameter value is mandatory.')
-        error.statusCode = 400
-        next(error)
-    }
-    try {
-        const configPair = new Config({ key, value, description })
-        await configPair.save()
-        res.json(configPair)
-    } catch (error) {
-        error.statusCode = 400
-        next(error)
-    }
+  const { key } = req.params
+  const { value, description } = req.body
+  console.log('set config', key, value, description)
+  if (!value) {
+    const error = Error('Parameter value is mandatory.')
+    error.statusCode = 400
+    next(error)
+  }
+  try {
+    const configPair = new Config({ key, value, description })
+    await configPair.save()
+    res.json(configPair)
+  } catch (error) {
+    error.statusCode = 400
+    next(error)
+  }
 })
 
 config.get('/:key?', async (req, res) => {
-    const { key } = req.params
-    const config = key ? await Config.findOne({ key }) : await Config.find().select({ key: 1, value: 1, description: 1 })
-    res.json({ config })
+  const { key } = req.params
+  const config = key
+    ? await Config.findOne({ key })
+    : await Config.find().select({ key: 1, value: 1, description: 1 })
+  res.json({ config })
 })
 
 config.put('/:key', async (req, res) => {
-    const { key } = req.params
-    const { value, description } = req.body
-    if (!value) {
-        const error = Error('Parameter value is mandatory.')
-        error.statusCode = 400
-        next(error)
-    }
-    const config = await Config.findOneAndUpdate({ key }, { value, description })
-    if (!config) {
-        const error = Error('Key not found.')
-        error.statusCode = 404
-        next(error)
-    }
-    res.json({...config, value, description})
+  const { key } = req.params
+  const { value, description } = req.body
+  if (!value) {
+    const error = Error('Parameter value is mandatory.')
+    error.statusCode = 400
+    next(error)
+  }
+  const config = await Config.findOneAndUpdate({ key }, { value, description })
+  if (!config) {
+    const error = Error('Key not found.')
+    error.statusCode = 404
+    next(error)
+  }
+  res.json({ ...config, value, description })
 })
 
 config.delete('/:key', async (req, res, next) => {
-    const { key } = req.params
-    const deleted = await Config.findOneAndRemove({ key })
-    if (deleted) {
-        res.json({ key })
-    } else {
-        const error = Error('Key not found.')
-        error.statusCode = 404
-        next(error)
-    }
+  const { key } = req.params
+  const deleted = await Config.findOneAndRemove({ key })
+  if (deleted) {
+    res.json({ key })
+  } else {
+    const error = Error('Key not found.')
+    error.statusCode = 404
+    next(error)
+  }
 })
 
-export async function listConfigs () {
-    return await Config.find()
+export async function listConfigs() {
+  return await Config.find()
 }
 
-export default config
+export default config

+ 290 - 185
server/src/restServer/routes/sztm.js

@@ -10,11 +10,11 @@ import Match from '../swisstennis/models/match'
 import MatchList from '../swisstennis/models/matchList'
 pdfMake.vfs = pdfFonts.pdfMake.vfs
 
-const sztm = express.Router() 
+const sztm = express.Router()
 
 sztm.get('/files', async (req, res) => {
   const dirContent = await awaitFs.readdir('sztm_files')
-  const fileList = dirContent.map(filename => {
+  const fileList = dirContent.map((filename) => {
     const stats = fs.statSync(`sztm_files/${filename}`)
     return { filename, size: stats.size, mtime: stats.mtime }
   })
@@ -34,187 +34,292 @@ sztm.delete('/files', async (req, res) => {
 })
 
 sztm.post('/zip', async (req, res) => {
-    try {
-        const { date } = req.body
-        if (!date) {
-            throw Error('date is mandatory.')
-        }
-        
-        const parsedDate = date.match(/(\d{4}\d{2}\d{2})T\d{2}\d{2}\d{2}/)[1]
-        const zipFile = `sztm_file_${parsedDate}.zip`
-        const zipStream = fs.createWriteStream(`sztm_files/${zipFile}`)
-        const archive = archiver('zip', { zlib: { level: 9 } })
-
-        const stream = new Promise((resolve, reject) => {
-            zipStream.on('close', resolve)
-            zipStream.on('error', reject)
-        })
-
-        archive.on('error', (error) => { throw error })
-        archive.pipe(zipStream)
-
-        archive.glob(`*-${parsedDate}.pdf`, { cwd: 'sztm_files/' })
-        archive.finalize()
-
-        await stream
-        const stats = fs.statSync(`sztm_files/${zipFile}`)
-        return res.json({ filename: zipFile, size: stats.size, mtime: stats.mtime })
-    } catch (error) {
-        return res.status(400).json({ msg: error.toString(), stack: error.stack })
+  try {
+    const { date } = req.body
+    if (!date) {
+      throw Error('date is mandatory.')
     }
+
+    const parsedDate = date.match(/(\d{4}\d{2}\d{2})T\d{2}\d{2}\d{2}/)[1]
+    const zipFile = `sztm_file_${parsedDate}.zip`
+    const zipStream = fs.createWriteStream(`sztm_files/${zipFile}`)
+    const archive = archiver('zip', { zlib: { level: 9 } })
+
+    const stream = new Promise((resolve, reject) => {
+      zipStream.on('close', resolve)
+      zipStream.on('error', reject)
+    })
+
+    archive.on('error', (error) => {
+      throw error
+    })
+    archive.pipe(zipStream)
+
+    archive.glob(`*-${parsedDate}.pdf`, { cwd: 'sztm_files/' })
+    archive.finalize()
+
+    await stream
+    const stats = fs.statSync(`sztm_files/${zipFile}`)
+    return res.json({ filename: zipFile, size: stats.size, mtime: stats.mtime })
+  } catch (error) {
+    return res.status(400).json({ msg: error.toString(), stack: error.stack })
+  }
 })
 
 sztm.post('/pdf', async (req, res) => {
-    try {
-        const { matchListId, date, place } = req.body
-        if (!matchListId || !date || !place) {
-            console.log('matchListId, date and place are mandatory.', req.body)
-            throw Error('matchListId, date and place are mandatory.')
-        }
-        
-        const matchList = await MatchList.findOne({ _id: matchListId })
-        const dateString = date.match(/(\d{4}\d{2}\d{2})T\d{2}\d{2}\d{2}/)[1]
-        const parsedDate = moment(dateString)
-        const query = { 
-            _id: { $in: matchList.matches },
-            place,
-            date: {
-                $gte: parsedDate.toDate(),
-                $lt: parsedDate.add(1, 'day').toDate()
-            }
-        }
-        
-        const matches = await Match.find(query).sort({ date: 1 }).populate('player1').populate('player2')
-        const players = []
-        const allPlaces = []
-        matches.forEach(match => {
-            console.log(match.idString, match.place)
-            if (match.player1) players.push(match.player1)
-            if (match.player2) players.push(match.player2)
-            if (match.place && !allPlaces.includes(match.place)) allPlaces.push(match.place)
-        })
-        
-        const tableRows = []
-        matches.filter(match => match.place && (match.place == place)).forEach(match => {
-            if (match.player1) tableRows.push([match.player1.paid ? 'OK' : '', match.category, moment(match.date).format('HH:mm'), `(${match.player1.junior ? '30.-' : '50.-'}) ${match.player1.fullName}`, ''])
-            if (match.player2) tableRows.push([match.player2.paid ? 'OK' : '', match.category, moment(match.date).format('HH:mm'), `(${match.player2.junior ? '30.-' : '50.-'}) ${match.player2.fullName}`, ''])
-        })
-        const paylistDoc = {
-            pageSize: 'A4',
-            pageOrientation: 'portrait',
-            content: [
-                { text: 'Stadtzürcher Tennismeisterschaft', style: 'header' },
-                { text: `Nenngelder für den ${parsedDate.subtract(1, 'day').format('DD.MM.YYYY')}`, style: 'subheader' },
-                { columns: [
-                    {text: places[place]},
-                    {text: '50.- oder 30.- (Junioren Jg. 2000 oder jünger)'}
-                ]},
-                { table: {
-                    headerRows: 1,
-                    widths: ['10%', '20%', '10%', '*', '15%'],
-                    heights: 30,
-                    body: [
-                        [{text: 'bezahlt', fillColor: '#eeeeee'}, 
-                            {text: 'Kategorie', fillColor: '#eeeeee'}, 
-                            {text: 'Zeit', fillColor: '#eeeeee'}, 
-                            {text: 'Name', fillColor: '#eeeeee'}, 
-                            {text: 'Betrag bez.', fillColor: '#eeeeee'}],
-                        ...tableRows
-                    ]},
-                    style: 'tableStyle'
-                },
-                { table: {
-                    dontBreakRows: true,
-                    widths: ['25%', '*', '25%', '25%'],
-                    body: [
-                        [{text: 'Datum', bold: true}, {text: '', colSpan: 3}, {}, {}],
-                        [{text: 'Turnierleiter', colSpan:2, margin: [0, 10, 0, 0], bold: true}, {}, 
-                            {text: 'Kassierer', colSpan:2, margin: [0, 10, 0, 0], bold: true}, {}],
-                        [{text: 'Betrag von Spielern erhalten', colSpan:2, margin: [0, 25, 0, 0]}, {}, 
-                            {text: 'Betrag von Turnierleiter erhalten', colSpan:2, margin: [0, 25, 0, 0]}, {}]
-                    ]},
-                    style: 'tableStyle'}
-                ],
-            styles: {
-                header: {
-                    fontSize: 22,
-                    bold: true,
-                    margin: [0, 20, 0, 8]
+  try {
+    const { matchListId, date, place } = req.body
+    if (!matchListId || !date || !place) {
+      console.log('matchListId, date and place are mandatory.', req.body)
+      throw Error('matchListId, date and place are mandatory.')
+    }
+
+    const matchList = await MatchList.findOne({ _id: matchListId })
+    const dateString = date.match(/(\d{4}\d{2}\d{2})T\d{2}\d{2}\d{2}/)[1]
+    const parsedDate = moment(dateString)
+    const query = {
+      _id: { $in: matchList.matches },
+      place,
+      date: {
+        $gte: parsedDate.toDate(),
+        $lt: parsedDate.add(1, 'day').toDate(),
+      },
+    }
+
+    const matches = await Match.find(query)
+      .sort({ date: 1 })
+      .populate('player1')
+      .populate('player2')
+    const players = []
+    const allPlaces = []
+    matches.forEach((match) => {
+      console.log(match.idString, match.place)
+      if (match.player1) players.push(match.player1)
+      if (match.player2) players.push(match.player2)
+      if (match.place && !allPlaces.includes(match.place))
+        allPlaces.push(match.place)
+    })
+
+    const tableRows = []
+    matches
+      .filter((match) => match.place && match.place == place)
+      .forEach((match) => {
+        if (match.player1)
+          tableRows.push([
+            match.player1.paid ? 'OK' : '',
+            match.category,
+            moment(match.date).format('HH:mm'),
+            `(${match.player1.junior ? '30.-' : '50.-'}) ${
+              match.player1.fullName
+            }`,
+            '',
+          ])
+        if (match.player2)
+          tableRows.push([
+            match.player2.paid ? 'OK' : '',
+            match.category,
+            moment(match.date).format('HH:mm'),
+            `(${match.player2.junior ? '30.-' : '50.-'}) ${
+              match.player2.fullName
+            }`,
+            '',
+          ])
+      })
+    const paylistDoc = {
+      pageSize: 'A4',
+      pageOrientation: 'portrait',
+      content: [
+        { text: 'Stadtzürcher Tennismeisterschaft', style: 'header' },
+        {
+          text: `Nenngelder für den ${parsedDate
+            .subtract(1, 'day')
+            .format('DD.MM.YYYY')}`,
+          style: 'subheader',
+        },
+        {
+          columns: [
+            { text: places[place] },
+            { text: '50.- oder 30.- (Junioren Jg. 2003 oder jünger)' },
+          ],
+        },
+        {
+          table: {
+            headerRows: 1,
+            widths: ['10%', '20%', '10%', '*', '15%'],
+            heights: 30,
+            body: [
+              [
+                { text: 'bezahlt', fillColor: '#eeeeee' },
+                { text: 'Kategorie', fillColor: '#eeeeee' },
+                { text: 'Zeit', fillColor: '#eeeeee' },
+                { text: 'Name', fillColor: '#eeeeee' },
+                { text: 'Betrag bez.', fillColor: '#eeeeee' },
+              ],
+              ...tableRows,
+            ],
+          },
+          style: 'tableStyle',
+        },
+        {
+          table: {
+            dontBreakRows: true,
+            widths: ['25%', '*', '25%', '25%'],
+            body: [
+              [{ text: 'Datum', bold: true }, { text: '', colSpan: 3 }, {}, {}],
+              [
+                {
+                  text: 'Turnierleiter',
+                  colSpan: 2,
+                  margin: [0, 10, 0, 0],
+                  bold: true,
                 },
-                subheader: {
-                    fontSize: 14,
-                    margin: [0, 15, 0, 12]
+                {},
+                {
+                  text: 'Kassierer',
+                  colSpan: 2,
+                  margin: [0, 10, 0, 0],
+                  bold: true,
                 },
-                tableStyle: {
-                    margin: [0, 15, 0, 5]
-                }
-            }
-        }
-    
-        const scheduleDoc = {
-            pageSize: 'A4',
-            pageOrientation: 'landscape',
-            header: {
-                stack: [
-                    { text: `Spielplan für den ${moment(parsedDate).format('DD.MM.YYYY')} (${places[place]})`, style: 'subheader' }
-                ],
-                margin: [40, 0]
-            },
-            content: [
-                { text: 'Stadtzürcher Tennismeisterschaft', style: 'header' },
-                { table: {
-                    headerRows: 1,
-                    widths: ['5%', '5%', '12%', '*', '5%', '*', '5%', '7%', '7%', '7%', '10%'],
-                    heights: 30,
-                    body: [
-                        [{text: 'Platz', fillColor: '#eeeeee'}, 
-                            {text: 'Zeit', fillColor: '#eeeeee'}, 
-                            {text: 'Kategorie', fillColor: '#eeeeee'}, 
-                            {text: 'Spieler 1', fillColor: '#eeeeee', colSpan: 2}, 
-                            {}, 
-                            {text: 'Spieler 2', fillColor: '#eeeeee', colSpan: 2}, 
-                            {}, 
-                            {text: '1. Satz', fillColor: '#eeeeee'}, 
-                            {text: '2. Satz', fillColor: '#eeeeee'}, 
-                            {text: '3. Satz', fillColor: '#eeeeee'}, 
-                            {text: 'WO Grund', fillColor: '#eeeeee'}],
-                        ...matches.filter(match => match.place && (match.place == place)).map((match) => {
-                            return ['', moment(match.date).format('HH:mm'), match.category, 
-                            match.player1 && match.player1.fullName, match.player1 && match.player1.ranking, 
-                            match.player2 && match.player2.fullName, match.player2 && match.player2.ranking, 
-                            '', '', '', '']
-                        })
-                    ]},
-                    style: 'tableStyle'
-                }],
-            styles: {
-                header: {
-                    fontSize: 22,
-                    bold: true,
-                    margin: [0, 20, 0, 8]
+                {},
+              ],
+              [
+                {
+                  text: 'Betrag von Spielern erhalten',
+                  colSpan: 2,
+                  margin: [0, 25, 0, 0],
                 },
-                subheader: {
-                    fontSize: 14,
-                    margin: [0, 15, 0, 12]
+                {},
+                {
+                  text: 'Betrag von Turnierleiter erhalten',
+                  colSpan: 2,
+                  margin: [0, 25, 0, 0],
                 },
-                tableStyle: {
-                    margin: [0, 15, 0, 5]
-                } 
-            }
-        }
-
-        const paylistGenerator = pdfMake.createPdf(paylistDoc)
-        const paylistFile = `Zahlliste-${place}-${dateString}.pdf`
-        paylistGenerator.getBuffer((buffer) => {
-            fs.writeFileSync(`sztm_files/${paylistFile}`, new Buffer(new Uint8Array(buffer)))
-        })
-        const scheduleGenerator = pdfMake.createPdf(scheduleDoc)
-        const scheduleFile = `Spielliste-${place}-${dateString}.pdf`
-        scheduleGenerator.getBuffer((buffer) => {
-            fs.writeFileSync(`sztm_files/${scheduleFile}`, new Buffer(new Uint8Array(buffer)))
-        })
-
-        /*
+                {},
+              ],
+            ],
+          },
+          style: 'tableStyle',
+        },
+      ],
+      styles: {
+        header: {
+          fontSize: 22,
+          bold: true,
+          margin: [0, 20, 0, 8],
+        },
+        subheader: {
+          fontSize: 14,
+          margin: [0, 15, 0, 12],
+        },
+        tableStyle: {
+          margin: [0, 15, 0, 5],
+        },
+      },
+    }
+
+    const scheduleDoc = {
+      pageSize: 'A4',
+      pageOrientation: 'landscape',
+      header: {
+        stack: [
+          {
+            text: `Spielplan für den ${moment(parsedDate).format(
+              'DD.MM.YYYY'
+            )} (${places[place]})`,
+            style: 'subheader',
+          },
+        ],
+        margin: [40, 0],
+      },
+      content: [
+        { text: 'Stadtzürcher Tennismeisterschaft', style: 'header' },
+        {
+          table: {
+            headerRows: 1,
+            widths: [
+              '5%',
+              '5%',
+              '12%',
+              '*',
+              '5%',
+              '*',
+              '5%',
+              '7%',
+              '7%',
+              '7%',
+              '10%',
+            ],
+            heights: 30,
+            body: [
+              [
+                { text: 'Platz', fillColor: '#eeeeee' },
+                { text: 'Zeit', fillColor: '#eeeeee' },
+                { text: 'Kategorie', fillColor: '#eeeeee' },
+                { text: 'Spieler 1', fillColor: '#eeeeee', colSpan: 2 },
+                {},
+                { text: 'Spieler 2', fillColor: '#eeeeee', colSpan: 2 },
+                {},
+                { text: '1. Satz', fillColor: '#eeeeee' },
+                { text: '2. Satz', fillColor: '#eeeeee' },
+                { text: '3. Satz', fillColor: '#eeeeee' },
+                { text: 'WO Grund', fillColor: '#eeeeee' },
+              ],
+              ...matches
+                .filter((match) => match.place && match.place == place)
+                .map((match) => {
+                  return [
+                    '',
+                    moment(match.date).format('HH:mm'),
+                    match.category,
+                    match.player1 && match.player1.fullName,
+                    match.player1 && match.player1.ranking,
+                    match.player2 && match.player2.fullName,
+                    match.player2 && match.player2.ranking,
+                    '',
+                    '',
+                    '',
+                    '',
+                  ]
+                }),
+            ],
+          },
+          style: 'tableStyle',
+        },
+      ],
+      styles: {
+        header: {
+          fontSize: 22,
+          bold: true,
+          margin: [0, 20, 0, 8],
+        },
+        subheader: {
+          fontSize: 14,
+          margin: [0, 15, 0, 12],
+        },
+        tableStyle: {
+          margin: [0, 15, 0, 5],
+        },
+      },
+    }
+
+    const paylistGenerator = pdfMake.createPdf(paylistDoc)
+    const paylistFile = `Zahlliste-${place}-${dateString}.pdf`
+    paylistGenerator.getBuffer((buffer) => {
+      fs.writeFileSync(
+        `sztm_files/${paylistFile}`,
+        new Buffer(new Uint8Array(buffer))
+      )
+    })
+    const scheduleGenerator = pdfMake.createPdf(scheduleDoc)
+    const scheduleFile = `Spielliste-${place}-${dateString}.pdf`
+    scheduleGenerator.getBuffer((buffer) => {
+      fs.writeFileSync(
+        `sztm_files/${scheduleFile}`,
+        new Buffer(new Uint8Array(buffer))
+      )
+    })
+
+    /*
         const payListStats = fs.statSync(`sztm_files/${paylistFile}`)
         const scheduleStats = fs.statSync(`sztm_files/${scheduleFile}`)
         
@@ -223,15 +328,15 @@ sztm.post('/pdf', async (req, res) => {
             paylistFile: {filename: paylistFile, size: payListStats.size, mtime: payListStats.mtime}, 
             scheduleFile: {filename: scheduleFile, size: scheduleStats.size, mtime: scheduleStats.mtime} 
         }) */
-        
-        res.json({ 
-            place,
-            paylistFile: {filename: paylistFile, size: 0, mtime: new Date()}, 
-            scheduleFile: {filename: scheduleFile, size: 0, mtime: new Date()} 
-        })
-    } catch (error) {
-        return res.status(400).json({ msg: error.toString(), stack: error.stack })
-    }
+
+    res.json({
+      place,
+      paylistFile: { filename: paylistFile, size: 0, mtime: new Date() },
+      scheduleFile: { filename: scheduleFile, size: 0, mtime: new Date() },
+    })
+  } catch (error) {
+    return res.status(400).json({ msg: error.toString(), stack: error.stack })
+  }
 })
 
-export default sztm
+export default sztm

+ 17 - 9
server/src/restServer/swisstennis/index.js

@@ -15,15 +15,19 @@ export const swisstennis = express.Router()
 // Use this variable for the session
 export const session = bhttp.session()
 
-async function checkLogin () {
-  const myTournamentsPage = await session.get('https://comp.swisstennis.ch/advantage/servlet/MyTournamentList?Lang=D')
+async function checkLogin() {
+  const myTournamentsPage = await session.get(
+    'https://comp.swisstennis.ch/advantage/servlet/MyTournamentList?Lang=D'
+  )
   const strMyTournamentsPage = myTournamentsPage.body.toString()
-  return strMyTournamentsPage.includes('Login-Zone') ? null : strMyTournamentsPage
+  return strMyTournamentsPage.includes('Login-Zone')
+    ? null
+    : strMyTournamentsPage
 }
 
 /*
-* Define the Routes
-*/
+ * Define the Routes
+ */
 
 // Login
 swisstennis.post('/login', async (req, res) => {
@@ -34,20 +38,24 @@ swisstennis.post('/login', async (req, res) => {
     }
     const username = req.body.username || configDb.tournament.value
     const password = req.body.password || configDb.tournamentPW.value
-    
+    console.log('Swisstennis login', username, password)
+
     // return, if username or password are missing
     if (!username || !password) {
       throw Error('Parameters username and password are required')
     }
-    
+
     // assemble the login data
     const loginData = {
       Lang: 'D',
       id: username,
       pwd: password,
-      Tournament: ''
+      Tournament: '',
     }
-    const loginPage = await session.post('https://comp.swisstennis.ch/advantage/servlet/Login', loginData)
+    const loginPage = await session.post(
+      'https://comp.swisstennis.ch/advantage/servlet/Login',
+      loginData
+    )
     const strLoginPage = loginPage.body.toString()
     if (strLoginPage.includes('Zugriff verweigert')) {
       throw Error('Access denied!')

+ 131 - 97
server/src/restServer/swisstennis/routes/calendar.js

@@ -8,126 +8,160 @@ import { normalize } from './helpers'
 import { session } from '../index'
 import { configDb } from '../../api'
 
+async function getCalendars(req, res) {
+  try {
+    // Calendar list from database. Unly query certain fields.
+    const calendars = await MatchList.find().select({
+      _id: 1,
+      imported: 1,
+      file: 1,
+      fileSize: 1,
+    })
+    return res.json(calendars)
+  } catch (error) {
+    return res.status(400).json({ msg: error.toString() })
+  }
+}
 
-async function getCalendars (req, res) {
-    try {
-        // Calendar list from database. Unly query certain fields.
-        const calendars = await MatchList.find().select({_id: 1, imported: 1, file: 1, fileSize: 1})
-        return res.json(calendars)
-    } catch (error) {
-        return res.status(400).json({ msg: error.toString() })
-    }
+async function getCalendar(req, res) {
+  try {
+    const key = req.query.calendarId ? { _id: req.query.calendarId } : {}
+    const calendar = await MatchList.findOne(key)
+      .sort({ imported: -1 })
+      .populate('matches')
+    await Promise.all(
+      calendar.matches.map(async (match) => {
+        await Player.populate(match, 'player1')
+        await Player.populate(match, 'player2')
+      })
+    )
+    return res.json(calendar)
+  } catch (error) {
+    return res.status(400).json({ msg: error.toString() })
+  }
 }
 
-async function getCalendar (req, res) {
-    try {
-        const key = req.query.calendarId ? { _id: req.query.calendarId } : {}
-        const calendar = await MatchList.findOne(key).sort({ imported: -1 }).populate('matches')
-        await Promise.all(calendar.matches.map(async match => {
-            await Player.populate(match, 'player1')
-            await Player.populate(match, 'player2')
-        }))
-        return res.json(calendar)
-    } catch (error) {
-        return res.status(400).json({ msg: error.toString() })
-    }
+async function downloadCalendar(req, res) {
+  const tournament = req.query.tournament || configDb.tournamentId.value
+
+  if (!tournament) {
+    return res.json({ msg: 'No tournament given.' })
+  }
+  try {
+    const filename = `Calendar-${tournament}-${moment().format(
+      'YYYYMMDDTHHmmss'
+    )}.xls`
+    const calendarFile = fs.createWriteStream(`swisstennis_files/${filename}`)
+    const calendar = await session.get(
+      `https://comp.swisstennis.ch/advantage/servlet/Calendar.xls?Lang=D&tournament=Id${tournament}&Type=Match&Inp_DateRangeFilter.fromDate=07.07.2021&Inp_DateRangeFilter.toDate=18.07.2021`,
+      { stream: true }
+    )
+    await calendar.pipe(calendarFile)
+    const streamDone = new Promise((resolve, reject) => {
+      calendarFile.on('finish', resolve)
+      calendarFile.on('error', reject)
+    })
+    await streamDone
+    const stats = fs.statSync(`swisstennis_files/${filename}`)
+    return res.json({ filename, size: stats.size, mtime: stats.mtime })
+  } catch (error) {
+    return res.status(400).json({ msg: error.toString() })
+  }
 }
 
-async function downloadCalendar (req, res) {
-    const tournament = req.query.tournament || configDb.tournamentId.value
-    
-    if (!tournament) {
-        return res.json({ msg: 'No tournament given.' })
+async function parseCalendar(req, res) {
+  try {
+    console.log('Parsing file', req.params.filename)
+
+    const filePath = `swisstennis_files/${req.params.filename}`
+    const dateElems = req.params.filename
+      .match(/(\d{4})(\d{2})(\d{2})T(\d{2})(\d{2})(\d{2})/)
+      .slice(1, 7)
+    const fileDate = new Date(
+      `${dateElems[0]}-${dateElems[1]}-${dateElems[2]} ${dateElems[3]}:${dateElems[4]}:${dateElems[5]}`
+    )
+    const stat = fs.statSync(filePath)
+
+    const fileNewer = await MatchList.find({ imported: { $gte: fileDate } })
+    if (fileNewer && fileNewer.length) {
+      throw Error('File has to be newer.')
     }
-    try {
-        const filename = `Calendar-${tournament}-${moment().format('YYYYMMDDTHHmmss')}.xls`
-        const calendarFile = fs.createWriteStream(`swisstennis_files/${filename}`)
-        const calendar = await session.get(`https://comp.swisstennis.ch/advantage/servlet/Calendar.xls?Lang=D&tournament=Id${tournament}&Type=Match&Inp_DateRangeFilter.fromDate=04.06.2018&Inp_DateRangeFilter.toDate=16.09.2018`, {stream: true})
-        await calendar.pipe(calendarFile)
-        const streamDone = new Promise((resolve, reject) => {
-            calendarFile.on('finish', resolve)
-            calendarFile.on('error', reject)
-        })
-        await streamDone
-        const stats = fs.statSync(`swisstennis_files/${filename}`)
-        return res.json({ filename, size: stats.size, mtime: stats.mtime })
-    } catch (error) {
-        return res.status(400).json({ msg: error.toString() })
+
+    console.log('About to read the calendar list.')
+    const worksheets = await Excel.readWorkbook(
+      `swisstennis_files/${req.params.filename}`
+    )
+    const worksheet = worksheets.Sheet1
+    console.log('DEBUG: worksheet', worksheet)
+    if ((worksheet[2].length < 8) | (worksheet[2].length > 9)) {
+      throw Error(`Wrong file structure.`)
     }
-}
 
-async function parseCalendar (req,res) {
-    try {
-        console.log('Parsing file', req.params.filename)
-        
-        const filePath = `swisstennis_files/${req.params.filename}`
-        const dateElems = req.params.filename.match(/(\d{4})(\d{2})(\d{2})T(\d{2})(\d{2})(\d{2})/).slice(1,7)
-        const fileDate = new Date(`${dateElems[0]}-${dateElems[1]}-${dateElems[2]} ${dateElems[3]}:${dateElems[4]}:${dateElems[5]}`)
-        const stat = fs.statSync(filePath)
-        
-        const fileNewer = await MatchList.find({ imported: { $gte: fileDate } })
-        if (fileNewer.length) {
-            throw Error('File has to be newer.')
+    const allMatches = await Promise.all(
+      worksheet.slice(2, worksheet.length).map(async (data, key) => {
+        const fileMatch = {
+          created: fileDate,
+          fileLine: key,
+          category: normalize(data[3]),
+          place: normalize(data[0]),
+          date: data[1],
+          result: normalize(data[8]),
         }
-        
-        console.log('About to read the calendar list.')    
-        const worksheets = await Excel.readWorkbook(`swisstennis_files/${req.params.filename}`)
-        const worksheet = worksheets.Sheet1
-        if (worksheet[2].length < 8 | worksheet[2].length > 9) {
-            throw Error(`Wrong file structure.`)
-        }
-        
-        const allMatches = await Promise.all(worksheet.slice(2, worksheet.length).map(async (data, key) => {
-            const fileMatch = {
-                created: fileDate,
-                fileLine: key,
-                category: normalize(data[3]),
-                place: normalize(data[0]),
-                date: data[1],
-                result: normalize(data[8]),
-            }
         fileMatch.doubles = !!fileMatch.category.match(/DM.*|[MW]D.*/)
-        const player1 = await Player.findOne({idString: `${fileMatch.category} % ${normalize(data[4])}`}).sort({created: -1})
-        const player2 = await Player.findOne({idString: `${fileMatch.category} % ${normalize(data[6])}`}).sort({created: -1})
-        fileMatch.idString = `${fileMatch.category} % ${player1 ? player1.idString : `${key}_1`} - ${player2 ? player2.idString : `${key}_2`}`
+        const player1 = await Player.findOne({
+          idString: `${fileMatch.category} % ${normalize(data[4])}`,
+        }).sort({ created: -1 })
+        const player2 = await Player.findOne({
+          idString: `${fileMatch.category} % ${normalize(data[6])}`,
+        }).sort({ created: -1 })
+        fileMatch.idString = `${fileMatch.category} % ${
+          player1 ? player1.idString : `${key}_1`
+        } - ${player2 ? player2.idString : `${key}_2`}`
         fileMatch.player1 = player1 ? player1._id : null
         fileMatch.player2 = player2 ? player2._id : null
-        
+
         const match = new Match(fileMatch)
         await match.save()
         return match._id
-    }))
+      })
+    )
     const matchList = new MatchList({
-        imported: fileDate,
-        file: req.params.filename,
-        fileSize: stat.size,
-        matches: allMatches
+      imported: fileDate,
+      file: req.params.filename,
+      fileSize: stat.size,
+      matches: allMatches,
     })
     await matchList.save()
-    
+
     return res.json({ matchList })
-} catch (error) {
+  } catch (error) {
     res.status(400).json({ msg: error.toString() })
-}
+  }
 }
 
 // Delete a calendar
-async function deleteCalendar (req, res) {
-    try {
-        const { calendarId } = req.params
-        
-        if (!calendarId) {
-            throw Error('No calendarId given.')
-        }
-        const calendar = await MatchList.findOne({ _id: calendarId })
-        if (!calendar) {
-            throw Error('Calendar not found.')
-        }
-        calendar.remove()
-        return res.json({ _id: calendarId })
-    } catch (error) {
-        return res.status(400).json({ msg: error.toString() })
+async function deleteCalendar(req, res) {
+  try {
+    const { calendarId } = req.params
+
+    if (!calendarId) {
+      throw Error('No calendarId given.')
     }
+    const calendar = await MatchList.findOne({ _id: calendarId })
+    if (!calendar) {
+      throw Error('Calendar not found.')
+    }
+    calendar.remove()
+    return res.json({ _id: calendarId })
+  } catch (error) {
+    return res.status(400).json({ msg: error.toString() })
+  }
 }
 
-export default { getCalendars, getCalendar, downloadCalendar, parseCalendar, deleteCalendar }
+export default {
+  getCalendars,
+  getCalendar,
+  downloadCalendar,
+  parseCalendar,
+  deleteCalendar,
+}

+ 169 - 124
server/src/restServer/swisstennis/routes/playerList.js

@@ -7,153 +7,198 @@ import { normalize, normalizePhone } from './helpers'
 import { session } from '../index'
 import { configDb } from '../../api'
 
+async function getPlayerLists(req, res) {
+  try {
+    const playerLists = await PlayerList.find().select({
+      _id: 1,
+      imported: 1,
+      file: 1,
+      filesize: 1,
+    })
+    return res.json(playerLists)
+  } catch (error) {
+    return res.status(400).json({ msg: error.toString() })
+  }
+}
 
-async function getPlayerLists (req, res) {
-    try {
-        const playerLists = await PlayerList.find().select({_id: 1, imported: 1, file: 1, filesize: 1})
-        return res.json(playerLists)
-    } catch (error) {
-        return res.status(400).json({ msg: error.toString() })
-    }
+async function getPlayerList(req, res) {
+  try {
+    const key = req.query.playerlistId ? { _id: req.query.playerlistId } : {}
+    const playerList = await PlayerList.findOne(key)
+      .sort({ imported: -1 })
+      .populate('players')
+    return res.json(playerList)
+  } catch (error) {
+    return res.status(400).json({ msg: error.toString() })
+  }
 }
 
-async function getPlayerList (req, res) {
-    try {
-        const key = req.query.playerlistId ? { _id: req.query.playerlistId } : {}
-        const playerList = await PlayerList.findOne(key).sort({ imported: -1 }).populate('players')
-        return res.json(playerList)
-    } catch (error) {
-        return res.status(400).json({ msg: error.toString() })
+async function downloadPlayerList(req, res) {
+  try {
+    const tournament = req.query.tournament || configDb.tournamentId.value
+
+    if (!tournament) {
+      throw Error('No tournament given.')
     }
+    const filename = `PlayerList-${tournament}-${moment().format(
+      'YYYYMMDDTHHmmss'
+    )}.xls`
+    const playerListFile = fs.createWriteStream(`swisstennis_files/${filename}`)
+    const playerList = await session.get(
+      `https://comp.swisstennis.ch/advantage/servlet/PlayerList.xls?tournament=Id${tournament}&lang=D`,
+      { stream: true }
+    )
+    playerList.pipe(playerListFile)
+    const streamDone = new Promise((resolve, reject) => {
+      playerListFile.on('finish', resolve)
+      playerListFile.on('error', reject)
+    })
+    await streamDone
+    const stats = fs.statSync(`swisstennis_files/${filename}`)
+    return res.json({ filename, size: stats.size, mtime: stats.mtime })
+  } catch (error) {
+    return res.status(400).json({ msg: error.toString() })
+  }
 }
 
-async function downloadPlayerList (req, res) {
-    try {
-        const tournament = req.query.tournament || configDb.tournamentId.value
-        
-        if (!tournament) {
-            throw Error('No tournament given.')
-        }
-        const filename = `PlayerList-${tournament}-${moment().format('YYYYMMDDTHHmmss')}.xls`
-        const playerListFile = fs.createWriteStream(`swisstennis_files/${filename}`)
-        const playerList = await session.get(`https://comp.swisstennis.ch/advantage/servlet/PlayerList.xls?tournament=Id${tournament}&lang=D`, {stream: true})
-        playerList.pipe(playerListFile)
-        const streamDone = new Promise((resolve, reject) => {
-            playerListFile.on('finish', resolve)
-            playerListFile.on('error', reject)
-        })
-        await streamDone
-        const stats = fs.statSync(`swisstennis_files/${filename}`)
-        return res.json({ filename, size: stats.size, mtime: stats.mtime })
-    } catch (error) {
-        return res.status(400).json({ msg: error.toString() })
+async function parsePlayerList(req, res) {
+  try {
+    console.log('Parsing file', req.params.filename)
+
+    const filePath = `swisstennis_files/${req.params.filename}`
+    const dateString = req.params.filename.match(
+      /(\d{4}\d{2}\d{2}T\d{2}\d{2}\d{2})/
+    )[1]
+    const fileDate = moment(dateString)
+    const stat = fs.statSync(filePath)
+
+    const fileNewer = await PlayerList.find({
+      imported: { $gte: fileDate.toDate() },
+    })
+    if (fileNewer.length) {
+      throw Error('File has to be newer.')
     }
-}
 
-async function parsePlayerList (req, res) {
-    try {
-        console.log('Parsing file', req.params.filename)
-        
-        const filePath = `swisstennis_files/${req.params.filename}`
-        const dateString = req.params.filename.match(/(\d{4}\d{2}\d{2}T\d{2}\d{2}\d{2})/)[1]
-        const fileDate = moment(dateString)
-        const stat = fs.statSync(filePath)
-        
-        const fileNewer = await PlayerList.find({ imported: { $gte: fileDate.toDate() } })
-        if (fileNewer.length) {
-            throw Error('File has to be newer.')
-        }
-        
-        console.log('About to read the player list.', filePath)
-        const worksheets = await Excel.readWorkbook(filePath)
-        const worksheet = worksheets.Players
-        if (worksheet[4].length !== 32 & worksheet[3][0] !== 'Konkurrenz' & worksheet[3][31] !== 'bezahlt') {
-            throw Error(`Wrong file structure. Length: ${worksheet[4].length} (expected 32), Column A name: ${worksheet[3][0]} (expected Konkurrenz).`)
+    console.log('About to read the player list.', filePath)
+    const worksheets = await Excel.readWorkbook(filePath)
+    const worksheet = worksheets.Players
+    if (
+      (worksheet[4].length !== 32) &
+      (worksheet[3][0] !== 'Konkurrenz') &
+      (worksheet[3][31] !== 'bezahlt')
+    ) {
+      throw Error(
+        `Wrong file structure. Length: ${worksheet[4].length} (expected 32), Column A name: ${worksheet[3][0]} (expected Konkurrenz).`
+      )
+    }
+
+    const headers = worksheet.slice(3, 4)
+
+    const allPlayers = await Promise.all(
+      worksheet.slice(4, worksheet.length).map(async (data) => {
+        const filePlayer = {
+          created: fileDate.toDate(),
+          category: normalize(data[0]),
+          licenseNr: normalize(data[2]),
+          name: normalize(data[5]),
+          firstName: normalize(data[6]),
+          nameDP: normalize(data[24]),
+          firstNameDP: normalize(data[25]),
+          birthDate: data[7] || null,
+          email: normalize(data[16]),
+          ranking: normalize(data[17]),
+          licenseNrDP: normalize(data[23]),
+          phonePrivate: normalizePhone(data[13]),
+          phoneWork: normalizePhone(data[14]),
+          phoneMobile: normalizePhone(data[15]),
+          birthDateDP: data[26] || null,
+          rankingDP: normalize(data[27]),
+          confirmed: !!data[31],
+          paid: !!data[33],
         }
-        
-        const headers = worksheet.slice(3, 4)
-        
-        const allPlayers = await Promise.all(worksheet.slice(4, worksheet.length).map(async data => {
-            const filePlayer = {
-                created: fileDate.toDate(),
-                category: normalize(data[0]),
-                licenseNr: normalize(data[2]),
-                name: normalize(data[5]),
-                firstName: normalize(data[6]), 
-                nameDP: normalize(data[24]),
-                firstNameDP: normalize(data[25]),
-                birthDate: data[7] || null,
-                email: normalize(data[16]),
-                ranking: normalize(data[17]),
-                licenseNrDP: normalize(data[23]),
-                phonePrivate: normalizePhone(data[13]),
-                phoneWork: normalizePhone(data[14]),
-                phoneMobile: normalizePhone(data[15]),
-                birthDateDP: data[26] || null,
-                rankingDP: normalize(data[27]),
-                confirmed: !!data[29],
-                paid: !!data[31],
-            }
-            
-            filePlayer.gender = filePlayer.category ? 
-            filePlayer.category[0] === 'M' ? 'm' : 
-            filePlayer.category[0] === 'W' ? 
-            'w' : 
-            null : 
-            null,
-        filePlayer.doubles = !!filePlayer.category.match(/DM.*|[MW]D.*/)
-        filePlayer.junior = filePlayer.birthDate ? 
-        filePlayer.birthDate.getTime() >= (new Date((new Date()).getFullYear() - 19, 11, 31, 23, 59, 59, 999)).getTime() : 
-        false
-        filePlayer.fullName = filePlayer.doubles ? 
-        `${filePlayer.name} ${filePlayer.firstName} / ${filePlayer.nameDP} ${filePlayer.firstNameDP}` : 
-        `${filePlayer.name} ${filePlayer.firstName}`
+
+        ;(filePlayer.gender = filePlayer.category
+          ? filePlayer.category[0] === 'M'
+            ? 'm'
+            : filePlayer.category[0] === 'W'
+            ? 'w'
+            : null
+          : null),
+          (filePlayer.doubles = !!filePlayer.category.match(/DM.*|[MW]D.*/))
+        filePlayer.junior = filePlayer.birthDate
+          ? filePlayer.birthDate.getTime() >=
+            new Date(
+              new Date().getFullYear() - 19,
+              11,
+              31,
+              23,
+              59,
+              59,
+              999
+            ).getTime()
+          : false
+        filePlayer.fullName = filePlayer.doubles
+          ? `${filePlayer.name} ${filePlayer.firstName} / ${filePlayer.nameDP} ${filePlayer.firstNameDP}`
+          : `${filePlayer.name} ${filePlayer.firstName}`
         filePlayer.idString = `${filePlayer.category} % ${filePlayer.fullName}`
         filePlayer.phone = null
         const reMobile = /^\+417/
         if (filePlayer.phoneWork && filePlayer.phoneWork.match(reMobile)) {
-            filePlayer.phone = filePlayer.phoneWork
-        } else if (filePlayer.phonePrivate && filePlayer.phonePrivate.match(reMobile)) {
-            filePlayer.phone = filePlayer.phonePrivate 
-        } else if (filePlayer.phoneMobile && filePlayer.phoneMobile.match(reMobile)) {
-            filePlayer.phone = filePlayer.phoneMobile
+          filePlayer.phone = filePlayer.phoneWork
+        } else if (
+          filePlayer.phonePrivate &&
+          filePlayer.phonePrivate.match(reMobile)
+        ) {
+          filePlayer.phone = filePlayer.phonePrivate
+        } else if (
+          filePlayer.phoneMobile &&
+          filePlayer.phoneMobile.match(reMobile)
+        ) {
+          filePlayer.phone = filePlayer.phoneMobile
         }
-        
+
         const player = new Player(filePlayer)
         player.save()
         return player._id
-    }))
-    
+      })
+    )
+
     const playerList = new PlayerList({
-        imported: fileDate,
-        file: req.params.filename,
-        fileSize: stat.size,
-        players: allPlayers
+      imported: fileDate,
+      file: req.params.filename,
+      fileSize: stat.size,
+      players: allPlayers,
     })
     await playerList.save()
-    
-    return res.json({playerList})
-} catch (error) {
+
+    return res.json({ playerList })
+  } catch (error) {
     return res.status(400).json({ msg: error.toString() })
-}
+  }
 }
 
-async function deletePlayerList (req, res) {
-    try {
-        const { playerlistId } = req.params
-        
-        if (!playerlistId) {
-            throw Error('No playerlistId given.')
-        }
-        const playerList = await PlayerList.findOne({ _id: playerlistId })
-        if (!playerList) {
-            throw Error('PlayerList not found.')
-        }
-        playerList.remove()
-        return res.json({ _id: playerlistId })
-    } catch (error) {
-        return res.status(400).json({ msg: error.toString() })
+async function deletePlayerList(req, res) {
+  try {
+    const { playerlistId } = req.params
+
+    if (!playerlistId) {
+      throw Error('No playerlistId given.')
     }
+    const playerList = await PlayerList.findOne({ _id: playerlistId })
+    if (!playerList) {
+      throw Error('PlayerList not found.')
+    }
+    playerList.remove()
+    return res.json({ _id: playerlistId })
+  } catch (error) {
+    return res.status(400).json({ msg: error.toString() })
+  }
 }
 
-export default { getPlayerLists, getPlayerList, downloadPlayerList, parsePlayerList, deletePlayerList }
+export default {
+  getPlayerLists,
+  getPlayerList,
+  downloadPlayerList,
+  parsePlayerList,
+  deletePlayerList,
+}

Nem az összes módosított fájl került megjelenítésre, mert túl sok fájl változott