training.js 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. import theme from '../styles/theme'
  2. import { Formik, Form } from 'formik'
  3. import { Query } from 'react-apollo'
  4. import { TRAINING } from '../lib/graphql'
  5. import { TextInput } from '../lib/forms'
  6. function calculateRating(ratings) {
  7. const numberOfRatings = ratings.length
  8. const sumOfRatings = ratings.reduce(
  9. (accumulator, rating) => accumulator + rating.value,
  10. 0
  11. )
  12. return numberOfRatings ? sumOfRatings / numberOfRatings : '-'
  13. }
  14. const TrainingArchive = props => (
  15. <div>
  16. <h2>Training Archive</h2>
  17. <ol>
  18. {props.trainings.map(training => (
  19. <TrainingHint key={training.id} training={training} />
  20. ))}
  21. </ol>
  22. </div>
  23. )
  24. const TrainingHint = props => (
  25. <div>
  26. <div>{props.training.date}</div>
  27. <div>{props.training.title}</div>
  28. </div>
  29. )
  30. const Training = props => (
  31. <article>
  32. <h2>{props.title}</h2>
  33. <aside>
  34. <div id='trainingType'>
  35. <span className='caption'>Type: </span>
  36. <span className='data'>{props.type.name}</span>
  37. </div>
  38. <div id='trainingDate'>
  39. <span className='caption'>Date: </span>
  40. <span className='data'>
  41. {new Date(props.trainingDate).toLocaleDateString()}
  42. </span>
  43. </div>
  44. <div id='trainingLocation'>
  45. <span className='caption'>Location: </span>
  46. <span className='data'>{props.location}</span>
  47. </div>
  48. <div id='trainingRegistrations'>
  49. <span className='caption'>Registrations: </span>
  50. <span className='data'> {props.registration.length} </span>
  51. </div>
  52. <div id='trainingAttendance'>
  53. <span className='caption'>Attendance: </span>
  54. <span className='data'>{props.attendance}</span>
  55. </div>
  56. <div id='trainingRatings'>
  57. <span className='caption'>Rating: </span>
  58. <span className='data'>
  59. {calculateRating(props.ratings)} [
  60. <a href=''>{props.ratings.length}</a>] Rate it!
  61. <a href=''>*</a>
  62. <a href=''>*</a>
  63. <a href=''>*</a>
  64. <a href=''>*</a>
  65. <a href=''>*</a>
  66. </span>
  67. </div>
  68. <button>Register now!</button>
  69. </aside>
  70. <section>
  71. <h3>Content</h3>
  72. <ol>
  73. {props.content
  74. .sort(block => block.sequence)
  75. .map(block => (
  76. <Block key={block.id} {...block} />
  77. ))}
  78. </ol>
  79. </section>
  80. <style jsx>
  81. {`
  82. article {
  83. display: grid;
  84. grid-template-areas:
  85. 'title title'
  86. 'information placeholder'
  87. 'content content';
  88. grid-template-columns: 1fr 2fr;
  89. background-color: rgba(127, 127, 127, 0.5);
  90. background-image: url('media/man_working_out.jpg');
  91. background-size: auto 400px;
  92. background-repeat: no-repeat;
  93. margin: 2em 0;
  94. }
  95. article > * {
  96. padding: 0.2em 1em;
  97. }
  98. article > h2 {
  99. grid-area: title;
  100. font-weight: 900;
  101. font-size: 120%;
  102. background: ${theme.colors.darkerblue};
  103. color: ${theme.colors.offWhite};
  104. }
  105. aside {
  106. grid-area: information;
  107. background: rgba(0, 127, 0, 0.5);
  108. padding: 1em 2em;
  109. margin: 0 1em;
  110. min-height: 350px;
  111. }
  112. section {
  113. grid-area: content;
  114. padding: 1em 2em;
  115. background: rgba(127, 0, 0, 0.5);
  116. }
  117. span.caption {
  118. display: none;
  119. }
  120. `}
  121. </style>
  122. </article>
  123. )
  124. const Youtube = props => {
  125. const { link, rest } = props
  126. const [crap, src] = props.link.match(/\?v=(.*)/)
  127. return (
  128. <iframe
  129. width='285'
  130. height='160'
  131. src={`https://www.youtube.com/embed/${src}`}
  132. frameBorder='0'
  133. allow='accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture'
  134. allowFullScreen
  135. {...rest}
  136. />
  137. )
  138. }
  139. const Spotify = props => {
  140. const src = props.link.match(/track\/(.*)/)[1]
  141. return (
  142. <iframe
  143. src={`https://open.spotify.com/embed/track/${src}`}
  144. width='300'
  145. height='80'
  146. frameBorder='0'
  147. allowtransparency='true'
  148. allow='encrypted-media'
  149. />
  150. )
  151. }
  152. const Media = props => {
  153. if (props.link.includes('youtube.com')) {
  154. return <Youtube {...props} />
  155. } else if (props.link.includes('spotify.com')) {
  156. return <Spotify {...props} />
  157. } else {
  158. return <p>Link not recognized.</p>
  159. }
  160. }
  161. const Block = props => (
  162. <li>
  163. <h2>{props.title}</h2>
  164. <p>
  165. <span className='caption'>Duration: </span>
  166. <span className='data'>{props.duration}</span>
  167. </p>
  168. <p>
  169. <span className='caption'>Variation: </span>
  170. <span className='data'>{props.variation}</span>
  171. </p>
  172. <p>
  173. <span className='caption'>Description: </span>
  174. <span className='data'>{props.description}</span>
  175. </p>
  176. <p>
  177. <span className='caption'>Format: </span>
  178. <span className='data'>
  179. {props.format.name}{' '}
  180. <sup>
  181. <a title={props.format.description}>[?]</a>
  182. </sup>
  183. </span>
  184. </p>
  185. <section>
  186. <h2>Tracks</h2>
  187. <ol>
  188. {props.tracks.map(track => (
  189. <Track key={track.id} {...track} />
  190. ))}
  191. </ol>
  192. </section>
  193. <section>
  194. <h2>Exercises</h2>
  195. <ol>
  196. {props.exercises.map(exercise => (
  197. <Exercise key={exercise.id} {...exercise} />
  198. ))}
  199. </ol>
  200. </section>
  201. <style jsx>
  202. {`
  203. section {
  204. display: grid;
  205. }
  206. `}
  207. </style>
  208. </li>
  209. )
  210. const Track = props => {
  211. return (
  212. <section>
  213. <p>
  214. Track {props.id}: {props.title} ({props.artist})
  215. </p>
  216. <Media link={props.link} />
  217. </section>
  218. )
  219. }
  220. const Exercise = props => {
  221. return (
  222. <section>
  223. <p>
  224. Exercise {props.id}: {props.name}
  225. </p>
  226. </section>
  227. )
  228. }
  229. const TrainingCreateForm = props => (
  230. <Formik
  231. initialVariables={{
  232. title: '',
  233. type: '',
  234. content: [],
  235. trainingDate: '',
  236. location: '',
  237. registration: [],
  238. attendance: 0,
  239. ratings: [],
  240. published: false
  241. }}
  242. onSubmit={ev => console.log(ev)}
  243. >
  244. {() => (
  245. <Form>
  246. <label htmlFor='title'>
  247. Title
  248. <input type='text' id='title' />
  249. </label>
  250. <label htmlFor='title'>
  251. Title
  252. <input type='text' id='title' />
  253. </label>
  254. <label htmlFor='title'>
  255. Title
  256. <input type='text' id='title' />
  257. </label>
  258. </Form>
  259. )}
  260. </Formik>
  261. )
  262. export { TrainingArchive, TrainingCreateForm }
  263. export default Training