瀏覽代碼

working on forms and mutations.

Tomi Cvetic 5 年之前
父節點
當前提交
28cff43312

+ 2 - 2
frontend/components/Connection.js

@@ -1,7 +1,7 @@
 import styled from 'styled-components'
 import gql from 'graphql-tag'
 import { Mutation, Query } from 'react-apollo'
-import { INTERFACE_LIST } from './InterfaceList'
+import { INTERFACES_FULL } from './InterfaceList'
 
 const StyledConnection = styled.div`
   fieldset {
@@ -58,7 +58,7 @@ class Connection extends React.Component {
           type: 'ask',
           string: this.state.command
         }}
-        refetchQueries={[{ query: INTERFACE_LIST }]}
+        refetchQueries={[{ query: INTERFACES_FULL }]}
         fetchPolicy='no-cache'>
         {(connectionCommand, { data, error, loading }) => (
           <StyledConnection>

+ 33 - 7
frontend/components/File.js

@@ -1,7 +1,7 @@
 import styled from 'styled-components'
 import { endpoint } from '../config'
 
-const ImageStyle = styled.div`
+const FileStyle = styled.div`
   display: grid;
   grid-template-columns: 250px 100px 1fr;
   grid-auto-flow: dense;
@@ -34,26 +34,52 @@ const ImageStyle = styled.div`
   }
 `
 
+const FileFields = {
+  id: null,
+  name: '',
+  description: '',
+  file: null
+}
+
+const FileFormFields = props => {
+  const { state, onChange } = props
+
+  return (
+    <fieldset>
+      <label htmlFor='name'>Name</label>
+      <input type='text' name='name' id='name' placeholder='Name'
+        value={state.name}
+        onChange={onChange} />
+      <label htmlFor='description'>Description</label>
+      <textarea name='description' id='description' placeholder='Description'
+        value={state.description}
+        onChange={onChange} />
+      <input type='file' name='file' onChange={onChange} />
+    </fieldset>
+  )
+}
+
 const File = props => {
   const { path, name, description, filename, mimetype, size } = props.data
   const downloadPath = `${endpoint}/${path}`
   return (
-    <ImageStyle>
+    <FileStyle>
       {mimetype.startsWith('image/') ? (
         <img src={downloadPath} alt={description} />
       ) : (
-        <a href={downloadPath}>
-          <img src={`${endpoint}/static/document-download-solid.png`} alt={description} width={75} />
-        </a>
-      )}
+          <a href={downloadPath}>
+            <img src={`${endpoint}/static/document-download-solid.png`} alt={description} width={75} />
+          </a>
+        )}
       <h2>{name}</h2>
       <p className='description'>{description}</p>
       <p>Path</p><p><a href={downloadPath}>{downloadPath}</a></p>
       <p>Filename</p><p>{filename}</p>
       <p>MIME Type</p><p>{mimetype}</p>
       <p>Size</p><p>{size}</p>
-    </ImageStyle>
+    </FileStyle>
   )
 }
 
 export default File
+export { FileFormFields }

+ 109 - 29
frontend/components/Instrument.js

@@ -1,34 +1,113 @@
 import gql from 'graphql-tag'
 import { Query, Mutation } from 'react-apollo'
-import InstrumentSubsystem from './InstrumentSubsystem'
+import { adopt } from 'react-adopt'
+import InstrumentSubsystem, { InstrumentSubsystemFormFields, InstrumentSubsystemFields } from './InstrumentSubsystem'
+import File, { FileFormFields } from './File'
 import Gallery from './Gallery'
+import { INTERFACES_FULL } from './InterfaceList'
 
-const InstrumentForm = props => {
-  const [state, setState] = React.useState({
-    id: null,
-    name: '',
-    description: '',
-    documents: [],
-    interfaces: [],
-    subsystems: [],
-    ...props.instrument
-  })
-
-  const toState = event => {
-    setState({ [event.target.name]: event.target.value })
+const CREATE_INSTRUMENT = gql`
+  mutation CREATE_INSTRUMENT($name: String!, $description: String!, $interfaces: [String]!) {
+    createInstrument(name: $name, description: $description, interfaces: $interfaces) {
+      id
+    }
   }
