Kaynağa Gözat

worked on search.

Tomi Cvetic 5 yıl önce
ebeveyn
işleme
52b4818c8e

+ 3 - 4
frontend/components/footer.js

@@ -5,10 +5,9 @@ const Footer = props => (
     <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};
+        background-color: ${theme.colors.darkgrey};
+        min-height: 125px;
+        color: ${theme.colors.offWhite};
       }
     `}</style>
   </footer>

+ 9 - 0
frontend/components/header.js

@@ -1,8 +1,17 @@
 import Logo from './logo'
+import Search from './search'
 
 const Header = props => (
   <header>
     <Logo />
+    <Search />
+
+    <style jsx>{`
+header {
+  display: grid;
+  grid-template-columns: 150px 1fr
+}
+    `}</style>
   </header>
 )
 

+ 27 - 0
frontend/components/page.js

@@ -0,0 +1,27 @@
+import Head from 'next/head'
+import Header from './header'
+import Meta from './meta'
+import Nav from './nav'
+import Footer from './footer'
+import GlobalStyle from '../styles/global'
+
+const Page = props =>
+    <>
+        <Meta />
+        <Head>
+            <title>Home</title>
+        </Head>
+
+        <Header />
+        <Nav />
+        <main>
+            {props.children}
+        </main>
+        <Footer />
+
+        <style jsx global>
+            {GlobalStyle}
+        </style>
+    </>
+
+export default Page

+ 102 - 0
frontend/components/search.js

@@ -0,0 +1,102 @@
+import fuse from 'fuse.js'
+
+const dummyResults = [{
+    title: 'good :-)',
+    text: 'a good text.',
+    link: 'good'
+}, {
+    title: 'bad :-(',
+    text: 'a bad text.',
+    link: 'bad'
+}]
+
+class Search extends React.Component {
+    state = {
+        active: false,
+        query: '',
+        results: []
+    }
+
+    focus = ev => {
+        this.setState({ active: true })
+    }
+
+    blur = ev => {
+        this.setState({ active: false })
+    }
+
+    change = ev => {
+        const { value } = ev.target
+        this.setState({ query: ev.target.value })
+        const fuseOptions = {
+            shouldSort: true,
+            includeMatches: true,
+            threshold: 0.3,
+            location: true,
+            keys: ['title', 'text', 'link']
+        }
+        const fuseInst = new fuse(dummyResults, fuseOptions)
+        const searchResults = fuseInst.search(value)
+        console.log(dummyResults, searchResults)
+        if (value) {
+            this.setState({
+                results: searchResults
+            })
+        } else {
+            this.setState({ results: [] })
+        }
+    }
+
+    render() {
+        return (
+            <>
+                <div id='searchbar'>
+                    <input
+                        type="text"
+                        placeholder="Search.."
+                        onFocus={this.focus}
+                        onBlur={this.blur}
+                        onChange={this.change}
+                    />
+                    <button type="submit">Search</button>
+                    {(this.state.active && this.state.query) && (this.state.results ?
+                        (
+                            <ul id='searchresults'>
+                                {this.state.results.map(result => <Result key={result.title} {...result} />)}
+                            </ul>
+                        ) : (
+                            <p>Nothing found.</p>
+                        )
+                    )}
+                </div>
+
+                <style jsx>
+                    {`
+                        input[type=text] {
+                            float: right;
+                            padding: 6px;
+                            border: none;
+                            margin-top: 8px;
+                            margin-right: 16px;
+                            font-size: 17px;
+                        }
+                        `}
+                </style>
+            </>
+        )
+    }
+}
+
+const Result = props => {
+    const { item, matches } = props
+    return (
+        <li>
+            <a href={item.link}>
+                <h3>{item.title}</h3>
+                <p>{item.text}</p>
+            </a>
+        </li>
+    )
+}
+
+export default Search

+ 8 - 0
frontend/components/training.js

@@ -144,6 +144,14 @@ const Block = props => (
         {props.exercises.map(exercise => <Exercise key={exercise.id} {...exercise} />)}
       </ol>
     </section>
+
+    <style jsx>
+      {`
+        section {
+          display: grid;
+        }
+      `}
+    </style>
   </li>
 )
 

+ 5 - 0
frontend/package-lock.json

@@ -4068,6 +4068,11 @@
       "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
       "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc="
     },
+    "fuse.js": {
+      "version": "3.4.5",
+      "resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-3.4.5.tgz",
+      "integrity": "sha512-s9PGTaQIkT69HaeoTVjwGsLfb8V8ScJLx5XGFcKHg0MqLUH/UZ4EKOtqtXX9k7AFqCGxD1aJmYb8Q5VYDibVRQ=="
+    },
     "get-stdin": {
       "version": "7.0.0",
       "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-7.0.0.tgz",

+ 2 - 1
frontend/package.json

@@ -15,6 +15,7 @@
     "apollo-link-error": "^1.1.12",
     "apollo-link-http": "^1.5.16",
     "dotenv": "^8.2.0",
+    "fuse.js": "3.4.5",
     "graphql": "^14.5.8",
     "graphql-tag": "^2.10.1",
     "next": "9.1.2",
@@ -28,4 +29,4 @@
     "standard": "^14.3.1",
     "styled-components": "^4.4.1"
   }
-}
+}

+ 4 - 1
frontend/pages/_app.js

@@ -1,6 +1,7 @@
 import App from 'next/app'
 import { ApolloProvider } from 'react-apollo'
 import withApollo from '../lib/withApollo'
+import Page from '../components/page'
 
 /**
  * Next.js uses the `App` component to initialize pages. See:
@@ -31,7 +32,9 @@ class MyApp extends App {
 
     return (
       <ApolloProvider client={apollo}>
-        <Component {...pageProps} />
+        <Page>
+          <Component {...pageProps} />
+        </Page>
       </ApolloProvider>
     )
   }

+ 19 - 30
frontend/pages/index.js

@@ -1,40 +1,29 @@
-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'
-import Footer from '../components/footer'
-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(data)
 
-const Home = () => (
+const Home = () =>
   <>
-    <Meta />
-    <Head>
-      <title>Home</title>
-    </Head>
+    <section>
+      <h1>Stay in Shape with u-fit</h1>
+      <p>u-fit is a high intensity interval training offered by u-blox.</p>
+      <aside>
+        <div id='trainingTime'>
+          <span className='caption'>When</span>
+          <span className='data'>Tuesdays, 11:45-12:30</span>
+        </div>
+        <div id='trainingEquipment'>
+          <span className='caption'>Equipment</span>
+          <span className='data'>Towel, water, optional: yoga mat</span>
+        </div>
+      </aside>
+    </section>
 
-    <Header />
-    <Nav />
-    <main>
-      <h1>Welcome to u-fit</h1>
-      <p>The right position to make every cell stronger over a short range!</p>
+    <section>
+      <h1>Your Next Training</h1>
       <Training {...data.trainings[data.trainings.length - 1]} />
-    </main>
-    <Footer />
-    {GlobalStyle}
-
-    <style jsx>{`
-    
-    `}</style>
+    </section>
   </>
-)
 
 export default Home

+ 10 - 9
frontend/styles/global.js

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

+ 1 - 0
frontend/styles/theme.js

@@ -3,6 +3,7 @@ const theme = {
         lightred: '#f0b0b0',
         red: '#f03535',
         black: '#393939',
+        darkgrey: '#5d6a6b',
         grey: '#7f8c8d',
         lightgrey: '#95a5a5',
         lighterblue: '#d6e4f0',