1
|
|
|
import { graphql, Link } from "gatsby" |
2
|
|
|
import { GatsbyImage, getSrc, StaticImage } from "gatsby-plugin-image" |
3
|
|
|
import React, { useEffect, useState, Suspense } from "react" |
4
|
|
|
import { Card } from "../components/Card" |
5
|
|
|
import Icon from "../components/Icon" |
6
|
|
|
import { Seo } from "../components/Seo" |
7
|
|
|
import { useSiteMetaData } from "../hooks/use-site-metadata" |
8
|
|
|
import Layout from "../layouts" |
9
|
|
|
import { DateTime } from "luxon" |
10
|
|
|
import { mapPositionCode, request } from "../scripts/helper" |
11
|
|
|
import { |
12
|
|
|
PlayerQuery, |
13
|
|
|
PlayerStatsDataObject, |
14
|
|
|
PlayerStatsDataResponseObject, |
15
|
|
|
PlayerStatsReportsResponseObject, |
16
|
|
|
} from "../Types/Player" |
17
|
|
|
import "./Player.scss" |
18
|
|
|
import iconCardRed from "../images/i_card_red.png" |
19
|
|
|
import iconCardYellow from "../images/i_card_yellow.png" |
20
|
|
|
import iconCleansheet from "../images/i_cleansheet.png" |
21
|
|
|
import iconGoal from "../images/i_goal.png" |
22
|
|
|
const RelatedNews = React.lazy(() => import(`../components/RelatedNews`)) |
23
|
|
|
|
24
|
|
|
const Player = ({ data: { nodePlayer } }: PlayerQuery) => { |
25
|
|
|
const cleanBody = |
26
|
|
|
(nodePlayer?.body && |
27
|
|
|
nodePlayer?.body?.processed?.replaceAll(`/sites/default/`, `${process.env.GATSBY_API_DOMAIN}/sites/default/`)) || |
28
|
|
|
`` |
29
|
|
|
const picture = nodePlayer?.relationships?.field_image?.localFile?.childImageSharp?.gatsbyImageData && ( |
30
|
|
|
<GatsbyImage |
31
|
|
|
image={nodePlayer?.relationships?.field_image?.localFile?.childImageSharp?.gatsbyImageData} |
32
|
|
|
alt={`${nodePlayer.field_firstname} ${nodePlayer.field_lastname}`} |
33
|
|
|
/> |
34
|
|
|
) |
35
|
|
|
|
36
|
|
|
const [data, setData] = useState<PlayerStatsDataObject>() |
37
|
|
|
const [loading, setLoading] = useState<boolean>(true) |
38
|
|
|
const { kcvvPsdApi } = useSiteMetaData() |
39
|
|
|
const playerId = nodePlayer.field_vv_id |
40
|
|
|
|
41
|
|
|
useEffect(() => { |
42
|
|
|
async function getData() { |
43
|
|
|
const response = await request.get(`${kcvvPsdApi}/stats/player/${playerId}`) |
44
|
|
|
setData(response.data) |
45
|
|
|
setLoading(false) |
46
|
|
|
} |
47
|
|
|
getData() |
48
|
|
|
}, [kcvvPsdApi, playerId]) |
49
|
|
|
|
50
|
|
|
const team = nodePlayer.relationships?.node__team || [] |
51
|
|
|
const articles = nodePlayer.relationships?.node__article || [] |
52
|
|
|
|
53
|
|
|
return ( |
54
|
|
|
<Layout> |
55
|
|
|
<div className="player__details__top"> |
56
|
|
|
<header className="page_header__wrapper player__header__wrapper"> |
57
|
|
|
<div className="page_header page_header--max player__header"> |
58
|
|
|
<div className="player__header__inner"> |
59
|
|
|
<div className="player__header__inner_text"> |
60
|
|
|
<h1 className={`player__detail__name`}> |
61
|
|
|
<span className={`player__detail__name_first`}>{nodePlayer.field_firstname}</span> |
62
|
|
|
<span className={`player__detail__name_last`}>{nodePlayer.field_lastname}</span> |
63
|
|
|
</h1> |
64
|
|
|
<p className={`player__detail__shirt_number`} aria-hidden="true"> |
65
|
|
|
{nodePlayer?.field_shirtnumber || ``} |
66
|
|
|
</p> |
67
|
|
|
</div> |
68
|
|
|
<div className="player__header__inner_image"> |
69
|
|
|
{picture || ( |
70
|
|
|
<StaticImage |
71
|
|
|
src={`../images/kcvv-player-bg.png`} |
72
|
|
|
alt={`${nodePlayer.field_firstname} ${nodePlayer.field_lastname}`} |
73
|
|
|
placeholder={`blurred`} |
74
|
|
|
/> |
75
|
|
|
)} |
76
|
|
|
</div> |
77
|
|
|
</div> |
78
|
|
|
</div> |
79
|
|
|
</header> |
80
|
|
|
{!loading && renderStats(data?.playerStatistics || [], nodePlayer.field_position || ``)} |
81
|
|
|
</div> |
82
|
|
|
<main className="player__details__middle"> |
83
|
|
|
{renderMeta(nodePlayer)} |
84
|
|
|
{cleanBody && <div dangerouslySetInnerHTML={{ __html: cleanBody }} className="player__details__intro" />} |
85
|
|
|
</main> |
86
|
|
|
|
87
|
|
|
<footer className="player__details__bottom"> |
88
|
|
|
{renderPlayerStatsFull(data?.playerStatistics || [])} |
89
|
|
|
{renderPlayerGamesFull(data?.gameReports || [])} |
90
|
|
|
</footer> |
91
|
|
|
|
92
|
|
|
{(team || articles) && ( |
93
|
|
|
<aside className="player__details__related_news page__wrapper"> |
94
|
|
|
<Suspense fallback={<div>Loading...</div>}> |
95
|
|
|
<RelatedNews items={[...team, ...articles]} limit={6} /> |
96
|
|
|
</Suspense> |
97
|
|
|
</aside> |
98
|
|
|
)} |
99
|
|
|
|
100
|
|
|
{/* <PlayerDetail player={nodePlayer} /> */} |
101
|
|
|
</Layout> |
102
|
|
|
) |
103
|
|
|
} |
104
|
|
|
|
105
|
|
|
export const Head = ({ data: { nodePlayer } }: PlayerQuery) => { |
106
|
|
|
const pathUrl = nodePlayer?.path?.alias || `` |
107
|
|
|
const heroImage = nodePlayer?.relationships?.field_image?.localFile?.childImageSharp?.gatsbyImageData |
108
|
|
|
|
109
|
|
|
const ogImage = heroImage && { |
110
|
|
|
src: getSrc(heroImage) || ``, |
111
|
|
|
width: heroImage.width, |
112
|
|
|
height: heroImage.height, |
113
|
|
|
} |
114
|
|
|
return ( |
115
|
|
|
<Seo |
116
|
|
|
title={nodePlayer?.title || ``} |
117
|
|
|
path={pathUrl} |
118
|
|
|
image={ogImage} |
119
|
|
|
description={nodePlayer?.title + ` - (ex-)speler van KCVV Elewijt`} |
120
|
|
|
/> |
121
|
|
|
) |
122
|
|
|
} |
123
|
|
|
|
124
|
|
|
export default Player |
125
|
|
|
|
126
|
|
|
const renderStats = (playerStatistics: PlayerStatsDataResponseObject[], playerPosition = ``) => ( |
127
|
|
|
<aside className="player__stats"> |
128
|
|
|
<section className="player__stats__item"> |
129
|
|
|
<div className="player__stats__item__number"> |
130
|
|
|
{playerStatistics.reduce((a, b) => a + (b?.gamesPlayed || 0), 0) || `0`} |
131
|
|
|
</div> |
132
|
|
|
<div className="player__stats__item__label">Wedstrijden</div> |
133
|
|
|
</section> |
134
|
|
|
{(playerPosition === `k` || playerPosition === `d`) && ( |
135
|
|
|
<section className="player__stats__item"> |
136
|
|
|
<div className="player__stats__item__number"> |
137
|
|
|
{playerStatistics.reduce((a, b) => a + (b?.cleanSheets || 0), 0) || `0`} |
138
|
|
|
</div> |
139
|
|
|
<div className="player__stats__item__label">Cleansheets</div> |
140
|
|
|
</section> |
141
|
|
|
)} |
142
|
|
|
<section className="player__stats__item"> |
143
|
|
|
<div className="player__stats__item__number"> |
144
|
|
|
{playerStatistics.reduce((a, b) => a + (b?.goals || 0), 0) || `0`} |
145
|
|
|
</div> |
146
|
|
|
<div className="player__stats__item__label">Doelpunten</div> |
147
|
|
|
</section> |
148
|
|
|
<section className="player__stats__item"> |
149
|
|
|
<div className="player__stats__item__number"> |
150
|
|
|
{playerStatistics.reduce((a, b) => a + (b?.yellowCards || 0), 0) || `0`} |
151
|
|
|
</div> |
152
|
|
|
<div className="player__stats__item__label">Gele kaarten</div> |
153
|
|
|
</section> |
154
|
|
|
<section className="player__stats__item"> |
155
|
|
|
<div className="player__stats__item__number"> |
156
|
|
|
{playerStatistics.reduce((a, b) => a + (b?.redCards || 0), 0) || `0`} |
157
|
|
|
</div> |
158
|
|
|
<div className="player__stats__item__label">Rode kaarten</div> |
159
|
|
|
</section> |
160
|
|
|
</aside> |
161
|
|
|
) |
162
|
|
|
|
163
|
|
|
const renderMeta = (nodePlayer: Queries.node__player) => { |
164
|
|
|
const currentlyPlaying = !nodePlayer.field_date_leave |
165
|
|
|
return ( |
166
|
|
|
<div className="player__details__meta"> |
167
|
|
|
<div className="player__details__meta__item player__details__meta__item--birthdate"> |
168
|
|
|
<span className="player__details__meta__item__label">Geboortedatum</span> |
169
|
|
|
<span className="player__details__meta__item__data">{nodePlayer.field_birth_date || `Onbekend`}</span> |
170
|
|
|
</div> |
171
|
|
|
<div className="player__details__meta__item player__details__meta__item--position"> |
172
|
|
|
<span className="player__details__meta__item__data">{mapPositionCode(nodePlayer.field_position || ``)}</span> |
173
|
|
|
<span className="player__details__meta__item__label"> |
174
|
|
|
{nodePlayer?.relationships?.node__team && ( |
175
|
|
|
<Link to={nodePlayer?.relationships?.node__team[0]?.path?.alias || ``}> |
176
|
|
|
{nodePlayer?.relationships?.node__team[0]?.title} |
177
|
|
|
</Link> |
178
|
|
|
)} |
179
|
|
|
</span> |
180
|
|
|
</div> |
181
|
|
|
<div className="player__details__meta__item player__details__meta__item--joindate"> |
182
|
|
|
<span className="player__details__meta__item__label"> |
183
|
|
|
{currentlyPlaying && `Speler bij KCVV sinds`} |
184
|
|
|
{!currentlyPlaying && `Speler tussen`} |
185
|
|
|
</span> |
186
|
|
|
<span className="player__details__meta__item__data"> |
187
|
|
|
{nodePlayer.field_join_date || `Onbekend`} |
188
|
|
|
{!currentlyPlaying && ( |
189
|
|
|
<> |
190
|
|
|
<span className={`text--regular`}> en </span> {nodePlayer.field_date_leave} |
191
|
|
|
</> |
192
|
|
|
)} |
193
|
|
|
</span> |
194
|
|
|
</div> |
195
|
|
|
</div> |
196
|
|
|
) |
197
|
|
|
} |
198
|
|
|
|
199
|
|
|
const renderPlayerStatsFull = (playerStatistics: PlayerStatsDataResponseObject[]) => { |
200
|
|
|
return ( |
201
|
|
|
<Card title="Statistieken" className={`player__stats__stats`} hasTable={true}> |
202
|
|
|
<table className={`player__stats__stats__table`}> |
203
|
|
|
<thead> |
204
|
|
|
<tr> |
205
|
|
|
<th className={`table__column__string`}>Team</th> |
206
|
|
|
<th className={`table__column__number show-for-medium`}> |
207
|
|
|
<span title="Wedstrijden gespeeld">P</span> |
208
|
|
|
</th> |
209
|
|
|
<th className={`table__column__number`}> |
210
|
|
|
<span title="Wedstrijden gewonnen">W</span> |
211
|
|
|
</th> |
212
|
|
|
<th className={`table__column__number`}> |
213
|
|
|
<span title="Wedstrijden gelijkgespeeld">D</span> |
214
|
|
|
</th> |
215
|
|
|
<th className={`table__column__number`}> |
216
|
|
|
<span title="Wedstrijden verloren">L</span> |
217
|
|
|
</th> |
218
|
|
|
<th className={`table__column__number`}> |
219
|
|
|
<img src={iconCardYellow} title="Gele kaart" alt="Gele kaart" className="table__header__icon" /> |
220
|
|
|
</th> |
221
|
|
|
<th className={`table__column__number`}> |
222
|
|
|
<img src={iconCardRed} title="Rode kaart" alt="Rode kaart" className="table__header__icon" /> |
223
|
|
|
</th> |
224
|
|
|
<th className={`table__column__number`}> |
225
|
|
|
<img |
226
|
|
|
src={iconGoal} |
227
|
|
|
title="Doelpunt(en) gescoord" |
228
|
|
|
alt="Doelpunt(en) gescoord" |
229
|
|
|
className="table__header__icon" |
230
|
|
|
/> |
231
|
|
|
</th> |
232
|
|
|
<th className={`table__column__number show-for-medium`}> |
233
|
|
|
<img src={iconCleansheet} title="Cleansheets" alt="Cleansheets" className="table__header__icon" /> |
234
|
|
|
</th> |
235
|
|
|
<th className={`table__column__number`}> |
236
|
|
|
<span title="Minuten gespeeld"> |
237
|
|
|
<Icon icon="fa-clock-o" /> |
238
|
|
|
</span> |
239
|
|
|
</th> |
240
|
|
|
</tr> |
241
|
|
|
</thead> |
242
|
|
|
<tbody> |
243
|
|
|
{playerStatistics.map(function (stats) { |
244
|
|
|
return ( |
245
|
|
|
<tr> |
246
|
|
|
<td className={`table__column__string`}>{stats?.team?.replace(`Voetbal : `, ``)}</td> |
247
|
|
|
<td className={`table__column__number show-for-medium`}>{stats.gamesPlayed}</td> |
248
|
|
|
<td className={`table__column__number`}>{stats.gamesWon}</td> |
249
|
|
|
<td className={`table__column__number`}>{stats.gamesEqual}</td> |
250
|
|
|
<td className={`table__column__number`}>{stats.gamesLost}</td> |
251
|
|
|
<td className={`table__column__number`}>{stats.yellowCards}</td> |
252
|
|
|
<td className={`table__column__number`}>{stats.redCards}</td> |
253
|
|
|
<td className={`table__column__number`}>{stats.goals}</td> |
254
|
|
|
<td className={`table__column__number show-for-medium`}>{stats.cleanSheets}</td> |
255
|
|
|
<td className={`table__column__number`}>{stats.minutes}'</td> |
256
|
|
|
</tr> |
257
|
|
|
) |
258
|
|
|
})} |
259
|
|
|
</tbody> |
260
|
|
|
</table> |
261
|
|
|
</Card> |
262
|
|
|
) |
263
|
|
|
} |
264
|
|
|
|
265
|
|
|
const renderPlayerGamesFull = (gameReports: PlayerStatsReportsResponseObject[]) => { |
266
|
|
|
return ( |
267
|
|
|
<Card className={`player__stats__games`} title="Wedstrijden" hasTable={true}> |
268
|
|
|
<table className={`player__stats__games__table responsive-card-table`}> |
269
|
|
|
<thead> |
270
|
|
|
<tr> |
271
|
|
|
<th className={`table__column__string`}>Team</th> |
272
|
|
|
<th className={`table__column__string`}>Type</th> |
273
|
|
|
<th className={`table__column__string`}>Datum</th> |
274
|
|
|
<th className={`table__column__number`}> |
275
|
|
|
<span title="Thuis/uit">H/A</span> |
276
|
|
|
</th> |
277
|
|
|
<th className={`table__column__string table__column__score`}>Score</th> |
278
|
|
|
<th className={`table__column__string`}>Tegenstander</th> |
279
|
|
|
<th className={`table__column__number`}> |
280
|
|
|
<img src={iconCardYellow} title="Gele kaart" alt="Gele kaart" className="table__header__icon" /> |
281
|
|
|
</th> |
282
|
|
|
<th className={`table__column__number`}> |
283
|
|
|
<img src={iconCardRed} title="Rode kaart" alt="Rode kaart" className="table__header__icon" /> |
284
|
|
|
</th> |
285
|
|
|
<th className={`table__column__number`}> |
286
|
|
|
<img src={iconGoal} title="Doelpunten gescoord" alt="Rode kaart" className="table__header__icon" /> |
287
|
|
|
</th> |
288
|
|
|
<th className={`table__column__number`}> |
289
|
|
|
<span title="Minuten gespeeld"> |
290
|
|
|
<Icon icon="fa-clock-o" /> |
291
|
|
|
</span> |
292
|
|
|
</th> |
293
|
|
|
</tr> |
294
|
|
|
</thead> |
295
|
|
|
<tbody> |
296
|
|
|
{gameReports.map(function (game) { |
297
|
|
|
return ( |
298
|
|
|
<tr> |
299
|
|
|
<td data-label="Team" className={`table__column__string`}> |
300
|
|
|
{game.team?.replace(`Voetbal : `, ``)} |
301
|
|
|
</td> |
302
|
|
|
<td data-label="Type" className={`table__column__string`}> |
303
|
|
|
{game.competition} |
304
|
|
|
</td> |
305
|
|
|
<td data-label="Datum" className={`table__column__string`}> |
306
|
|
|
{game.date && DateTime.fromFormat(game.date, `yyyy-MM-dd HH:mm`).toFormat(`dd/MM/yyyy`)} |
307
|
|
|
</td> |
308
|
|
|
<td data-label="Thuis/uit" className={`table__column__number`}> |
309
|
|
|
{game.home ? ( |
310
|
|
|
<span className={`player__stats__games__home`} title="Thuiswedstrijd"> |
311
|
|
|
<Icon icon="fa-home" alt="Thuiswedstrijd" /> |
312
|
|
|
</span> |
313
|
|
|
) : ( |
314
|
|
|
<span className={`player__stats__games__away`} title="Uitwedstrijd"> |
315
|
|
|
<Icon icon="fa-bus" alt="Uitwedstrijd" /> |
316
|
|
|
</span> |
317
|
|
|
)} |
318
|
|
|
</td> |
319
|
|
|
<td data-label="Score" className={`table__column__string table__column__score`}> |
320
|
|
|
{game.goalsHomeTeam} - {game.goalsAwayTeam} |
321
|
|
|
</td> |
322
|
|
|
<td data-label="Tegenstander" className={`table__column__string`}> |
323
|
|
|
{game.opponent} |
324
|
|
|
</td> |
325
|
|
|
<td data-label="Gele kaart(en)" className={`table__column__number`}> |
326
|
|
|
{game.yellowCards} |
327
|
|
|
</td> |
328
|
|
|
<td data-label="Rode kaart(en)" className={`table__column__number`}> |
329
|
|
|
{game.redCards} |
330
|
|
|
</td> |
331
|
|
|
<td data-label="Doelpunten" className={`table__column__number`}> |
332
|
|
|
{game.goals} |
333
|
|
|
</td> |
334
|
|
|
<td data-label="Speeltijd" className={`table__column__number`}> |
335
|
|
|
{game.minutesPlayed}' |
336
|
|
|
</td> |
337
|
|
|
</tr> |
338
|
|
|
) |
339
|
|
|
})} |
340
|
|
|
</tbody> |
341
|
|
|
</table> |
342
|
|
|
</Card> |
343
|
|
|
) |
344
|
|
|
} |
345
|
|
|
|
346
|
|
|
export const query = graphql` |
347
|
|
|
query PlayerQuery($slug: String!) { |
348
|
|
|
nodePlayer(path: { alias: { eq: $slug } }) { |
349
|
|
|
path { |
350
|
|
|
alias |
351
|
|
|
} |
352
|
|
|
body { |
353
|
|
|
processed |
354
|
|
|
} |
355
|
|
|
title |
356
|
|
|
field_join_date |
357
|
|
|
field_date_leave |
358
|
|
|
field_lastname |
359
|
|
|
field_position |
360
|
|
|
field_firstname |
361
|
|
|
field_birth_date |
362
|
|
|
field_shirtnumber |
363
|
|
|
field_vv_id |
364
|
|
|
relationships { |
365
|
|
|
node__article { |
366
|
|
|
title |
367
|
|
|
timestamp: created(formatString: "x") |
368
|
|
|
path { |
369
|
|
|
alias |
370
|
|
|
} |
371
|
|
|
relationships { |
372
|
|
|
field_media_article_image { |
373
|
|
|
...ArticleImage |
374
|
|
|
} |
375
|
|
|
} |
376
|
|
|
} |
377
|
|
|
node__team { |
378
|
|
|
title |
379
|
|
|
relationships { |
380
|
|
|
field_media_article_image { |
381
|
|
|
...ArticleImage |
382
|
|
|
} |
383
|
|
|
} |
384
|
|
|
path { |
385
|
|
|
alias |
386
|
|
|
} |
387
|
|
|
} |
388
|
|
|
field_image { |
389
|
|
|
localFile { |
390
|
|
|
...KCVVFluid480 |
391
|
|
|
} |
392
|
|
|
} |
393
|
|
|
field_image_celebrate { |
394
|
|
|
localFile { |
395
|
|
|
...KCVVFixedPlayerTeaserShare |
396
|
|
|
} |
397
|
|
|
} |
398
|
|
|
} |
399
|
|
|
} |
400
|
|
|
} |
401
|
|
|
` |
402
|
|
|
|