Ver código fonte

work in progress

Tomi Cvetic 5 anos atrás
pai
commit
c682158086

+ 5 - 1
.vscode/settings.json

@@ -4,5 +4,9 @@
     "javascript.validate.enable": false,
     "emmet.includeLanguages": {
         "javascript": "javascriptreact"
-    }
+    },
+    "prettifySymbolsMode.adjustCursorMovement": true,
+    "editor.fontFamily": "'Fira Code', 'Droid Sans Mono', 'monospace', monospace, 'Droid Sans Fallback'",
+    "editor.fontLigatures": true,
+    "files.trimTrailingWhitespace": true
 }

+ 1 - 1
backend/src/resolvers.js

@@ -2,7 +2,7 @@ const { forwardTo } = require('prisma-binding')
 const bcrypt = require('bcryptjs')
 const jwt = require('jsonwebtoken')
 
-const LoginError = new Error('ERR_LOGIN: You must be logged in.')
+const LoginError = new Error('*ERR_LOGIN* You must be logged in.')
 
 const Query = {
   users: forwardTo('db'),

+ 2 - 20
frontend/components/exercise.js

@@ -1,27 +1,9 @@
-const ExerciseList = props => (
-  <div>
-    <h2>
-      {props.exercises.map(exercise => (
-        <ExerciseHint key={exercise.id} exercise={exercise} />
-      ))}
-    </h2>
-  </div>
-);
-
-const ExerciseHint = props => (
-  <div>
-    <h3>{props.exercise.name}</h3>
-    <p>{props.exercise.description}</p>
-  </div>
-);
-
 const Exercise = props => (
   <div>
     <h3>{props.exercise.name}</h3>
     <p>{props.exercise.description}</p>
     <p>{props.exercise.video}</p>
   </div>
-);
+)
 
-export { ExerciseList };
-export default Exercise;
+export default Exercise

+ 1 - 0
frontend/components/login.js

@@ -49,6 +49,7 @@ const LoginForm = props => (
         <button type='submit'>Login!</button>
       </Form>
     )}
+    {props.children}
   </LoginAdoption>
 )
 

+ 44 - 0
frontend/components/media.js

@@ -0,0 +1,44 @@
+
+const Youtube = props => {
+  const { link, rest } = props
+  const src = link.match(/\?v=(.*)/)[1]
+  return (
+    <iframe
+      width='285'
+      height='160'
+      src={`https://www.youtube.com/embed/${src}`}
+      frameBorder='0'
+      allow='accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture'
+      allowFullScreen
+      {...rest}
+    />
+  )
+}
+
+const Spotify = props => {
+  const { link, rest } = props
+  const src = link.match(/track\/(.*)/)[1]
+  return (
+    <iframe
+      src={`https://open.spotify.com/embed/track/${src}`}
+      width='300'
+      height='80'
+      frameBorder='0'
+      allowtransparency='true'
+      allow='encrypted-media'
+      {...rest}
+    />
+  )
+}
+
+const Media = props => {
+  if (props.link.includes('youtube.com')) {
+    return <Youtube {...props} />
+  } else if (props.link.includes('spotify.com')) {
+    return <Spotify {...props} />
+  } else {
+    return <p>Link not recognized.</p>
+  }
+}
+
+export default Media

+ 14 - 0
frontend/components/track.js

@@ -0,0 +1,14 @@
+import Media from './media'
+
+const Track = props => {
+  return (
+    <section>
+      <p>
+        Track {props.id}: {props.title} ({props.artist})
+      </p>
+      <Media link={props.link} />
+    </section>
+  )
+}
+
+export default Track

+ 3 - 171
frontend/components/training.js

@@ -1,11 +1,8 @@
 import theme from '../styles/theme'
-import { Formik, Form } from 'formik'
-import { Query } from 'react-apollo'
 
-import { TRAINING } from '../lib/graphql'
-import { TextInput } from '../lib/forms'
+import TrainingBlock from './trainingBlock'
 
