Przeglądaj źródła

create working. need to get update running, too.

Tomi Cvetic 5 lat temu
rodzic
commit
1f4ec97446

+ 8 - 8
backend/database/generated/prisma-client/index.ts

@@ -723,8 +723,8 @@ export interface BlockInstanceWhereInput {
   id_ends_with?: Maybe<ID_Input>;
   id_not_ends_with?: Maybe<ID_Input>;
   block?: Maybe<BlockWhereInput>;
-  parentTraining?: Maybe<TrainingWhereInput>;
   parentBlock?: Maybe<BlockWhereInput>;
+  parentTraining?: Maybe<TrainingWhereInput>;
   order?: Maybe<Int>;
   order_not?: Maybe<Int>;
   order_in?: Maybe<Int[] | Int>;
@@ -1599,8 +1599,8 @@ export interface BlockInstanceCreateManyWithoutBlockInput {
 
 export interface BlockInstanceCreateWithoutBlockInput {
   id?: Maybe<ID_Input>;
-  parentTraining?: Maybe<TrainingCreateOneWithoutBlocksInput>;
   parentBlock?: Maybe<BlockCreateOneWithoutBlocksInput>;
+  parentTraining?: Maybe<TrainingCreateOneWithoutBlocksInput>;
   order: Int;
   rounds?: Maybe<Int>;
   variation?: Maybe<String>;
@@ -2559,8 +2559,8 @@ export interface BlockInstanceUpdateWithWhereUniqueWithoutBlockInput {
 }
 
 export interface BlockInstanceUpdateWithoutBlockDataInput {
-  parentTraining?: Maybe<TrainingUpdateOneWithoutBlocksInput>;
   parentBlock?: Maybe<BlockUpdateOneWithoutBlocksInput>;
+  parentTraining?: Maybe<TrainingUpdateOneWithoutBlocksInput>;
   order?: Maybe<Int>;
   rounds?: Maybe<Int>;
   variation?: Maybe<String>;
@@ -2611,8 +2611,8 @@ export interface BlockUpdateManyMutationInput {
 export interface BlockInstanceCreateInput {
   id?: Maybe<ID_Input>;
   block: BlockCreateOneWithoutLinksInput;
-  parentTraining?: Maybe<TrainingCreateOneWithoutBlocksInput>;
   parentBlock?: Maybe<BlockCreateOneWithoutBlocksInput>;
+  parentTraining?: Maybe<TrainingCreateOneWithoutBlocksInput>;
   order: Int;
   rounds?: Maybe<Int>;
   variation?: Maybe<String>;
@@ -2620,8 +2620,8 @@ export interface BlockInstanceCreateInput {
 
 export interface BlockInstanceUpdateInput {
   block?: Maybe<BlockUpdateOneRequiredWithoutLinksInput>;
-  parentTraining?: Maybe<TrainingUpdateOneWithoutBlocksInput>;
   parentBlock?: Maybe<BlockUpdateOneWithoutBlocksInput>;
+  parentTraining?: Maybe<TrainingUpdateOneWithoutBlocksInput>;
   order?: Maybe<Int>;
   rounds?: Maybe<Int>;
   variation?: Maybe<String>;
@@ -3274,8 +3274,8 @@ export interface BlockInstancePromise
     Fragmentable {
   id: () => Promise<ID_Output>;
   block: <T = BlockPromise>() => T;
-  parentTraining: <T = TrainingPromise>() => T;
   parentBlock: <T = BlockPromise>() => T;
+  parentTraining: <T = TrainingPromise>() => T;
   order: () => Promise<Int>;
   rounds: () => Promise<Int>;
   variation: () => Promise<String>;
@@ -3286,8 +3286,8 @@ export interface BlockInstanceSubscription
     Fragmentable {
   id: () => Promise<AsyncIterator<ID_Output>>;
   block: <T = BlockSubscription>() => T;
-  parentTraining: <T = TrainingSubscription>() => T;
   parentBlock: <T = BlockSubscription>() => T;
+  parentTraining: <T = TrainingSubscription>() => T;
   order: () => Promise<AsyncIterator<Int>>;
   rounds: () => Promise<AsyncIterator<Int>>;
   variation: () => Promise<AsyncIterator<String>>;
@@ -3298,8 +3298,8 @@ export interface BlockInstanceNullablePromise
     Fragmentable {
   id: () => Promise<ID_Output>;
   block: <T = BlockPromise>() => T;
-  parentTraining: <T = TrainingPromise>() => T;
   parentBlock: <T = BlockPromise>() => T;
+  parentTraining: <T = TrainingPromise>() => T;
   order: () => Promise<Int>;
   rounds: () => Promise<Int>;
   variation: () => Promise<String>;

+ 6 - 6
backend/database/generated/prisma-client/prisma-schema.ts

@@ -140,8 +140,8 @@ type BlockEdge {
 type BlockInstance {
   id: ID!
   block: Block!
-  parentTraining: Training
   parentBlock: Block
+  parentTraining: Training
   order: Int!
   rounds: Int
   variation: String
@@ -156,8 +156,8 @@ type BlockInstanceConnection {
 input BlockInstanceCreateInput {
   id: ID
   block: BlockCreateOneWithoutLinksInput!
-  parentTraining: TrainingCreateOneWithoutBlocksInput
   parentBlock: BlockCreateOneWithoutBlocksInput
+  parentTraining: TrainingCreateOneWithoutBlocksInput
   order: Int!
   rounds: Int
   variation: String
@@ -180,8 +180,8 @@ input BlockInstanceCreateManyWithoutParentTrainingInput {
 
 input BlockInstanceCreateWithoutBlockInput {
   id: ID
-  parentTraining: TrainingCreateOneWithoutBlocksInput
   parentBlock: BlockCreateOneWithoutBlocksInput
+  parentTraining: TrainingCreateOneWithoutBlocksInput
   order: Int!
   rounds: Int
   variation: String
@@ -298,8 +298,8 @@ input BlockInstanceSubscriptionWhereInput {
 
 input BlockInstanceUpdateInput {
   block: BlockUpdateOneRequiredWithoutLinksInput
-  parentTraining: TrainingUpdateOneWithoutBlocksInput
   parentBlock: BlockUpdateOneWithoutBlocksInput
+  parentTraining: TrainingUpdateOneWithoutBlocksInput
   order: Int
   rounds: Int
   variation: String
@@ -359,8 +359,8 @@ input BlockInstanceUpdateManyWithWhereNestedInput {
 }
 
 input BlockInstanceUpdateWithoutBlockDataInput {
-  parentTraining: TrainingUpdateOneWithoutBlocksInput
   parentBlock: BlockUpdateOneWithoutBlocksInput
+  parentTraining: TrainingUpdateOneWithoutBlocksInput
   order: Int
   rounds: Int
   variation: String
@@ -431,8 +431,8 @@ input BlockInstanceWhereInput {
   id_ends_with: ID
   id_not_ends_with: ID
   block: BlockWhereInput
-  parentTraining: TrainingWhereInput
   parentBlock: BlockWhereInput
+  parentTraining: TrainingWhereInput
   order: Int
   order_not: Int
   order_in: [Int!]

+ 7 - 7
backend/database/generated/prisma.graphql

@@ -1,5 +1,5 @@
 # source: http://prisma:4466
-# timestamp: Wed Apr 08 2020 13:30:04 GMT+0000 (Coordinated Universal Time)
+# timestamp: Wed Apr 08 2020 18:07:43 GMT+0000 (Coordinated Universal Time)
 
 type AggregateBlock {
   count: Int!
@@ -148,8 +148,8 @@ type BlockEdge {
 type BlockInstance implements Node {
   id: ID!
   block: Block!
-  parentTraining: Training
   parentBlock: Block
+  parentTraining: Training
   order: Int!
   rounds: Int
   variation: String
@@ -171,8 +171,8 @@ input BlockInstanceCreateInput {
   rounds: Int
   variation: String
   block: BlockCreateOneWithoutLinksInput!
-  parentTraining: TrainingCreateOneWithoutBlocksInput
   parentBlock: BlockCreateOneWithoutBlocksInput
+  parentTraining: TrainingCreateOneWithoutBlocksInput
 }
 
 input BlockInstanceCreateManyWithoutBlockInput {
@@ -195,8 +195,8 @@ input BlockInstanceCreateWithoutBlockInput {
   order: Int!
   rounds: Int
   variation: String
-  parentTraining: TrainingCreateOneWithoutBlocksInput
   parentBlock: BlockCreateOneWithoutBlocksInput
+  parentTraining: TrainingCreateOneWithoutBlocksInput
 }
 
 input BlockInstanceCreateWithoutParentBlockInput {
@@ -421,8 +421,8 @@ input BlockInstanceUpdateInput {
   rounds: Int
   variation: String
   block: BlockUpdateOneRequiredWithoutLinksInput
-  parentTraining: TrainingUpdateOneWithoutBlocksInput
   parentBlock: BlockUpdateOneWithoutBlocksInput
+  parentTraining: TrainingUpdateOneWithoutBlocksInput
 }
 
 input BlockInstanceUpdateManyDataInput {
@@ -482,8 +482,8 @@ input BlockInstanceUpdateWithoutBlockDataInput {
   order: Int
   rounds: Int
   variation: String
-  parentTraining: TrainingUpdateOneWithoutBlocksInput
   parentBlock: BlockUpdateOneWithoutBlocksInput
+  parentTraining: TrainingUpdateOneWithoutBlocksInput
 }
 
 input BlockInstanceUpdateWithoutParentBlockDataInput {
@@ -669,8 +669,8 @@ input BlockInstanceWhereInput {
   """All values not ending with the given string."""
   variation_not_ends_with: String
   block: BlockWhereInput
-  parentTraining: TrainingWhereInput
   parentBlock: BlockWhereInput
+  parentTraining: TrainingWhereInput
 }
 
 input BlockInstanceWhereUniqueInput {

+ 1 - 1
backend/datamodel.prisma

@@ -55,8 +55,8 @@ type Block {
 type BlockInstance {
     id: ID! @id
     block: Block! @relation(name: "ParentChild", onDelete: SET_NULL)
-    parentTraining: Training @relation(link: INLINE)
     parentBlock: Block @relation(name: "Instances")
+    parentTraining: Training @relation(link: INLINE)
     order: Int!
     rounds: Int
     variation: String

+ 30 - 11
frontend/pages/admin/training/index.tsx

@@ -4,18 +4,37 @@ import { useTrainingsQuery } from '../../../src/gql'
 const TrainingsList = () => {
   const { data, error, loading } = useTrainingsQuery()
 
-  if (error) return <p>Error loading trainings...</p>
-  if (loading) return <p>Loading data...</p>
   return (
-    <ul>
-      {data?.trainings.map(training => (
-        <li key={training.id}>
-          <Link href='training/[id]' as={`training/${training.id}`}>
-            <a>{training.title}</a>
-          </Link>
-        </li>
-      ))}
-    </ul>
+    <section>
+      {error && <p>Error loading trainings...</p>}
+      {loading && <p>Loading data...</p>}
+      {data && data.trainings.length > 0 ? (
+        <ul>
+          {data?.trainings.map(training => (
+            <li key={training.id}>
+              <button type='button'>Delete</button>
+              <Link href='training/[id]' as={`training/${training.id}`}>
+                <a>{training.title}</a>
+              </Link>
+            </li>
+          ))}
+        </ul>
+      ) : (
+        <p>No trainings found.</p>
+      )}
+      <Link href='training/create'>
+        <a>Create training</a>
+      </Link>
+
+      <style jsx>{`
+        li {
+          list-style: none;
+        }
+        ul {
+          padding: 0;
+        }
+      `}</style>
+    </section>
   )
 }
 

+ 3 - 3
frontend/src/gql/index.tsx

@@ -122,8 +122,8 @@ export type BlockCreateWithoutLinksInput = {
 export type BlockInstance = Node & {
   id: Scalars['ID'],
   block: Block,
-  parentTraining?: Maybe<Training>,
   parentBlock?: Maybe<Block>,
+  parentTraining?: Maybe<Training>,
   order: Scalars['Int'],
   rounds?: Maybe<Scalars['Int']>,
   variation?: Maybe<Scalars['String']>,
@@ -149,8 +149,8 @@ export type BlockInstanceCreateWithoutBlockInput = {
   order: Scalars['Int'],
   rounds?: Maybe<Scalars['Int']>,
   variation?: Maybe<Scalars['String']>,
-  parentTraining?: Maybe<TrainingCreateOneWithoutBlocksInput>,
   parentBlock?: Maybe<BlockCreateOneWithoutBlocksInput>,
+  parentTraining?: Maybe<TrainingCreateOneWithoutBlocksInput>,
 };
 
 export type BlockInstanceCreateWithoutParentBlockInput = {
@@ -274,8 +274,8 @@ export type BlockInstanceWhereInput = {
   /** All values not ending with the given string. */
   variation_not_ends_with?: Maybe<Scalars['String']>,
   block?: Maybe<BlockWhereInput>,
-  parentTraining?: Maybe<TrainingWhereInput>,
   parentBlock?: Maybe<BlockWhereInput>,
+  parentTraining?: Maybe<TrainingWhereInput>,
 };
 
 export type BlockInstanceWhereUniqueInput = {

+ 63 - 33
frontend/src/training/components/BlockInstanceInputs.tsx

@@ -41,6 +41,7 @@ const BlockInstanceInputs = ({
   useEffect(() => {
     const missingIds = value
       .filter(item => !state.includes(item.id))
+      .filter(item => item.id && !item.id.startsWith('--'))
       .map(item => item.id)
     const stateWithoutRemovedItems = state.filter(stateId =>
       value.find(item => stateId === item.id)
@@ -48,41 +49,70 @@ const BlockInstanceInputs = ({
     setState([...stateWithoutRemovedItems, ...missingIds])
   }, [value])
 
-  const items = state.map(stateId => {
-    const itemIndex = value.findIndex(item => item.id === stateId)
-    if (itemIndex < 0) return null
-    const item = value[itemIndex]
-    return (
-      <div key={item.id}>
-        <p>
-          {item.order} {item.id}
-        </p>
-        <TextInput
-          name={`${name}.${itemIndex}.rounds`}
-          label='Rounds'
-          value={item.rounds}
-          type='number'
-          onChange={data => {
-            console.log(data)
-            onChange(data)
-          }}
-        />
-        <TextInput
-          name={`${name}.${itemIndex}.variation`}
-          label='Variation'
-          value={item.variation}
-          onChange={onChange}
-        />
-        {item.block && (
-          <BlockInputs
-            name={`${name}.${itemIndex}.block`}
-            value={item.block}
+  const items = state
+    .map(stateId => {
+      const itemIndex = value.findIndex(item => item.id === stateId)
+      if (itemIndex < 0) return null
+      const item = value[itemIndex]
+      return (
+        <div key={item.id}>
+          <p>
+            {item.order} {item.id}
+          </p>
+          <TextInput
+            name={`${name}.${itemIndex}.rounds`}
+            label='Rounds'
+            value={item.rounds}
+            type='number'
+            onChange={data => {
+              console.log(data)
+              onChange(data)
+            }}
+          />
+          <TextInput
+            name={`${name}.${itemIndex}.variation`}
+            label='Variation'
+            value={item.variation}
             onChange={onChange}
           />
-        )}
-      </div>
-    )
-  })
+          {item.block && (
+            <BlockInputs
+              name={`${name}.${itemIndex}.block`}
+              value={item.block}
+              onChange={onChange}
+            />
+          )}
+          <button
+            type='button'
+            onClick={ev => {
+              const updatedValues: CustomChangeEvent = {
+                target: { type: 'custom', name, value }
+              }
+              if (item.id?.startsWith('++')) {
+                updatedValues.target.value = [
+                  ...value.slice(0, itemIndex),
+                  ...value.slice(itemIndex + 1)
+                ]
+              } else {
+                updatedValues.target.value = [
+                  ...value.slice(0, itemIndex),
+                  {
+                    ...item,
+                    id: `--${item.id}`
+                  },
+                  ...value.slice(itemIndex + 1)
+                ]
+              }
+              console.log(updatedValues)
+              onChange(updatedValues)
+            }}
+          >
+            Delete block
+          </button>
+        </div>
+      )
+    })
+    .filter(block => block !== null)
 
   return (
     <SortableList

+ 10 - 52
frontend/src/training/components/EditTraining.tsx

@@ -1,6 +1,6 @@
 import { useCreateTrainingMutation, Training } from '../../gql'
 import { useForm, TextInput, DateTimeInput, Checkbox } from '../../form'
-import { emptyTraining, emptyBlockInstance } from '../utils'
+import { emptyTraining, emptyBlockInstance, transformArrayToDB } from '../utils'
 import TrainingTypeSelector from './TrainingTypeSelector'
 import BlockInstanceInputs from './BlockInstanceInputs'
 import { TTraining } from '../types'
@@ -17,57 +17,13 @@ const EditTraining = ({ training }: { training?: TTraining }) => {
     <form
       onSubmit={ev => {
         ev.preventDefault()
-        function collect(arr: any[]) {
-          const create: any[] = []
-          const connect: any[] = []
-          arr.forEach(val => {
-            if (typeof val === 'object' && val['connect']) {
-              connect.push(val['connect'])
-            }
-            if (typeof val === 'object' && val['create']) {
-              create.push(val['create'])
-            }
-          })
-          if (create.length > 0 || connect.length > 0) {
-            return { connect }
-          } else {
-            return arr
-          }
-        }
-        function magic(acc: any, val: any, key: any, object: any) {
-          if (key === '__typename') {
-            // remove the typename from the database
-            return
-          } else if (
-            key === 'id' &&
-            typeof val === 'string' &&
-            val.startsWith('__')
-          ) {
-            // remove placeholder IDs
-            return
-          } else if (isArray(val)) {
-            // collect 'create' and 'connect' statements
-            acc[key] = collect(transform(val, magic))
-          } else if (typeof val === 'object' && !!val) {
-            // we found an object!
-            if (
-              !!val['id'] &&
-              typeof val['id'] === 'string' &&
-              val['id'].startsWith('__')
-            ) {
-              // values with placeholder IDs are preserved
-              acc[key] = { create: transform(val, magic) }
-            } else {
-              // values with real IDs are just connected
-              acc[key] = { connect: { id: val['id'] } }
-            }
-          } else {
-            // copy the value
-            acc[key] = val
-          }
+        const newValues = transform(values, transformArrayToDB)
+        console.log(newValues)
+        if (!values.id.startsWith('++')) {
+          createTraining({ variables: newValues })
+        } else {
+          //updateTraining({variables: newValues})
         }
-        const newValues = transform(values, magic)
-        createTraining({ variables: newValues })
       }}
     >
       <TextInput
@@ -118,7 +74,9 @@ const EditTraining = ({ training }: { training?: TTraining }) => {
         onClick={event => {
           event.preventDefault()
           const newBlock = emptyBlockInstance({
-            order: values.blocks ? values.blocks.length : 0
+            order: values.blocks
+              ? values.blocks.filter(block => !block.id.startsWith('--')).length
+              : 0
           })
           onChange({
             target: {

+ 0 - 88
frontend/src/training/components/TrainingInputs.tsx

@@ -1,88 +0,0 @@
-import { TextInput, DateTimeInput, Checkbox } from '../../form'
-import TrainingTypeSelector from './TrainingTypeSelector'
-import BlockInstanceInputs from './BlockInstanceInputs'
-import { TrainingQuery } from '../../gql'
-import { emptyBlockInstance } from '../utils'
-
-const TrainingInputs = ({
-  values,
-  onChange,
-  name = ''
-}: {
-  values: NonNullable<TrainingQuery['training']>
-  name: string
-  onChange: GenericEventHandler
-}) => {
-  return (
-    <>
-      <TextInput
-        name='title'
-        label='Title'
-        value={values.title}
-        onChange={onChange}
-      />
-      <TrainingTypeSelector
-        name='type'
-        value={values.type}
-        onChange={onChange}
-      />
-      <DateTimeInput
-        name='trainingDate'
-        label='Training date'
-        value={values.trainingDate}
-        onChange={onChange}
-      />
-      <TextInput
-        name='location'
-        label='Location'
-        value={values.location}
-        onChange={onChange}
-      />
-      <TextInput
-        name='attendance'
-        label='Attendance'
-        type='number'
-        value={values.attendance}
-        onChange={onChange}
-      />
-      <Checkbox
-        name='published'
-        label='Published'
-        value={values.published}
-        onChange={onChange}
-      />
-      <label>Blocks</label>
-      {values.blocks && (
-        <BlockInstanceInputs
-          name='blocks'
-          value={values.blocks}
-          onChange={onChange}
-        />
-      )}
-      <button
-        onClick={event => {
-          event.preventDefault()
-          console.log('add')
-          const newBlock = emptyBlockInstance({
-            id: `__${Math.random()
-              .toString(36)
-              .replace(/[^a-zA-Z0-9]+/g, '')
-              .substr(0, 11)}`
-          })
-          onChange({
-            target: {
-              type: 'custom',
-              name: 'blocks',
-              value: values.blocks ? [...values.blocks, newBlock] : [newBlock]
-            }
-          })
-        }}
-        type='button'
-      >
-        Add block
-      </button>
-    </>
-  )
-}
-
-export default TrainingInputs

+ 61 - 2
frontend/src/training/utils.ts

@@ -6,6 +6,7 @@ import {
   BlockContentFragment,
   Training
 } from '../gql'
+import { isArray, transform } from 'lodash'
 
 /**
  * Takes a block of exercises and calculates the duration in seconds.
@@ -106,7 +107,7 @@ export function nextTime(weekDay: TWeekdays, time: string) {
 }
 
 function randomID() {
-  return `__${Math.random()
+  return `++${Math.random()
     .toString(36)
     .replace(/[^a-zA-Z0-9]+/g, '')
     .substr(0, 10)}`
@@ -135,7 +136,7 @@ export function emptyBlockInstance(input?: Partial<SubBlockFragment>) {
 }
 
 export function emptyTraining(input?: TTraining) {
-  const emptyTraining: Partial<Training> = {
+  const emptyTraining: TTraining = {
     id: randomID(),
     title: '',
     type: { id: '', name: '', description: '' },
@@ -146,3 +147,61 @@ export function emptyTraining(input?: TTraining) {
   }
   return { ...emptyTraining, ...input }
 }
+
+export function collectMutationCreateConnect(arr: any[]) {
+  const create: any[] = []
+  const connect: any[] = []
+  const del: any[] = []
+  arr.forEach(val => {
+    if (typeof val === 'object' && val['connect']) {
+      connect.push(val['connect'])
+    }
+    if (typeof val === 'object' && val['create']) {
+      create.push(val['create'])
+    }
+    if (typeof val === 'object' && val['delete']) {
+      del.push(val['delete'])
+    }
+  })
+  if (create.length > 0 || connect.length > 0) {
+    return { create, connect, delete: del }
+  } else {
+    return arr
+  }
+}
+
+export function transformArrayToDB(acc: any, val: any, key: any, object: any) {
+  if (key === '__typename') {
+    // remove the typename from the database
+    return
+  } else if (key === 'id' && typeof val === 'string' && val.startsWith('++')) {
+    // remove placeholder IDs
+    return
+  } else if (isArray(val)) {
+    // collect 'create' and 'connect' statements
+    acc[key] = collectMutationCreateConnect(transform(val, transformArrayToDB))
+  } else if (typeof val === 'object' && !!val) {
+    // we found an object!
+    if (
+      !!val['id'] &&
+      typeof val['id'] === 'string' &&
+      val['id'].startsWith('++')
+    ) {
+      // values with placeholder IDs are preserved
+      acc[key] = { create: transform(val, transformArrayToDB) }
+    } else if (
+      !!val['id'] &&
+      typeof val['id'] === 'string' &&
+      val['id'].startsWith('--')
+    ) {
+      // values with placeholder IDs are preserved
+      acc[key] = { delete: { id: val['id'].substr(2) } }
+    } else {
+      // values with real IDs are just connected
+      acc[key] = { connect: { id: val['id'] } }
+    }
+  } else {
+    // copy the value
+    acc[key] = val
+  }
+}