Kaynağa Gözat

worked on implementing redux.

Tomi Cvetic 7 yıl önce
ebeveyn
işleme
427efe1706

+ 1 - 0
package.json

@@ -11,6 +11,7 @@
     "react-dom": "^15.5.4",
     "react-redux": "^5.0.5",
     "redux": "^3.6.0",
+    "redux-saga": "^0.15.4",
     "xlsx": "^0.10.4"
   },
   "devDependencies": {

+ 8 - 8
src/Main.js

@@ -1,8 +1,8 @@
 import React from 'react'           // React to manage the GUI
 import Player from './classes/player'
 import Match from './classes/match'
-import PlayerList from './playerList'       // Everything that has to do with players
-import MatchList from './matchList'         // Everything that has to do with matches
+import playerList from './playerList'       // Everything that has to do with players
+import matchList from './matchList'         // Everything that has to do with matches
 import Excel from './excel'         // Helper files to create Excel files
 import { date2s, time2s } from './helpers'
 import 'bootstrap/dist/css/bootstrap.css'
@@ -106,9 +106,9 @@ class Main extends React.Component {
 
   generatePlayerList (worksheet) {
     console.log('About to read the player list.')
-    const worksheets = { ...this.state.worksheets }
+    /* const worksheets = { ...this.state.worksheets }
     worksheets['PlayerList'] = worksheet
-    this.setState({ worksheets })
+    this.setState({ worksheets }) */
 
     if (worksheet[4].length !== 32 & worksheet[3][0] !== 'Konkurrenz' & worksheet[3][31] !== 'bezahlt') {
       throw Error('Wrong file structure.')
@@ -359,8 +359,8 @@ class Main extends React.Component {
   }
 
   render () {
-    const Pli = PlayerList.components.PlayerList
-    const Mli = MatchList.components.MatchList
+    const PlayerTable = playerList.components.PlayerTable
+    const MatchTable = matchList.components.MatchTable
     const places = this.props.matchList.places || []
     const dates = this.props.matchList.dates || {}
     const matchCategories = this.props.matchList.categories || []
@@ -409,10 +409,10 @@ class Main extends React.Component {
           <button onClick={this.generatePayTable} disabled={(!this.props.matchList.filteredMatches.length | !this.props.playerList.filteredPlayers.length)}>Zahlliste generieren</button>
           <button onClick={this.generatePhoneList} disabled={!this.props.playerList.allPlayers.length}>Telefonliste generieren</button>
         </form>
-        <Mli match={this.props.matchList} />
+        <MatchTable match={this.props.matchList} />
         <PhoneList filtered={this.props.matchList.filteredMatches} players={this.props.playerList.allPlayers} />
         <EmailList filtered={this.props.matchList.filteredMatches} players={this.props.playerList.allPlayers} />
-        <Pli player={this.props.playerList} />
+        <PlayerTable player={this.props.playerList} />
       </div>
     )
   }

+ 0 - 109
src/excel/SZTM_Zahlliste.bas

@@ -1,109 +0,0 @@
-Attribute VB_Name = "Module1"
-Sub SZTM_Zahlliste()
-Attribute SZTM_Zahlliste.VB_Description = "Formatiert die Zahlliste"
-Attribute SZTM_Zahlliste.VB_ProcData.VB_Invoke_Func = "F\n14"
-'
-' SZTM_Zahlliste Macro
-' Formatiert die Zahlliste
-'
-' Keyboard Shortcut: Ctrl+Shift+F
-'
-    Dim ws As Worksheet
-    For Each ws In ActiveWorkbook.Sheets
-        With ws
-        
-            ws.Activate
-                
-            Range("A3:F3").Select
-            With Selection.Interior
-                .Pattern = xlSolid
-                .PatternColorIndex = xlAutomatic
-                .ThemeColor = xlThemeColorDark1
-                .TintAndShade = -0.149998474074526
-                .PatternTintAndShade = 0
-            End With
-            Selection.Font.Bold = True
-            Range("A1:F1").Select
-            With Selection
-                .HorizontalAlignment = xlLeft
-                .VerticalAlignment = xlBottom
-                .WrapText = False
-                .Orientation = 0
-                .AddIndent = False
-                .IndentLevel = 0
-                .ShrinkToFit = False
-                .ReadingOrder = xlContext
-                .MergeCells = True
-            End With
-            Selection.Merge
-            With Selection.Font
-                .Name = "Calibri"
-                .Size = 14
-                .Strikethrough = False
-                .Superscript = False
-                .Subscript = False
-                .OutlineFont = False
-                .Shadow = False
-                .Underline = xlUnderlineStyleNone
-                .ThemeColor = xlThemeColorLight1
-                .TintAndShade = 0
-                .ThemeFont = xlThemeFontMinor
-            End With
-            Selection.Font.Bold = True
-            ActiveWindow.SmallScroll Down:=-3
-            Range("A3").Select
-            Range(Selection, ActiveCell.SpecialCells(xlLastCell)).Select
-            Selection.Borders(xlDiagonalDown).LineStyle = xlNone
-            Selection.Borders(xlDiagonalUp).LineStyle = xlNone
-            With Selection.Borders(xlEdgeLeft)
-                .LineStyle = xlContinuous
-                .ColorIndex = 0
-                .TintAndShade = 0
-                .Weight = xlThin
-            End With
-            With Selection.Borders(xlEdgeTop)
-                .LineStyle = xlContinuous
-                .ColorIndex = 0
-                .TintAndShade = 0
-                .Weight = xlThin
-            End With
-            With Selection.Borders(xlEdgeBottom)
-                .LineStyle = xlContinuous
-                .ColorIndex = 0
-                .TintAndShade = 0
-                .Weight = xlThin
-            End With
-            With Selection.Borders(xlEdgeRight)
-                .LineStyle = xlContinuous
-                .ColorIndex = 0
-                .TintAndShade = 0
-                .Weight = xlThin
-            End With
-            With Selection.Borders(xlInsideVertical)
-                .LineStyle = xlContinuous
-                .ColorIndex = 0
-                .TintAndShade = 0
-                .Weight = xlThin
-            End With
-            With Selection.Borders(xlInsideHorizontal)
-                .LineStyle = xlContinuous
-                .ColorIndex = 0
-                .TintAndShade = 0
-                .Weight = xlThin
-            End With
-            Columns("A:A").EntireColumn.AutoFit
-            Columns("B:B").EntireColumn.AutoFit
-            Columns("C:C").EntireColumn.AutoFit
-            Columns("D:D").EntireColumn.AutoFit
-            Columns("E:E").EntireColumn.AutoFit
-            Columns("F:F").EntireColumn.AutoFit
-            Range("A4").Select
-            Range(Selection, ActiveCell.SpecialCells(xlLastCell)).Select
-            Range("A4").Select
-            Range(Selection, ActiveCell.SpecialCells(xlLastCell)).Select
-            Selection.RowHeight = 33.75
-            
-        End With
-    Next
-
-End Sub

+ 12 - 2
src/index.js

@@ -3,8 +3,10 @@
 // Import dependencies
 import React from 'react'
 import ReactDOM from 'react-dom'
-import { createStore, combineReducers, bindActionCreators, compose } from 'redux'
+import { createStore, applyMiddleware, combineReducers, bindActionCreators, compose } from 'redux'
 import { Provider, connect } from 'react-redux'
+import createSagaMiddleware from 'redux-saga'
+
 /** react-router is not used in this project.
 import { browserHistory, Router, Route, IndexRoute } from 'react-router'
 import { syncHistoryWithStore, routerReducer } from 'react-router-redux'
@@ -35,14 +37,22 @@ const defaultState = {
 }
 console.log('Default state:', defaultState)
 
+/** Create the saga middleware */
+const sagaMiddleware = createSagaMiddleware()
+
 /** The enhancer allows to use Redux development tools in Chrome */
 const enhancers = compose(
-  window.devToolsExtension ? window.devToolsExtension() : f => f
+  window.devToolsExtension ? window.devToolsExtension() : f => f,
+  applyMiddleware(sagaMiddleware)
 )
 console.log('Enhancers:', enhancers)
 
 /** Build the Redux store from the rootReducer, the defualtState and the enhancers. */
 const store = createStore(rootReducer, defaultState, enhancers)
+sagaMiddleware.run(function * () {
+  yield playerList.saga()
+  yield matchList.saga()
+})
 console.log('Store:', store)
 /** react-route is not used in this project.
 const history = syncHistoryWithStore(browserHistory, store)

+ 0 - 20
src/matchList/components/MatchDisp.js

@@ -1,20 +0,0 @@
-import React from 'react'
-import { date2s, time2s } from '../../helpers.js'
-
-class MatchDisp extends React.Component {
-  render () {
-    const match = this.props.match
-    return (
-      <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>
-    )
-  }
-}
-
-export default MatchDisp

+ 0 - 35
src/matchList/components/MatchList.js

@@ -1,35 +0,0 @@
-import React from 'react'
-import MatchDisp from './MatchDisp'
-
-class MatchList extends React.Component {
-  render () {
-    console.log(this.props)
-    const matches = this.props.match.allMatches
-    const filtered = this.props.match.filteredMatches
-
-    return (
-      <div>
-        <h2>Matches ({filtered.length}/{matches.length})</h2>
-        <table className='table table-bordered table-striped'>
-          <thead>
-            <tr>
-              <th>Ort</th>
-              <th>Datum</th>
-              <th>Zeit</th>
-              <th>Konkurrenz</th>
-              <th>Spieler 1</th>
-              <th>Spieler 2</th>
-            </tr>
-          </thead>
-          <tbody>
-            {filtered.map((match, key) =>
-              <MatchDisp key={key} match={match} />
-            )}
-          </tbody>
-        </table>
-      </div>
-    )
-  }
-}
-
-export default MatchList

+ 51 - 0
src/matchList/components/MatchTable.js

@@ -0,0 +1,51 @@
+import React from 'react'
+import { date2s, time2s } from '../../helpers.js'
+
+class MatchRow extends React.Component {
+  render () {
+    const match = this.props.match
+    return (
+      <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>
+    )
+  }
+}
+
+class MatchTable extends React.Component {
+  render () {
+    console.log(this.props)
+    const matches = this.props.match.allMatches
+    const filtered = this.props.match.filteredMatches
+
+    return (
+      <div>
+        <h2>Matches ({filtered.length}/{matches.length})</h2>
+        <table className='table table-bordered table-striped'>
+          <thead>
+            <tr>
+              <th>Ort</th>
+              <th>Datum</th>
+              <th>Zeit</th>
+              <th>Konkurrenz</th>
+              <th>Spieler 1</th>
+              <th>Spieler 2</th>
+            </tr>
+          </thead>
+          <tbody>
+            {filtered.map((match, key) =>
+              <MatchRow key={key} match={match} />
+            )}
+          </tbody>
+        </table>
+      </div>
+    )
+  }
+}
+
+export default MatchTable

+ 2 - 3
src/matchList/components/index.js

@@ -1,4 +1,3 @@
-import MatchList from './MatchList'
-import MatchDisp from './MatchDisp'
+import MatchTable from './MatchTable'
 
-export default { MatchList, MatchDisp }
+export default { MatchTable }

+ 0 - 23
src/playerList/components/PlayerDisp.js

@@ -1,23 +0,0 @@
-import React from 'react'
-import { date2s, time2s } from '../../helpers'
-
-class PlayerDisp extends React.Component {
-  render () {
-    const player = this.props.player
-    return (
-      <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)} ${time2s(player.BezahltAm)}` : 'Unbekannt'}</td>
-        <td>{player.isJunior ? 'Junior' : ''}</td>
-        <td>{player.isJuniorDP ? 'DP Junior' : ''}</td>
-      </tr>
-    )
-  }
-}
-
-export default PlayerDisp

+ 19 - 0
src/playerList/components/PlayerFilter.js

@@ -0,0 +1,19 @@
+import React from 'react'
+import { FieldGroup } from 'react-bootstrap'
+
+class PlayerFilter extends React.Component {
+  render () {
+    return (
+      <form>
+        <FieldGroup
+          id='playerListFile'
+          type='file'
+          label='playerList.xls File'
+          help='Die Datei wird von der Swisstennis Turniersoftware generiert.'
+        />
+      </form>
+    )
+  }
+}
+
+export default PlayerFilter

+ 26 - 0
src/playerList/components/PlayerForm.js

@@ -0,0 +1,26 @@
+import React from 'react'
+import { FieldGroup, Panel } from 'react-bootstrap'
+
+class PlayerForm extends React.Component {
+  render () {
+    const fileUpload = this.props.fileUpload
+
+    return (
+      <div>
+        {fileUpload === 'failure'
+        ? (<Panel header='Fehler'>Fehler beim laden.</Panel>)
+        : ''}
+        <form>
+          <FieldGroup
+            id='playerListFile'
+            type='file'
+            label='playerList.xls File'
+            help='Die Datei wird von der Swisstennis Turniersoftware generiert.'
+          />
+        </form>
+      </div>
+    )
+  }
+}
+
+export default PlayerForm

+ 0 - 37
src/playerList/components/PlayerList.js

@@ -1,37 +0,0 @@
-import React from 'react'
-import PlayerDisp from './PlayerDisp'
-
-class PlayerList extends React.Component {
-  render () {
-    console.log(this.props)
-    const filtered = this.props.player.filteredPlayers
-
-    return (
-      <div>
-        <h2>Spielerliste ({filtered.length})</h2>
-        <table className='table table-bordered table-striped'>
-          <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>
-    )
-  }
-}
-
-export default PlayerList

+ 56 - 0
src/playerList/components/PlayerTable.js

@@ -0,0 +1,56 @@
+import React from 'react'
+import { date2s, time2s } from '../../helpers'
+
+class PlayerRow extends React.Component {
+  render () {
+    const player = this.props.player
+    return (
+      <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)} ${time2s(player.BezahltAm)}` : 'Unbekannt'}</td>
+        <td>{player.isJunior ? 'Junior' : ''}</td>
+        <td>{player.isJuniorDP ? 'DP Junior' : ''}</td>
+      </tr>
+    )
+  }
+}
+
+class PlayerTable extends React.Component {
+  render () {
+    console.log(this.props)
+    const filtered = this.props.player.filteredPlayers
+
+    return (
+      <div>
+        <h2>Spielerliste ({filtered.length})</h2>
+        <table className='table table-bordered table-striped'>
+          <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) =>
+              <PlayerRow key={key} player={player} />
+            )}
+          </tbody>
+        </table>
+      </div>
+    )
+  }
+}
+
+export default PlayerTable

+ 2 - 3
src/playerList/components/index.js

@@ -1,4 +1,3 @@
-import PlayerDisp from './PlayerDisp'
-import PlayerList from './PlayerList'
+import PlayerTable from './PlayerTable'
 
-export default { PlayerDisp, PlayerList }
+export default { PlayerTable }

+ 17 - 0
src/playerList/functions.js

@@ -0,0 +1,17 @@
+export function generatePlayerList (worksheet) {
+  console.log('About to read the player list.')
+    /* const worksheets = { ...this.state.worksheets }
+    worksheets['PlayerList'] = worksheet
+    this.setState({ worksheets }) */
+
+  if (worksheet[4].length !== 32 & worksheet[3][0] !== 'Konkurrenz' & worksheet[3][31] !== 'bezahlt') {
+    throw Error('Wrong file structure.')
+  }
+  const players = worksheet.slice(4, worksheet.length).map((playerData) => new Player.Player(playerData))
+  const player = { ...this.state.player }
+  player.players = players
+  this.setState({ player })
+  this.calculatePayDate()
+  this.filterPlayers()
+  console.log('State after generating player list:', this.state)
+}

+ 41 - 4
src/playerList/state.js

@@ -1,4 +1,5 @@
 /** @module player/state */
+import { call, put, takeEvery, takeLatest } from 'redux-saga/effects'
 
 /**
  * state.js
@@ -13,6 +14,21 @@ export const actions = {
       type: 'SET_PLAYERS',
       players
     }
+  },
+  fileUploadStart: () => {
+    return {
+      type: 'PLAYER_FILE_UPLOAD_START'
+    }
+  },
+  fileUploadSuccess: () => {
+    return {
+      type: 'PLAYER_FILE_UPLOAD_SUCCESS'
+    }
+  },
+  fileUploadFailure: () => {
+    return {
+      type: 'PLAYER_FILE_UPLOAD_FAILURE'
+    }
   }
 }
 console.log('State actions', actions)
@@ -21,15 +37,36 @@ console.log('State actions', actions)
 export const state = {
   allPlayers: [],
   filteredPlayers: [],
-  filters: {}
+  filters: {},
+  fileUpload: 'idle'
 }
 console.log('State state', state)
 
 /** reducer is called by the redux dispatcher and handles all component actions */
 export function reducer (state = [], action) {
-  let nextState = state
-  return nextState
+  switch (action.type) {
+    case 'SET_PLAYERS':
+      return { ...state, allPlayers: action.players }
+    case 'PLAYER_FILE_UPLOAD_START':
+      return { ...state, fileUpload: 'started' }
+    case 'PLAYER_FILE_UPLOAD_SUCCESS':
+      return { ...state, fileUpload: 'finished' }
+    case 'PLAYER_FILE_UPLOAD_FAILURE':
+      return { ...state, fileUpload: 'failure' }
+    default:
+      return state
+  }
+}
+
+function * uploadFile (action) {
+  try {
+    yield put(actions.fileUploadSuccess())
+  } catch (e) {
+    yield put(actions.fileUploadFailure())
+  }
 }
 
 /** sagas are asynchronous workers (JS generators) to handle the state. */
-export function * saga () {}
+export function * saga () {
+  yield takeEvery('PLAYER_FILE_UPLOAD_START', uploadFile)
+}

+ 8 - 0
src/settings/index.js

@@ -0,0 +1,8 @@
+import { actions, reducer, state } from './state'
+import components from './components'
+
+const filters = {}
+
+const selectors = {}
+
+export default { actions, components, filters, selectors, reducer, state }

+ 46 - 0
src/settings/state.js

@@ -0,0 +1,46 @@
+/** @module setting/state */
+
+/**
+ * state.js
+ *
+ * Collection of everything which has to do with state changes.
+ **/
+
+/** actionTypes define what actions are handeled by the reducer. */
+export const actions = {
+  changePriceAdult: priceAdult => {
+    return {
+      type: 'SETTING_CHANGE_PRICE_ADULT',
+      priceAdult
+    }
+  },
+  changePriceJunior: priceJunior => {
+    return {
+      type: 'SETTING_CHANGE_PRICE_JUNIOR',
+      priceJunior
+    }
+  }
+}
+console.log('State actions', actions)
+
+/** state definition */
+export const state = {
+  priceAdult: 50,
+  priceJunior: 30
+}
+console.log('State state', state)
+
+/** reducer is called by the redux dispatcher and handles all component actions */
+export function reducer (state = [], action) {
+  switch (action.type) {
+    case 'SETTING_CHANGE_PRICE_ADULT':
+      return { ...state, priceAdult: action.priceAdult }
+    case 'SETTING_CHANGE_PRICE_JUNIOR':
+      return { ...state, priceJunior: action.priceJunior }
+    default:
+      return state
+  }
+}
+
+/** sagas are asynchronous workers (JS generators) to handle the state. */
+export function * saga () {}

+ 4 - 0
yarn.lock

@@ -5144,6 +5144,10 @@ reduce-function-call@^1.0.1:
   dependencies:
     balanced-match "^0.4.2"
 
+redux-saga@^0.15.4:
+  version "0.15.4"
+  resolved "https://registry.yarnpkg.com/redux-saga/-/redux-saga-0.15.4.tgz#27982a947280053b7ecbb5d3170c837a5fe6b261"
+
 redux@^3.6.0:
   version "3.6.0"
   resolved "https://registry.yarnpkg.com/redux/-/redux-3.6.0.tgz#887c2b3d0b9bd86eca2be70571c27654c19e188d"