浏览代码

worked mainly on styles

Tomi Cvetic 5 年之前
父节点
当前提交
f48b119d64

+ 10 - 0
frontend/components/footer.js

@@ -1,6 +1,16 @@
+import theme from '../styles/theme'
+
 const Footer = props => (
   <footer>
     <p>Footer</p>
+    <style jsx>{`
+      footer {
+        background-color: ${theme.colors.lighterblue};
+        min-height: 75px;
+        border-top: 1px solid ${theme.colors.darkblue};
+        color: ${theme.colors.darkerblue};
+      }
+    `}</style>
   </footer>
 )
 

+ 0 - 1
frontend/components/login.js

@@ -28,7 +28,6 @@ class Login extends React.Component {
                 return <form method="post" onSubmit={async ev => {
                     ev.preventDefault()
                     const res = await signin()
-                    console.log(res)
                     this.setState({ name: '', email: '', password: '' })
                 }}>
                     <fieldset disabled={loading} aria-busy={loading}>

+ 3 - 2
frontend/components/logo.js

@@ -8,8 +8,9 @@ const Logo = () => (
         display: grid;
         grid-template-columns: 60px 1fr;
         grid-auto-flow: column;
-        grid-spacing: 3px;
-        font-family: roboto_black;
+        grid-gap: 3px;
+        font-family: "roboto";
+        font-weight: 900;
         font-size: 40px;
         line-height: 1;
       }

+ 1 - 1
frontend/components/meta.js

@@ -4,7 +4,7 @@ const Meta = () => (
   <Head>
     <meta name='viewport' content='width=device-width, initial-scale=1' />
     <meta charSet='utf-8' />
-    <link rel='shortcut icon' href='/favicon.svg' />
+    <link rel='shortcut icon' href='/favicon.ico' />
     <link rel='stylesheet' type='text/css' href='/nprogress.css' />
     <link rel='stylesheet' type='text/css' href='/reset.css' />
     <title>u-fit</title>

+ 0 - 108
frontend/components/page.js

@@ -1,108 +0,0 @@
-import React from 'react'
-import styled, { ThemeProvider, createGlobalStyle } from 'styled-components'
-import Meta from './meta'
-
-const theme = {
-  lightred: '#f0b0b0',
-  red: '#f03535',
-  black: '#393939',
-  grey: '#7f8c8d',
-  lightgrey: '#95a5a5',
-  lighterblue: '#d6e4f0',
-  lightblue: '#b0d3f0',
-  blue: '#4482c3',
-  darkblue: '#285680',
-  darkerblue: '#204567',
-  offWhite: '#EDEDED',
-  maxWidth: '1000px',
-  bs: '0 12px 24px 0 rgba(0,0,0,0.09)',
-  bsSmall: '0 5px 10px 0 rgba(0,0,0,0.19)'
-}
-
-const StyledPage = styled.div`
-  background: white;
-  color: ${props => props.theme.black};
-`
-
-const Inner = styled.div`
-  max-width: ${props => props.theme.maxWidth};
-  margin: 0 auto;
-  padding: 2rem;
-`
-
-const GlobalStyle = createGlobalStyle`
-  @font-face {
-    font-family: 'roboto';
-    src: url('/Roboto-Thin.woff2');
-  }
-  @font-face {
-    font-family: 'roboto_mono';
-    src: url('/RobotoMono-Thin.woff2');
-  }
-  @font-face {
-    font-family: 'roboto_black';
-    src: url('/Roboto-Black.woff2');
-  }
-  
-  html {
-    box-sizing: border-box;
-    font-size: 12px;
-  }
-
-  *, *:before, *:after {
-    box-sizing: inherit;
-  }
-
-  body {
-    padding: 0;
-    margin: 0;
-    font-size: 1.5rem;
-    line-height: 2;
-    font-family: 'roboto', sans-serif;
-  }
-
-  h1 {
-    font-family: 'roboto_black';
-  }
-
-  h2, h3, h4, h5, h6 {
-  }
-
-  button {
-    font-family: 'roboto_black';
-    background: ${props => props.theme.darkblue};
-    color: ${props => props.theme.lighterblue};
-    border: 1px solid ${props => props.theme.darkerblue};
-    padding: 0.3em 1.8em;
-    cursor: pointer;
-    box-shadow: ${props => props.theme.bsSmall};
-  }
-
-  input,
-  textarea {
-    font-family: 'roboto';
-    border: 1px solid ${props => props.theme.lightgrey};
-    padding: 6px;
-    margin: 0 8px;
-  }
-
-  pre {
-    font-family: 'roboto_mono';
-  }
-`
-
-class Page extends React.Component {
-  render () {
-    return (
-      <ThemeProvider theme={theme}>
-        <StyledPage>
-          <Meta />
-          <GlobalStyle />
-          <Inner>{this.props.children}</Inner>
-        </StyledPage>
-      </ThemeProvider>
-    )
-  }
-}
-
-export default Page

+ 47 - 23
frontend/components/training.js

@@ -1,17 +1,15 @@
-function calculateRating (ratings) {
+function calculateRating(ratings) {
   const numberOfRatings = ratings.length
   const sumOfRatings = ratings.reduce(
     (accumulator, rating) => accumulator + rating.value,
     0
   )
-  console.log(ratings, sumOfRatings, numberOfRatings)
   return numberOfRatings ? sumOfRatings / numberOfRatings : '-'
 }
 
 const TrainingArchive = props => (
   <div>
     <h2>Training Archive</h2>
-    {console.log('archive', props)}
     <ol>
       {props.trainings.map(training => (
         <TrainingHint key={training.id} training={training} />
@@ -37,9 +35,9 @@ const Training = props => (
       </div>
       <div id='trainingDate'>
         <span className='caption'>Date: </span>
-        <date className='data'>
+        <span className='data'>
           {new Date(props.trainingDate).toLocaleDateString()}
-        </date>
+        </span>
       </div>
       <div id='trainingLocation'>
         <span className='caption'>Location: </span>
@@ -77,25 +75,13 @@ const Training = props => (
             <Block key={block.id} {...block} />
           ))}
       </ol>
-      <iframe
-        width='560'
-        height='315'
-        src='https://www.youtube.com/embed/hRz0qPREKho'
-        frameborder='0'
-        allow='accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture'
-        allowfullscreen
-      />
     </section>
 
     <style jsx>
       {`
-        article {
-          border: 1px solid black;
-        }
-
         article > h2 {
+          font-weight: 900;
           font-size: 120%;
-          font-family: roboto_black;
         }
 
         aside {
@@ -108,6 +94,20 @@ 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 Block = props => (
   <li>
     <h2>{props.title}</h2>
@@ -119,6 +119,10 @@ const Block = props => (
       <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'>
@@ -128,15 +132,35 @@ const Block = props => (
         </sup>
       </span>
     </p>
-    <p>
-      <span className='caption'>Description: </span>
-      <span className='data'>{props.description}</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>
   </li>
 )
 
+const Track = props => {
+  return <section>
+    <p>Track {props.id}: {props.title} ({props.artist})</p>
+    <Youtube link={props.link} />
+  </section>
+}
+
+const Exercise = props => {
+  return <section>
+    <p>Exercise {props.id}: {props.name}</p>
+  </section>
+}
 class TrainingCreateForm extends React.Component {
-  render () {
+  render() {
     return (
       <form>
         <label htmlFor='title'>

+ 38 - 58
frontend/initial-data.js

@@ -35,13 +35,14 @@ const data = {
         }],
         exercises: [{
           id: 'exercise0',
-          name: 'Squat',
-          variations: [{
-            id: 'exerciseVariation0',
-            name: 'lateral jump squat',
-            description: '',
-            link: ''
-          }]
+          name: 'lateral jump squat',
+          description: '',
+          video: '',
+          targets: ['Glutes'],
+          baseExercise: {
+            id: 'baseExercise0',
+            name: 'squat',
+          }
         }],
         description: 'Warm up everything.'
       },
@@ -57,71 +58,50 @@ const data = {
           description: ''
         },
         tracks: [{
+          id: 0,
           artist: 'Daniel Portman',
           title: "You're Not Alone",
-          link: ''
+          duration: 180,
+          link: 'https://www.youtube.com/watch?v=J64jRxcygwg'
         }, {
+          id: 1,
           artist: 'ATFC & David Penn',
           title: "Hipcats",
-          link: ''
+          duration: 180,
+          link: 'https://www.youtube.com/watch?v=9seTXdfNS2Y'
         }, {
+          id: 2,
           artist: 'Eli Brown',
           title: "In the Dance",
-          link: ''
+          duration: 180,
+          link: 'https://www.youtube.com/watch?v=o2mGqfWgZ-k'
         }, {
+          id: 3,
           artist: 'Biscits',
           title: "Do It Like This",
-          link: ''
+          duration: 180,
+          link: 'https://www.youtube.com/watch?v=KwK9bUYCaIg'
         }],
         exercises: [{
-          base: [{
-            name: 'On the Spot Run',
-            variations: [{
-              name: 'regular', description: '', video: ''
-            }, {
-              name: 'high knees', description: '', video: ''
-            }, {
-              name: 'sprint', description: '', video: ''
-            }, {
-              name: 'butt kick', description: '', video: ''
-            }, {
-              name: 'boxer', description: '', video: ''
-            }]
-          }, {
-            name: 'Squat',
-            variations: [{
-              name: 'regular', description: '', video: ''
-            }, {
-              name: 'jump', description: '', video: ''
-            }, {
-              name: 'sumo', description: '', video: ''
-            }, {
-              name: 'drop', description: '', video: ''
-            }, {
-              name: '1/4 turn', description: '', video: ''
-            }, {
-              name: '1/2 turn', description: '', video: ''
-            }, {
-              name: 'prisoner', description: '', video: ''
-            }, {
-              name: 'side-to-side', description: '', video: ''
-            }, {
-              name: 'in-out', description: '', video: ''
-            }]
-          }],
-          variation: '8 boxers, 4 Rotational Drop Squats'
+          id: 'exercise1',
+          name: 'boxer',
+          description: '',
+          video: '',
+          targets: ['Thighs', 'Glutes'],
+          baseExercise: {
+            id: 'baseExercise1',
+            name: 'on the spot run'
+          }
         }, {
-          base: [{
-            name: 'Push-Up',
-            variations: ['regular', 'narrow', 'wide', 'plyo', 'around the world', 'push-back', 'jack']
-          }, {
-            name: 'Frog Jump',
-            variations: ['regular', 'jump', 'sumo', 'drop', '1/4 turn', '1/2 turn', 'prisoner', 'side-to-side', 'in-out']
-          }, {
-            name: 'Kick-Out',
-            variations: ['regular', 'jump', 'sumo', 'drop', '1/4 turn', '1/2 turn', 'prisoner', 'side-to-side', 'in-out']
-          }],
-          variation: '8 boxers, 4 Rotational Drop Squats'
+          id: 'exercise2',
+          name: 'rotational drop squat',
+          description: '',
+          video: '',
+          targets: ['Thighs', 'Glutes'],
+          baseExercise: {
+            id: 'baseExercise0',
+            name: 'squat'
+          }
         }]
       }
     ]

+ 1 - 1
frontend/lib/withApollo.js

@@ -15,7 +15,7 @@ import { ApolloLink } from 'apollo-link'
 
 const cache = new InMemoryCache()
 
-function createClient ({ ctx, headers }) {
+function createClient({ ctx, headers }) {
   return new ApolloClient({
     link: ApolloLink.from([
       onError(({ graphQLErrors, networkError }) => {

+ 3 - 13
frontend/pages/_app.js

@@ -1,5 +1,4 @@
 import App from 'next/app'
-import Page from '../components/page'
 import { ApolloProvider } from 'react-apollo'
 import withApollo from '../lib/withApollo'
 
@@ -7,20 +6,13 @@ import withApollo from '../lib/withApollo'
  * Next.js uses the `App` component to initialize pages. See:
  * https://github.com/zeit/next.js/#custom-app
  *
- * Example how to use it to style child components:
- * https://github.com/zeit/next.js/blob/canary/examples/with-app-layout/pages/_app.js
- *
- * Instead of the Layout component, we use the Page component here,
- * where we add a styled-components theme provider, and Next.js headers and metas.
- * - Using Page with layout information
- *
  * Using next-with-apollo:
  * https://github.com/lfades/next-with-apollo
  * - Wrapping MyApp in withApollo HOC
  */
 
 class MyApp extends App {
-  static async getInitialProps ({ Component, ctx }) {
+  static async getInitialProps({ Component, ctx }) {
     let pageProps = {}
 
     if (Component.getInitialProps) {
@@ -34,14 +26,12 @@ class MyApp extends App {
     return { pageProps }
   }
 
-  render () {
+  render() {
     const { Component, apollo, pageProps } = this.props
 
     return (
       <ApolloProvider client={apollo}>
-        <Page id='page'>
-          <Component {...pageProps} />
-        </Page>
+        <Component {...pageProps} />
       </ApolloProvider>
     )
   }

+ 0 - 43
frontend/pages/_document.js

@@ -1,43 +0,0 @@
-/**
- * _document is only rendered on the server
- * https://github.com/zeit/next.js/#custom-document
- *
- * We're using styled-components, so here is the right place
- * to place the styles.
- * https://www.styled-components.com/docs/advanced#server-side-rendering
- *
- * Configure babel for server-side rendering with styled-components
- * https://dev.to/aprietof/nextjs--styled-components-the-really-simple-guide----101c
- */
-
-import Document from 'next/document'
-import { ServerStyleSheet } from 'styled-components'
-
-class MyDocument extends Document {
-  static async getInitialProps (ctx) {
-    const sheet = new ServerStyleSheet()
-    const originalRenderPage = ctx.renderPage
-
-    try {
-      ctx.renderPage = () =>
-        originalRenderPage({
-          enhanceApp: App => props => sheet.collectStyles(<App {...props} />)
-        })
-
-      const initialProps = await Document.getInitialProps(ctx)
-      return {
-        ...initialProps,
-        styles: (
-          <>
-            {initialProps.styles}
-            {sheet.getStyleElement()}
-          </>
-        )
-      }
-    } finally {
-      sheet.seal()
-    }
-  }
-}
-
-export default MyDocument

+ 14 - 4
frontend/pages/index.js

@@ -1,5 +1,6 @@
 import React from 'react'
 import Head from 'next/head'
+import Meta from '../components/meta'
 import Header from '../components/header'
 import Nav from '../components/nav'
 import Sidebar from '../components/sidebar'
@@ -8,23 +9,32 @@ import Poll from '../components/poll'
 import Training from '../components/training'
 import Exercise from '../components/exercise'
 import { PeopleList } from '../components/people'
+import GlobalStyle from '../styles/global'
+import theme from '../styles/theme'
 import data from '../initial-data.js'
-console.log('home done.')
+console.log(data)
 
 const Home = () => (
-  <div id='app'>
+  <>
+    <Meta />
     <Head>
       <title>Home</title>
-      <link rel='icon' href='/favicon.ico' />
     </Head>
 
     <Header />
     <Nav />
     <main>
+      <h1>Welcome to u-fit</h1>
+      <p>The right position to make every cell stronger over a short range!</p>
       <Training {...data.trainings[data.trainings.length - 1]} />
     </main>
     <Footer />
-  </div>
+    {GlobalStyle}
+
+    <style jsx>{`
+    
+    `}</style>
+  </>
 )
 
 export default Home

二进制
frontend/public/Roboto-Black.woff2


二进制
frontend/public/Roboto-Medium.woff2


二进制
frontend/public/Roboto-Thin.woff2


二进制
frontend/public/RobotoMono-Medium.woff2


二进制
frontend/public/RobotoMono-Thin.woff2


+ 58 - 0
frontend/styles/global.js

@@ -0,0 +1,58 @@
+import theme from './theme'
+
+const GlobalStyle = <style jsx global>{`
+@import url('https://fonts.googleapis.com/css?family=Roboto+Mono:300|Roboto:300,900&display=swap');
+
+html {
+  box-sizing: border-box;
+  font-size: 12px;
+}
+
+*, *:before, *:after {
+  box-sizing: inherit;
+}
+
+body {
+  padding: 0;
+  margin: 0;
+  font-size: 1.5rem;
+  line-height: 2;
+  font-family: 'roboto', sans-serif;
+  font-weight: 300;
+  max-width: ${theme.maxWidth};
+  margin: 0 auto;
+}
+
+#__next {
+  background: ${theme.colors.offWhite};
+  color: ${theme.colors.black};
+  box-shadow: ${theme.bs};
+}
+
+h1, h2, h3, h4, h5, h6 {
+  font-weight: 900;
+}
+
+button {
+  font-weight: 900;
+  background: ${props => props.theme.darkblue};
+  color: ${props => props.theme.lighterblue};
+  border: 1px solid ${props => props.theme.darkerblue};
+  padding: 0.3em 1.8em;
+  cursor: pointer;
+  box-shadow: ${props => props.theme.bsSmall};
+}
+
+input,
+textarea {
+  border: 1px solid ${props => props.theme.lightgrey};
+  padding: 6px;
+  margin: 0 8px;
+}
+
+pre {
+  font-family: 'roboto_mono';
+}
+`}</style>
+
+export default GlobalStyle

+ 20 - 0
frontend/styles/theme.js

@@ -0,0 +1,20 @@
+const theme = {
+    colors: {
+        lightred: '#f0b0b0',
+        red: '#f03535',
+        black: '#393939',
+        grey: '#7f8c8d',
+        lightgrey: '#95a5a5',
+        lighterblue: '#d6e4f0',
+        lightblue: '#b0d3f0',
+        blue: '#4482c3',
+        darkblue: '#285680',
+        darkerblue: '#204567',
+        offWhite: '#EDEDED'
+    },
+    maxWidth: '1000px',
+    bs: '0 12px 24px 0 rgba(0,0,0,0.09)',
+    bsSmall: '0 5px 10px 0 rgba(0,0,0,0.19)'
+}
+
+export default theme