-function calculateRating(ratings) {
+function calculateRating (ratings) {
   const numberOfRatings = ratings.length
   const sumOfRatings = ratings.reduce(
     (accumulator, rating) => accumulator + rating.value,
@@ -14,24 +11,6 @@ function calculateRating(ratings) {
   return numberOfRatings ? sumOfRatings / numberOfRatings : '-'
 }
 
-const TrainingArchive = props => (
-  <div>
-    <h2>Training Archive</h2>
-    <ol>
-      {props.trainings.map(training => (
-        <TrainingHint key={training.id} training={training} />
-      ))}
-    </ol>
-  </div>
-)
-
-const TrainingHint = props => (
-  <div>
-    <div>{props.training.date}</div>
-    <div>{props.training.title}</div>
-  </div>
-)
-
 const Training = props => (
   <article>
     <h2>{props.title}</h2>
@@ -78,7 +57,7 @@ const Training = props => (
         {props.content
           .sort(block => block.sequence)
           .map(block => (
-            <Block key={block.id} {...block} />
+            <TrainingBlock key={block.id} {...block} />
           ))}
       </ol>
     </section>
@@ -133,151 +112,4 @@ const Training = props => (
   </article>
 )
 
-const Youtube = props => {
-  const { link, rest } = props
-  const [crap, src] = props.link.match(/\?v=(.*)/)
-  return (
-    <iframe
-      width='285'
-      height='160'
-      src={`https://www.youtube.com/embed/${src}`}
-      frameBorder='0'
-      allow='accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture'
-      allowFullScreen
-      {...rest}
-    />
-  )
-}
-
-const Spotify = props => {
-  const src = props.link.match(/track\/(.*)/)[1]
-  return (
-    <iframe
-      src={`https://open.spotify.com/embed/track/${src}`}
-      width='300'
-      height='80'
-      frameBorder='0'
-      allowtransparency='true'
-      allow='encrypted-media'
-    />
-  )
-}
-
-const Media = props => {
-  if (props.link.includes('youtube.com')) {
-    return <Youtube {...props} />
-  } else if (props.link.includes('spotify.com')) {
-    return <Spotify {...props} />
-  } else {
-    return <p>Link not recognized.</p>
-  }
-}
-
-const Block = props => (
-  <li>
-    <h2>{props.title}</h2>
-    <p>
-      <span className='caption'>Duration: </span>
-      <span className='data'>{props.duration}</span>
-    </p>
-    <p>
-      <span className='caption'>Variation: </span>
-      <span className='data'>{props.variation}</span>
-    </p>
-    <p>
-      <span className='caption'>Description: </span>
-      <span className='data'>{props.description}</span>
-    </p>
-    <p>
-      <span className='caption'>Format: </span>
-      <span className='data'>
-        {props.format.name}{' '}
-        <sup>
-          <a title={props.format.description}>[?]</a>
-        </sup>
-      </span>
-    </p>
-    <section>
-      <h2>Tracks</h2>
-      <ol>
-        {props.tracks.map(track => (
-          <Track key={track.id} {...track} />
-        ))}
-      </ol>
-    </section>
-    <section>
-      <h2>Exercises</h2>
-      <ol>
-        {props.exercises.map(exercise => (
-          <Exercise key={exercise.id} {...exercise} />
-        ))}
-      </ol>
-    </section>
-
-    <style jsx>
-      {`
-        section {
-          display: grid;
-        }
-      `}
-    </style>
-  </li>
-)
-
-const Track = props => {
-  return (
-    <section>
-      <p>
-        Track {props.id}: {props.title} ({props.artist})
-      </p>
-      <Media link={props.link} />
-    </section>
-  )
-}
-
-const Exercise = props => {
-  return (
-    <section>
-      <p>
-        Exercise {props.id}: {props.name}
-      </p>
-    </section>
-  )
-}
-
-const TrainingCreateForm = props => (
-  <Formik
-    initialVariables={{
-      title: '',
-      type: '',
-      content: [],
-      trainingDate: '',
-      location: '',
-      registration: [],
-      attendance: 0,
-      ratings: [],
-      published: false
-    }}
-    onSubmit={ev => console.log(ev)}
-  >
-    {() => (
-      <Form>
-        <label htmlFor='title'>
-          Title
-          <input type='text' id='title' />
-        </label>
-        <label htmlFor='title'>
-          Title
-          <input type='text' id='title' />
-        </label>
-        <label htmlFor='title'>
-          Title
-          <input type='text' id='title' />
-        </label>
-      </Form>
-    )}
-  </Formik>
-)
-
-export { TrainingArchive, TrainingCreateForm }
 export default Training

+ 14 - 0
frontend/components/trainingArchive.js

@@ -0,0 +1,14 @@
+import TrainingHint from './trainingHint'
+
+const TrainingArchive = props => (
+  <div>
+    <h2>Training Archive</h2>
+    <ol>
+      {props.trainings.map(training => (
+        <TrainingHint key={training.id} training={training} />
+      ))}
+    </ol>
+  </div>
+)
+
+export default TrainingArchive

+ 55 - 0
frontend/components/trainingBlock.js

@@ -0,0 +1,55 @@
+import Track from './track'
+import Exercise from './exercise'
+
+const TrainingBlock = props => (
+  <li>
+    <h2>{props.title}</h2>
+    <p>
+      <span className='caption'>Duration: </span>
+      <span className='data'>{props.duration}</span>
+    </p>
+    <p>
+      <span className='caption'>Variation: </span>
+      <span className='data'>{props.variation}</span>
+    </p>
+    <p>
+      <span className='caption'>Description: </span>
+      <span className='data'>{props.description}</span>
+    </p>
+    <p>
+      <span className='caption'>Format: </span>
+      <span className='data'>
+        {props.format.name}{' '}
+        <sup>
+          <a title={props.format.description}>[?]</a>
+        </sup>
+      </span>
+    </p>
+    <section>
+      <h2>Tracks</h2>
+      <ol>
+        {props.tracks.map(track => (
+          <Track key={track.id} {...track} />
+        ))}
+      </ol>
+    </section>
+    <section>
+      <h2>Exercises</h2>
+      <ol>
+        {props.exercises.map(exercise => (
+          <Exercise key={exercise.id} {...exercise} />
+        ))}
+      </ol>
+    </section>
+
+    <style jsx>
+      {`
+          section {
+            display: grid;
+          }
+        `}
+    </style>
+  </li>
+)
+
+export default TrainingBlock

+ 2 - 2
frontend/pages/createTraining.js → frontend/components/trainingForm.js

@@ -24,7 +24,7 @@ const TrainingAdoption = adopt({
     </Formik>)
 })
 
-const CreateTraining = props => (
+const TrainingForm = props => (
   <TrainingAdoption>
     {({ training, form }) => (
       <Form>
@@ -46,4 +46,4 @@ const CreateTraining = props => (
   </TrainingAdoption>
 )
 
-export default CreateTraining
+export default TrainingForm

+ 8 - 0
frontend/components/trainingHint.js

@@ -0,0 +1,8 @@
+const TrainingHint = props => (
+  <div>
+    <div>{props.training.date}</div>
+    <div>{props.training.title}</div>
+  </div>
+)
+
+export default TrainingHint

+ 39 - 24
frontend/components/user.js

@@ -32,34 +32,27 @@ class UserNav extends React.Component {
   render() {
     return (
       <UserAdoption>{({ user, logout }) => {
-        console.log(user, logout)
-        if (user.error) return <LoginForm />
-        if (user.loading) return <p>Loading...</p>
+        if (user.loading) return (
+          <p>Loading...</p>
+        )
+        if (user.error) return (
+          <LoginForm>
+            <style jsx>
+              {`
+            label {
+              display: inline;
+            }
+          `}
+            </style>
+          </LoginForm>
+        )
         const { name, email, id } = user.data.me
         return (
           <>
             <a href='' onClick={this.toggleMenu}>{name}</a>
-            {
-              this.state.menu ? (
-                <section className='usermenu'>
-                  <h2>Welcome, {name}</h2>
-                  <Link href={{ path: 'user' }}><a>Edit user data</a></Link>
-                  <a href='' onClick={ev => {
-                    ev.preventDefault()
-                    this.logout(ev, logout)
-                  }}>Logout</a>
-                </section>
-              ) : null
-            }
-
-            <style jsx>
-              {`
-section.usermenu {
-  position: absolute;
-  background: rgba(127,0,0,0.5);
-}
-                `}
-            </style>
+            {this.state.menu || (
+              <UserNavMenu />
+            )}
           </>
         )
       }}
@@ -68,6 +61,28 @@ section.usermenu {
   }
 }
 
+const UserNavMenu = props => (
+  <>
+    <section className='usermenu'>
+      <h2>Welcome, {name}</h2>
+      <Link href={{ path: 'user' }}><a>Edit user data</a></Link>
+      <a href='' onClick={ev => {
+        ev.preventDefault()
+        this.logout(ev, logout)
+      }}>Logout</a>
+    </section>
+
+    <style jsx>
+      {`
+section.usermenu {
+  position: absolute;
+  background: rgba(127,0,0,0.5);
+}
+                `}
+    </style>
+  </>
+)
+
 const User = props => <a />
 
 export { UserNav }

+ 16 - 13
frontend/lib/withApollo.js

@@ -9,25 +9,28 @@
 
 import withApollo from 'next-with-apollo'
 import { ApolloClient } from 'apollo-client'
+import { ApolloLink } from 'apollo-link'
 import { InMemoryCache } from 'apollo-cache-inmemory'
 import { HttpLink } from 'apollo-link-http'
 import { onError } from 'apollo-link-error'
-import { ApolloLink } from 'apollo-link'
 
 const cache = new InMemoryCache()
-const link = new HttpLink({ uri: 'http://localhost:8801/', credentials: 'include' })
+const httpLink = new HttpLink({ uri: 'http://localhost:8801/', credentials: 'include' })
+const errorLink = onError(({ graphQLErrors, networkError, response }) => {
+  console.log('ERRORS', graphQLErrors, networkError)
+  if (graphQLErrors) {
+    graphQLErrors.map(({ message, locations, path }) => {
+      console.log(`[GraphQL error] Message: ${message}, Location: ${locations}, Path: ${path}`)
+    })
+  }
+  if (networkError) {
+    console.log(`[Network error] ${networkError}`)
+  }
+})
 
-const oldLink = ApolloLink.from([
-  onError(({ graphQLErrors, networkError }) => {
-    if (graphQLErrors) {
-      graphQLErrors.map(({ message, locations, path }) =>
-        console.log(
-          `[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`
-        )
-      )
-    }
-    if (networkError) console.log(`[Network error]: ${networkError}`)
-  })
+const link = ApolloLink.from([
+  errorLink,
+  httpLink
 ])
 
 function createClient ({ ctx, headers }) {

+ 1 - 1
frontend/package.json

@@ -37,4 +37,4 @@
   "standard": {
     "parser": "babel-eslint"
   }
-}
+}

+ 1 - 1
frontend/pages/index.js

@@ -47,7 +47,7 @@ const Home = () => (
         }
       }
       </Query>
-      <Link href={{ pathname: '/createTraining' }}><a>create training...</a></Link>
+      <Link href={{ pathname: '/training' }}><a>create training...</a></Link>
     </section>
   </>
 )

+ 7 - 0
frontend/pages/training.js

@@ -0,0 +1,7 @@
+import TrainingForm from '../components/trainingForm'
+
+const Training = props => (
+  <TrainingForm />
+)
+
+export default Training