Main.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392
  1. import React from 'react' // React to manage the GUI
  2. import Player from './classes/player'
  3. import Match from './classes/match'
  4. import playerList from './playerList' // Everything that has to do with players
  5. import matchList from './matchList' // Everything that has to do with matches
  6. import Excel from './excel' // Helper files to create Excel files
  7. import { date2s, time2s } from './helpers'
  8. import 'bootstrap/dist/css/bootstrap.css'
  9. import EmailList from './lists/components/EmailList'
  10. import PhoneList from './lists/components/PhoneList'
  11. import layout from './layout'
  12. /**
  13. * General Application Design
  14. *
  15. * 4 Components:
  16. * - PlayerList (representing the PlayerList Excel file)
  17. * - Calendar (representing the Calendar Excel file)
  18. * - MatchList (representing the Spielliste Excel file)
  19. * - PaymentList (representing the Zahlliste Excel file)
  20. *
  21. * PlayerList
  22. * - Shows the relevant information from the file
  23. * - Shows calculated information from combination with Calendar
  24. * - Points out potential problems
  25. * - Allows access to player information
  26. *
  27. * Calendar
  28. * - Shows the relevant information from the file
  29. * - Shows calculated information from combination with PlayerList
  30. * - Points out potential problems
  31. * - Allows access to match information
  32. *
  33. * MatchList
  34. * - Shows the calculated match lists
  35. * - Points out problems
  36. *
  37. * PaymentList
  38. * - Shows the calculated payment lists
  39. * - Points out problems
  40. *
  41. */
  42. const FILTER_OFF = 'Alle'
  43. const PLACES = {
  44. 'LE': 'TC Lerchenberg',
  45. 'WA': 'TC Waidberg',
  46. 'VA': 'TC Valsana',
  47. 'SE': 'TC Seebach',
  48. 'BU': 'TC Bührle',
  49. 'HO': 'TC Höngg',
  50. 'TS': 'Tennis-Sport Club',
  51. 'HA': 'Städtische Plätze Hardhof',
  52. 'AU': 'Auswärtig'
  53. }
  54. /** Main application */
  55. class Main extends React.Component {
  56. /**
  57. * Constructor
  58. * Defines the state and binds all 'this' in all functions to the object.
  59. */
  60. constructor () {
  61. super()
  62. // Bind 'this' to functions
  63. this.handlePlayerList = this.handlePlayerList.bind(this)
  64. this.handleCalendar = this.handleCalendar.bind(this)
  65. this.generatePlayerList = this.generatePlayerList.bind(this)
  66. this.generateCalendar = this.generateCalendar.bind(this)
  67. this.filterMatches = this.filterMatches.bind(this)
  68. this.filterPlayers = this.filterPlayers.bind(this)
  69. this.generateSchedule = this.generateSchedule.bind(this)
  70. this.generatePayTable = this.generatePayTable.bind(this)
  71. this.generatePhoneList = this.generatePhoneList.bind(this)
  72. }
  73. calculateMatchFilters () {
  74. const dates = {}
  75. const places = []
  76. const categories = []
  77. this.state.match.matches.forEach((item) => {
  78. const dateString = date2s(item.Datum)
  79. if (!!item.Datum & !dates.hasOwnProperty(dateString)) {
  80. dates[dateString] = item.Datum
  81. }
  82. if (!!item.Ort & !places.includes(item.Ort)) {
  83. places.push(item.Ort)
  84. }
  85. if (!!item.Konkurrenz & !categories.includes(item.Konkurrenz)) {
  86. categories.push(item.Konkurrenz)
  87. }
  88. })
  89. const match = { ...this.state.match, dates, places, categories }
  90. this.setState({ match })
  91. }
  92. calculatePlayerFilters () {
  93. const categories = []
  94. this.state.player.players.forEach((item) => {
  95. if (!!item.Konkurrenz & !categories.includes(item.Konkurrenz)) {
  96. categories.push(item.Ort)
  97. }
  98. })
  99. const player = { ...this.state.player, categories }
  100. this.setState({ player })
  101. }
  102. handlePlayerList (event) {
  103. const file = this.playerList.files[0]
  104. Excel.readWorkbook(file, this.generatePlayerList)
  105. }
  106. handleCalendar (event) {
  107. const file = this.calendar.files[0]
  108. Excel.readWorkbook(file, this.generateCalendar)
  109. }
  110. generatePlayerList (worksheet) {
  111. console.log('About to read the player list.')
  112. /* const worksheets = { ...this.state.worksheets }
  113. worksheets['PlayerList'] = worksheet
  114. this.setState({ worksheets }) */
  115. if (worksheet[4].length !== 32 & worksheet[3][0] !== 'Konkurrenz' & worksheet[3][31] !== 'bezahlt') {
  116. throw Error('Wrong file structure.')
  117. }
  118. const players = worksheet.slice(4, worksheet.length).map((playerData) => new Player.Player(playerData))
  119. const player = { ...this.state.player }
  120. player.players = players
  121. this.setState({ player })
  122. this.calculatePayDate()
  123. this.filterPlayers()
  124. console.log('State after generating player list:', this.state)
  125. }
  126. generateCalendar (worksheet) {
  127. console.log('About to read the calendar.')
  128. const worksheets = { ...this.state.worksheets }
  129. worksheets['Calendar'] = worksheet
  130. this.setState({ worksheets })
  131. if (worksheet[2].length < 8 | worksheet[2].length > 9) {
  132. throw Error('Wrong file structure.')
  133. }
  134. const matches = worksheet.slice(2, worksheet.length).map((matchData) => new Match.MatchClass(matchData))
  135. const match = { ...this.state.match }
  136. match.matches = matches
  137. this.setState({ match })
  138. this.calculateMatchFilters()
  139. this.calculatePayDate()
  140. this.filterMatches()
  141. console.log('State after generating calendar:', this.state)
  142. }
  143. filterMatches () {
  144. const filters = {
  145. date: this.matchDate.value !== FILTER_OFF ? this.matchDate.value : null,
  146. place: this.matchPlace.value !== FILTER_OFF ? this.matchPlace.value : null,
  147. category: this.matchCategory.value !== FILTER_OFF ? this.matchCategory.value : null
  148. }
  149. console.log('New filter settings:', filters)
  150. const match = { ...this.state.match }
  151. match.filtered = match.matches.filter((match) => {
  152. const matchDate = new Date(match.Datum)
  153. matchDate.setHours(0, 0, 0, 0)
  154. const filtDate = new Date(filters.date)
  155. filtDate.setHours(0, 0, 0, 0)
  156. return (!filters.date | matchDate.getTime() === filtDate.getTime()) &
  157. (!filters.place | filters.place === match.Ort) &
  158. (!filters.category | filters.category === match.Konkurrenz)
  159. })
  160. this.setState({ match })
  161. const player = { ...this.state.player, filters }
  162. player.filtered = player.players
  163. this.setState({ player })
  164. }
  165. filterPlayers () {
  166. const filters = {
  167. junior: this.playerJunior.checked,
  168. paid: this.playerPaid.checked,
  169. category: this.playerCategory.value !== FILTER_OFF ? this.playerCategory.value : null
  170. }
  171. console.log('New filter settings:', filters)
  172. const player = { ...this.state.player, filters }
  173. player.filtered = player.players
  174. this.setState({ player })
  175. }
  176. generateSchedule (event) {
  177. event.preventDefault()
  178. const matchlist = new Excel.Workbook()
  179. matchlist.SheetNames = []
  180. matchlist.Sheets = {}
  181. const worksheets = {}
  182. let placeArray = this.state.match.places
  183. if (placeArray.length > 1) {
  184. // placeArray = placeArray.concat([FILTER_OFF])
  185. }
  186. const date = Object.keys(this.state.match.dates).find((key) =>
  187. String(this.state.match.dates[key]) === this.matchDate.value
  188. )
  189. placeArray.forEach(place => {
  190. let header = [
  191. ['Stadtzürcher Tennismeisterschaft'],
  192. [`Spielplan für den ${date} (${PLACES[place] || place})`],
  193. [],
  194. ['Platz', 'Zeit', 'Kategorie', 'Spieler 1', '', 'Spieler 2', '', '1. Satz', '2. Satz', '3. Satz', 'WO Grund']
  195. ]
  196. let matchListPerPlace = this.state.match.filtered.filter((match) => (match.Ort === place | place === FILTER_OFF)).map((match) =>
  197. [null, time2s(match.Datum), match.Konkurrenz, match.Spieler1, match.Spieler1Klassierung, match.Spieler2, match.Spieler2Klassierung]
  198. )
  199. console.log('Generated match list per place:', matchListPerPlace)
  200. worksheets[place] = Excel.SheetFromArray(header.concat(matchListPerPlace))
  201. matchlist.SheetNames.push(place)
  202. matchlist.Sheets[place] = worksheets[place]
  203. })
  204. Excel.saveAs(matchlist, 'Spielplan.xlsx')
  205. }
  206. generatePhoneList (event) {
  207. event.preventDefault()
  208. const phoneMail = new Excel.Workbook()
  209. phoneMail.SheetNames = []
  210. phoneMail.Sheets = {}
  211. const dataList = [
  212. ['Vorname', 'Nachname', 'Anrede', 'Geschlecht', 'Handy', 'E-Mail']
  213. ]
  214. const phonePot = []
  215. const players = this.state.player.filtered
  216. players.forEach(player => {
  217. if (!player.phone.match(/^FEHLER/) && !phonePot.includes(player.phone)) {
  218. phonePot.push(player.phone)
  219. dataList.push([
  220. player.Vorname,
  221. player.Name,
  222. 2,
  223. player.geschlecht === 'w' ? 2 : 1,
  224. player.phone
  225. ])
  226. }
  227. })
  228. phoneMail.Sheets['Sheet1'] = Excel.SheetFromArray(dataList)
  229. phoneMail.SheetNames.push('Sheet1')
  230. Excel.saveAs(phoneMail, 'Telefon.xlsx')
  231. }
  232. calculatePayDate () {
  233. if ((this.state.player.players.length === 0) | (this.state.match.matches.length === 0)) {
  234. return
  235. }
  236. this.state.match.matches.forEach((match) => {
  237. [match.Spieler1, match.Spieler2].forEach((matchPlayer) => {
  238. if (matchPlayer) {
  239. let foundPlayer = this.state.player.players.find((player) =>
  240. (player.name === matchPlayer) & (player.Konkurrenz === match.Konkurrenz)
  241. )
  242. if (!foundPlayer) {
  243. console.log('Debug payerlist:', foundPlayer, match)
  244. throw Error('Match player not found in player list. This is an error!')
  245. }
  246. if (!foundPlayer.BezahltAm) {
  247. foundPlayer.BezahltAm = match.Datum
  248. }
  249. }
  250. })
  251. })
  252. }
  253. generatePayTable (event) {
  254. event.preventDefault()
  255. const paylist = new Excel.Workbook()
  256. paylist.SheetNames = []
  257. paylist.Sheets = {}
  258. const worksheets = {}
  259. let placeArray = this.state.match.places
  260. /* if (placeArray.length > 1) {
  261. placeArray = placeArray.concat([FILTER_OFF])
  262. } */
  263. const date = Object.keys(this.state.match.dates).find((key) =>
  264. String(this.state.match.dates[key]) === this.matchDate.value
  265. )
  266. placeArray.forEach((place) => {
  267. let header = [
  268. ['Stadtzürcher Tennismeisterschaft'],
  269. [`Nenngelder für ${date}`],
  270. [],
  271. [`${PLACES[place] || place}`, null, '50.- oder 30.- (Junioren Jg. 1999 oder jünger)'],
  272. [],
  273. ['bezahlt', 'Kat.', 'Zeit', 'Name', 'Betrag bez.', 'Quittung']
  274. ]
  275. // Per place
  276. let payListPerPlace = []
  277. this.state.match.filtered.forEach((match) => {
  278. [match.Spieler1, match.Spieler2].forEach((matchPlayer) => {
  279. if (!!matchPlayer & (match.Ort === place | FILTER_OFF === place)) {
  280. const player = this.state.player.players.find((player) =>
  281. (player.Konkurrenz === match.Konkurrenz) & (player.name === matchPlayer)
  282. )
  283. let paid = null
  284. if (player.BezahltAm < this.matchDate.value) {
  285. paid = date2s(player.BezahltAm)
  286. }
  287. if (player.Bezahlt) {
  288. paid = 'OK'
  289. }
  290. let price
  291. if (player.isDoubles) {
  292. price = (player.isJunior ? 15 : 25) + (player.isJuniorDP ? 15 : 25)
  293. } else {
  294. price = player.isJunior ? 30 : 50
  295. }
  296. payListPerPlace.push([ paid, match.Konkurrenz, time2s(match.Datum), `(${price}.-) ${matchPlayer}` ])
  297. }
  298. })
  299. })
  300. let footer = [
  301. [],
  302. ['Datum'],
  303. ['Turnierleiter', null, null, 'Kassierer'],
  304. ['Betrag von Spielern erhalten', null, null, 'Betrag von Turnierleiter erhalten']
  305. ]
  306. console.log(`Generated pay list per place ${place}:`, payListPerPlace)
  307. worksheets[place] = Excel.SheetFromArray(header.concat(payListPerPlace, footer))
  308. paylist.SheetNames.push(place)
  309. paylist.Sheets[place] = worksheets[place]
  310. })
  311. /* let payListPerPlace = []
  312. this.state.match.filtered.forEach((match) => {
  313. [match.Spieler1, match.Spieler2].forEach((matchPlayer) => {
  314. if (matchPlayer) {
  315. const player = this.state.player.players.find((player) =>
  316. (player.Konkurrenz === match.Konkurrenz) & (player.name === matchPlayer)
  317. )
  318. let price
  319. if (player.isDoubles) {
  320. price = (player.isJunior ? 15 : 25) + (player.isJuniorDP ? 15 : 25)
  321. } else {
  322. price = player.isJunior ? 30 : 50
  323. }
  324. payListPerPlace.push([ match.Ort, match.Konkurrenz, `${matchPlayer} (${price}.-)` ])
  325. }
  326. })
  327. })
  328. console.log(`Generated pay list for "Alle":`, payListPerPlace)
  329. worksheets[FILTER_OFF] = Excel.SheetFromArray(payListPerPlace)
  330. paylist.SheetNames.push(FILTER_OFF)
  331. paylist.Sheets[FILTER_OFF] = worksheets[FILTER_OFF]
  332. */
  333. Excel.saveAs(paylist, 'Zahlliste.xlsx')
  334. }
  335. render () {
  336. const AppLayout = layout.components.AppLayout
  337. return (
  338. <div className='container'>
  339. <AppLayout
  340. layout={this.props.layout}
  341. layoutActions={this.props.layoutActions}
  342. state={this.props}
  343. components={{
  344. PlayerList: playerList.components.PlayerList,
  345. MatchTable: matchList.components.MatchTable
  346. }}
  347. />
  348. </div>
  349. )
  350. }
  351. }
  352. export default Main