+`
 
-  return (
-    <form>
-      <fieldset>
-        <label htmlFor='name'>Name</label>
-        <input type='text' name='name' id='name' placeholder='Name' value={state.name} onChange={onChange} />
-        <label htmlFor='description'>Name</label>
-        <textarea name='description' id='description' placeholder='Description' value={state.description} onChange={onChange} />
-      </fieldset>
-
-    </form>
-  )
+const InstrumentFields = {
+  id: null,
+  name: '',
+  description: '',
+  documents: [],
+  interfaces: [],
+  commands: [],
+  parameters: [],
+  subsystems: []
+}
+
+const MockInterfaces = { interfaces: [{ name: 'serial' }, { name: 'usbtmc' }] }
+
+class InstrumentForm extends React.Component {
+  state = {
+    ...InstrumentFields,
+    ...this.props.instrument
+  }
+
+  toState = event => {
+    this.setState({ [event.target.name]: event.target.value })
+  }
+
+  handleInterface = event => {
+    let interfaces = this.state.interfaces
+    const ifaceSelected = event.target.checked
+    const index = this.state.interfaces.findIndex(iface => iface === event.target.value)
+    if (ifaceSelected && index < 0) interfaces = [
+      ...this.state.interfaces,
+      event.target.value
+    ]
+    if (!ifaceSelected && index >= 0) interfaces = [
+      ...this.state.interfaces.slice(0, index),
+      ...this.state.interfaces.slice(index + 1)
+    ]
+    this.setState({ interfaces })
+  }
+
+  render() {
+    return (
+      <form>
+        <h1>Create new instrument.</h1>
+        <fieldset>
+          <label htmlFor='name'>Name</label>
+          <input type='text' name='name' id='name' placeholder='Name'
+            value={this.state.name}
+            onChange={this.toState} />
+          <label htmlFor='description'>Description</label>
+          <textarea name='description' id='description' placeholder='Description'
+            value={this.state.description}
+            onChange={this.toState} />
+        </fieldset>
+        {this.state.documents.map(file =>
+          <FileFormFields />
+        )}
+        <Query query={INTERFACES_FULL}>
+          {({ data, error, loading }) => {
+            if (loading) return <p>Loading interfaces.</p>
+            //if (error) return <p>Error loading interfaces: {error.message}</p>
+            //if (!data.length) return <p>No interfaces found.</p>
+            return (
+              <fieldset>
+                {MockInterfaces.interfaces.map((iface, index) =>
+                  <div key={iface.name}>
+                    <label htmlFor={iface.name}>{iface.name}</label>
+                    <input type='checkbox' name='interface' id={iface.name}
+                      value={iface.name}
+                      checked={this.state.interfaces.includes(iface.name)}
+                      onChange={this.handleInterface} />
+                  </div>
+                )}
+              </fieldset>
+            )
+          }}
+        </Query>
+        {this.state.subsystems.map((subsystem, index) =>
+          <InstrumentSubsystemFormFields key={index} state={subsystem} onChange={event => {
+            const updatedSubsystem = { ...this.state.subsystems[index] }
+            updatedSubsystem[event.target.name] = event.target.value
+            this.setState({
+              subsystems: [
+                ...this.state.subsystems.slice(0, index),
+                updatedSubsystem,
+                ...this.state.subsystems.slice(index + 1)
+              ]
+            })
+          }} />
+        )}
+        <button type='button' onClick={event => {
+          this.setState({ subsystems: [...this.state.subsystems, InstrumentFields] })
+        }}>Add subsystem</button>
+      </form>
+    )
+  }
 }
 
 const Instrument = props => {
@@ -40,12 +119,13 @@ const Instrument = props => {
       <Gallery title='Documents' items={['Hallo']} />
       <Gallery title='Interfaces' items={['serial', 'usbtmc'].map(item => <div>{item}</div>)} />
       <Gallery title='Subsystems' items={instrument.subsystems.map(subsystem =>
-        <InstrumentSubsystem subsystem={subsystem} />)} />
+        <InstrumentSubsystem subsystem={subsystem} />
+      )} />
     </div>
   ) : (
-    <p>Instrument not found.</p>
-  )
+      <p>Instrument not found.</p>
+    )
 }
 
 export default Instrument
-export { InstrumentForm }
+export { InstrumentFields, InstrumentForm }

+ 15 - 17
frontend/components/InstrumentList.js

@@ -1,6 +1,7 @@
 import gql from 'graphql-tag'
 import { Query } from 'react-apollo'
 import Link from 'next/link'
+import { InstrumentForm } from './Instrument'
 
 const INSTRUMENTS = gql`
   query INSTRUMENTS {
@@ -19,23 +20,20 @@ const InstrumentList = props => (
         <h1>Instrument List</h1>
       ]
       if (loading) content.push(<p>Loading instruments...</p>)
-      if (error) content.push(<p>Error loading instruments: {error.message}</p>)
-
-      if (data.instruments.length > 0) {
-        content.push(
-          <ul>
-            {data.instruments.map(instrument => (
-              <li key={instrument.id} title={instrument.description}>
-                <Link href={{ pathname: 'instruments', query: { id: instrument.id } }}>
-                  <a>{instrument.name}</a>
-                </Link>
-              </li>
-            ))}
-          </ul>
-        )
-      } else {
-        content.push(<p>No instruments found.</p>)
-      }
+      else if (error) content.push(<p>Error loading instruments: {error.message}</p>)
+      else if (data.instruments.length > 0) content.push(
+        <ul>
+          {data.instruments.map(instrument => (
+            <li key={instrument.id} title={instrument.description}>
+              <Link href={{ pathname: 'instruments', query: { id: instrument.id } }}>
+                <a>{instrument.name}</a>
+              </Link>
+            </li>
+          ))}
+        </ul>
+      )
+      else content.push(<p>No instruments found.</p>)
+      content.push(<InstrumentForm />)
       return content
     }}
   </Query>

+ 29 - 3
frontend/components/InstrumentSubsystem.js

@@ -4,6 +4,31 @@ import Gallery from './Gallery'
 import InstrumentCommand from './InstrumentCommand'
 import InstrumentParameter from './InstrumentParameter'
 
+const CREATE_INSTRUMENT_SUBSYSTEM = gql`
+  mutation CREATE_INSTRUMENT_SUBSYSTEM($name: String!, $description: String!) {
+    createInstrumentSubsystem(name: $name, description: $description) {
+      id
+    }
+  }
+`
+
+const UPDATE_INSTRUMENT_SUBSYSTEM = gql`
+  mutation UPDATE_INSTRUMENT_SUBSYSTEM($name: String, $description: String, $commands: [ID]) {
+    updateInstrumentSubsystem(name: $name, description:$description, commands: $commands) {
+      id
+    }
+  }
+`
+
+const InstrumentSubsystemFields = {
+  id: null,
+  name: '',
+  description: '',
+  commands: [],
+  parameters: [],
+  subsystems: []
+}
+
 const InstrumentSubsystemFormFields = props => {
   const { state, onChange } = props
 
@@ -32,9 +57,10 @@ const InstrumentSubsystem = props => {
         <InstrumentSubsystem subsystem={childSubsystem} />)} />
     </div>
   ) : (
-    <p>No data found.</p>
-  )
+      <p>No data found.</p>
+    )
 }
 
 export default InstrumentSubsystem
-export { InstrumentSubsystemFormFields }
+export { InstrumentSubsystemFields, InstrumentSubsystemFormFields }
+export { CREATE_INSTRUMENT_SUBSYSTEM }

+ 20 - 6
frontend/components/InterfaceList.js

@@ -2,8 +2,8 @@ import gql from 'graphql-tag'
 import { Query } from 'react-apollo'
 import Interface from './Interface'
 
-const INTERFACE_LIST = gql`
-  query INTERFACE_LIST {
+const INTERFACES_FULL = gql`
+  query INTERFACES_FULL {
     interfaces {
       interfaceName
       workerScript
@@ -29,8 +29,22 @@ const INTERFACE_LIST = gql`
   }
 `
 
+const INTERFACES = gql`
+  query INTERFACES {
+    interfaces {
+      interfaceName
+      ports {
+        id
+      }
+      connections {
+        id
+      }
+    }
+  }
+`
+
 const InterfaceList = props => (
-  <Query query={INTERFACE_LIST}>
+  <Query query={INTERFACES_FULL}>
     {({ data }, loading, error) => {
       if (!data) return <p>No interfaces found.</p>
 
@@ -41,8 +55,8 @@ const InterfaceList = props => (
           {loading ? (
             <p>Loading interfaces...</p>
           ) : (
-            interfaces && interfaces.map(iface => <Interface key={iface.interfaceName} data={iface} />)
-          )}
+              interfaces && interfaces.map(iface => <Interface key={iface.interfaceName} data={iface} />)
+            )}
         </div>
       )
     }}
@@ -50,4 +64,4 @@ const InterfaceList = props => (
 )
 
 export default InterfaceList
-export { INTERFACE_LIST }
+export { INTERFACES, INTERFACES_FULL }

+ 3 - 3
frontend/components/Port.js

@@ -1,6 +1,6 @@
 import { Mutation } from 'react-apollo'
 import gql from 'graphql-tag'
-import { INTERFACE_LIST } from './InterfaceList'
+import { INTERFACES_FULL } from './InterfaceList'
 
 const CONNECT_PORT = gql`
   mutation CONNECT_PORT($interfaceName: String!, $device: String!) {
@@ -13,13 +13,13 @@ const CONNECT_PORT = gql`
 `
 
 class Port extends React.Component {
-  render () {
+  render() {
     const { interfaceName, device, name, description } = this.props.data
     return (
       <Mutation
         mutation={CONNECT_PORT}
         variables={{ interfaceName, device }}
-        refetchQueries={[{ query: INTERFACE_LIST }]}
+        refetchQueries={[{ query: INTERFACES_FULL }]}
       >
         {connect => (
           <div>