Ver Fonte

added publish and register mutations

Tomi Cvetic há 4 anos atrás
pai
commit
9a96039efe

+ 1 - 0
backend/schema.graphql

@@ -119,4 +119,5 @@ type Mutation {
   resetPassword(token: String!, password: String!): User!
   register(training: ID!): String!
   deregister(training: ID!): String!
+  publish(training: ID!, status: Boolean): String!
 }

+ 14 - 14
backend/src/training/resolvers.ts

@@ -74,27 +74,27 @@ export const resolvers: IResolvers = {
     },
     register: async (parent, args, context, info) => {
       checkPermission(context)
-      const training = await context.db.query.training(
-        { where: { id: args.training } },
-        '{ id }'
-      )
-      if (!training) throw Error(`Training ${args.training} not found`)
-      return context.db.mutation.updateTraining({
-        where: { id: training.id },
+      await context.db.mutation.updateTraining({
+        where: { id: args.training },
         data: { registrations: { connect: { id: context.req.userId } } }
       })
+      return 'Success!'
     },
     deregister: async (parent, args, context, info) => {
       checkPermission(context)
-      const training = await context.db.query.training(
-        { where: { id: args.training } },
-        '{ id }'
-      )
-      if (!training) throw Error(`Training ${args.training} not found`)
-      return context.db.mutation.updateTraining({
-        where: { id: training.id },
+      await context.db.mutation.updateTraining({
+        where: { id: args.training },
         data: { registrations: { disconnect: { id: context.req.userId } } }
       })
+      return 'Success!'
+    },
+    publish: async (parent, args, context, info) => {
+      checkPermission(context, ['INSTRUCTOR'])
+      await context.db.mutation.updateTraining({
+        where: { id: args.training },
+        data: { published: args.status }
+      })
+      return 'Success!'
     }
   }
 }

+ 1 - 1
frontend/src/app/components/Nav.tsx

@@ -15,7 +15,7 @@ const Nav = () => {
       <nav>
         <ul>
           <li>
-            <Link href='/trainings'>
+            <Link href='/training'>
               <a>
                 <FontAwesomeIcon icon={faCalendarAlt} />
                 Archive

+ 18 - 11
frontend/src/app/components/Page.tsx

@@ -2,19 +2,26 @@ import Header from './Header'
 import Meta from './Meta'
 import Footer from './Footer'
 import GlobalStyle from '../../styles/global'
+import { useContext } from 'react'
+import { UserContext } from '../../user/hooks'
+import { LoginForm } from '../../user'
 
-const Page = (props: any) => (
-  <>
-    <Meta />
+const Page = (props: any) => {
+  const user = useContext(UserContext)
+  console.log(user)
+  return (
+    <>
+      <Meta />
 
-    <Header />
-    <main>{props.children}</main>
-    <Footer />
+      <Header />
+      <main>{user.user ? props.children : <LoginForm />}</main>
+      <Footer />
 
-    <style jsx global>
-      {GlobalStyle}
-    </style>
-  </>
-)
+      <style jsx global>
+        {GlobalStyle}
+      </style>
+    </>
+  )
+}
 
 export default Page

+ 1 - 0
frontend/src/form/components/TextInput.tsx

@@ -29,6 +29,7 @@ const TextInput = ({
       onChange(newValue)
     } else return onChange(event)
   }
+  console.log(name)
 
   return (
     <>

+ 83 - 0
frontend/src/gql/index.tsx

@@ -1534,6 +1534,7 @@ export type Mutation = {
   resetPassword: User,
   register: Scalars['String'],
   deregister: Scalars['String'],
+  publish: Scalars['String'],
 };
 
 
@@ -1621,6 +1622,12 @@ export type MutationDeregisterArgs = {
   training: Scalars['ID']
 };
 
+
+export type MutationPublishArgs = {
+  training: Scalars['ID'],
+  status?: Maybe<Scalars['Boolean']>
+};
+
 /** An object with an ID */
 export type Node = {
   /** The id of the object. */
@@ -3528,6 +3535,21 @@ export type RegisterMutationVariables = {
 
 export type RegisterMutation = Pick<Mutation, 'register'>;
 
+export type DeregisterMutationVariables = {
+  training: Scalars['ID']
+};
+
+
+export type DeregisterMutation = Pick<Mutation, 'deregister'>;
+
+export type PublishMutationVariables = {
+  training: Scalars['ID'],
+  status?: Maybe<Scalars['Boolean']>
+};
+
+
+export type PublishMutation = Pick<Mutation, 'publish'>;
+
 export type UsersQueryVariables = {};
 
 
@@ -4196,6 +4218,67 @@ export function useRegisterMutation(baseOptions?: ApolloReactHooks.MutationHookO
 export type RegisterMutationHookResult = ReturnType<typeof useRegisterMutation>;
 export type RegisterMutationResult = ApolloReactCommon.MutationResult<RegisterMutation>;
 export type RegisterMutationOptions = ApolloReactCommon.BaseMutationOptions<RegisterMutation, RegisterMutationVariables>;
+export const DeregisterDocument = gql`
+    mutation deregister($training: ID!) {
+  deregister(training: $training)
+}
+    `;
+export type DeregisterMutationFn = ApolloReactCommon.MutationFunction<DeregisterMutation, DeregisterMutationVariables>;
+
+/**
+ * __useDeregisterMutation__
+ *
+ * To run a mutation, you first call `useDeregisterMutation` within a React component and pass it any options that fit your needs.
+ * When your component renders, `useDeregisterMutation` returns a tuple that includes:
+ * - A mutate function that you can call at any time to execute the mutation
+ * - An object with fields that represent the current status of the mutation's execution
+ *
+ * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
+ *
+ * @example
+ * const [deregisterMutation, { data, loading, error }] = useDeregisterMutation({
+ *   variables: {
+ *      training: // value for 'training'
+ *   },
+ * });
+ */
+export function useDeregisterMutation(baseOptions?: ApolloReactHooks.MutationHookOptions<DeregisterMutation, DeregisterMutationVariables>) {
+        return ApolloReactHooks.useMutation<DeregisterMutation, DeregisterMutationVariables>(DeregisterDocument, baseOptions);
+      }
+export type DeregisterMutationHookResult = ReturnType<typeof useDeregisterMutation>;
+export type DeregisterMutationResult = ApolloReactCommon.MutationResult<DeregisterMutation>;
+export type DeregisterMutationOptions = ApolloReactCommon.BaseMutationOptions<DeregisterMutation, DeregisterMutationVariables>;
+export const PublishDocument = gql`
+    mutation publish($training: ID!, $status: Boolean) {
+  publish(training: $training, status: $status)
+}
+    `;
+export type PublishMutationFn = ApolloReactCommon.MutationFunction<PublishMutation, PublishMutationVariables>;
+
+/**
+ * __usePublishMutation__
+ *
+ * To run a mutation, you first call `usePublishMutation` within a React component and pass it any options that fit your needs.
+ * When your component renders, `usePublishMutation` returns a tuple that includes:
+ * - A mutate function that you can call at any time to execute the mutation
+ * - An object with fields that represent the current status of the mutation's execution
+ *
+ * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
+ *
+ * @example
+ * const [publishMutation, { data, loading, error }] = usePublishMutation({
+ *   variables: {
+ *      training: // value for 'training'
+ *      status: // value for 'status'
+ *   },
+ * });
+ */
+export function usePublishMutation(baseOptions?: ApolloReactHooks.MutationHookOptions<PublishMutation, PublishMutationVariables>) {
+        return ApolloReactHooks.useMutation<PublishMutation, PublishMutationVariables>(PublishDocument, baseOptions);
+      }
+export type PublishMutationHookResult = ReturnType<typeof usePublishMutation>;
+export type PublishMutationResult = ApolloReactCommon.MutationResult<PublishMutation>;
+export type PublishMutationOptions = ApolloReactCommon.BaseMutationOptions<PublishMutation, PublishMutationVariables>;
 export const UsersDocument = gql`
     query Users {
   users {

+ 2 - 2
frontend/src/styles/theme.ts

@@ -15,8 +15,8 @@ const theme = {
     nav: '#393939e7'
   },
   maxWidth: '1000px',
-  bs: '0 12px 24px 0 rgba(0,0,0,0.09)',
-  bsUp: '0 -12px 24px 0 rgba(0,0,0,0.09)',
+  bs: '0 8px 12px 0 rgba(0,0,0,0.09)',
+  bsUp: '0 -8px 12px 0 rgba(0,0,0,0.18)',
   bsSmall: '0 5px 10px 0 rgba(0,0,0,0.19)'
 }
 

+ 19 - 20
frontend/src/training/components/EditTraining.tsx

@@ -4,7 +4,8 @@ import {
   emptyTraining,
   emptyBlockInstance,
   transformArrayToDB,
-  diffDB
+  diffDB,
+  prepareDataForDB
 } from '../utils'
 import TrainingTypeSelector from './TrainingTypeSelector'
 import BlockInstanceInputs from './BlockInstanceInputs'
@@ -22,25 +23,23 @@ const EditTraining = ({ training }: { training?: TTraining }) => {
 
   return (
     <form
-      onSubmit={ev => {
-        ev.preventDefault()
-        if (values.id.startsWith('++')) {
-          const newValues = transform(values, transformArrayToDB)
-          console.log({ newValues })
-          createTraining({ variables: newValues })
-        } else {
-          console.log(values, training, typeof diffDB)
-          const a = diffDB(values, training || emptyTraining())
-          const { id, ...changes } = a
-          const newValues = transform(changes, transformArrayToDB)
-          if (Object.keys(newValues).length > 0) {
-            console.log('saving changes', changes, newValues)
-            updateTraining({
-              variables: { where: { id: values.id }, data: newValues }
-            })
-          } else {
-            console.log('no changes.')
-          }
+      onSubmit={async event => {
+        event.preventDefault()
+        const newValues = prepareDataForDB(values, training || emptyTraining())
+        if (!newValues || Object.keys(newValues).length === 0) {
+          console.log('no changes.')
+          return
+        }
+        console.log(newValues)
+        const { id, ...data } = newValues
+        if (id.startsWith('++')) {
+          const createData = await createTraining({ variables: data })
+          console.log('created training', createData)
+        } else if (id.startsWith('@@')) {
+          const updateData = await updateTraining({
+            variables: { where: { id: id.substr(2) }, data }
+          })
+          console.log('updated training', updateData)
         }
       }}
     >

+ 5 - 2
frontend/src/training/components/TrainingMeta.tsx

@@ -2,13 +2,16 @@ import { TTraining } from '../types'
 import { calculateRating } from '../utils'
 import { useContext } from 'react'
 import { UserContext } from '../../user/hooks'
-import { useRegisterMutation } from '../../gql'
+import { useRegisterMutation, useDeregisterMutation } from '../../gql'
 
 const TrainingMeta = ({ training }: { training: TTraining }) => {
   const { user } = useContext(UserContext)
   const [register, registerData] = useRegisterMutation({
     variables: { training: training.id }
   })
+  const [deregister, deregisterData] = useDeregisterMutation({
+    variables: { training: training.id }
+  })
 
   return (
     <aside>
@@ -35,7 +38,7 @@ const TrainingMeta = ({ training }: { training: TTraining }) => {
             user?.data?.currentUser &&
             registeredUser.id === user.data.currentUser.id
         ) ? (
-          <button onClick={() => register()}>Deregister</button>
+          <button onClick={() => deregister()}>Deregister</button>
         ) : (
           <button onClick={() => register()}>Register now!</button>
         )}

+ 8 - 0
frontend/src/training/training.graphql

@@ -290,3 +290,11 @@ mutation createFormat($name: String!, $description: String!) {
 mutation register($training: ID!) {
   register(training: $training)
 }
+
+mutation deregister($training: ID!) {
+  deregister(training: $training)
+}
+
+mutation publish($training: ID!, $status: Boolean) {
+  publish(training: $training, status: $status)
+}

+ 13 - 7
frontend/src/training/utils.ts

@@ -170,7 +170,6 @@ export function collectMutations(arr: any[]) {
   const del: any[] = []
   const update: any[] = []
   arr.forEach(val => {
-    console.log(arr, val)
     if (val?.connect) connect.push(val.connect)
     if (val?.create) create.push(val.create)
     if (val?.delete) del.push(val.delete)
@@ -207,13 +206,10 @@ export function transformArrayToDB(acc: any, val: any, key: any, object: any) {
       acc[key] = collectedArray
     }
   } else if (typeof val === 'object' && !!val) {
-    // we found an object!
-    if (!!val['id'] && val['id'].startsWith('++')) {
-      // values with placeholder IDs are preserved
+    if (val?.id && val.id.startsWith('++')) {
       acc[key] = { create: transform(val, transformArrayToDB) }
-    } else if (!!val['id'] && val['id'].startsWith('--')) {
-      // IDs starting with -- are for deletion
-      acc[key] = { delete: { id: val['id'].substr(2) } }
+    } else if (val?.id && val.id.startsWith('--')) {
+      acc[key] = { delete: { id: val.id.substr(2) } }
     } else {
       const { id, ...data } = val
       if (id?.startsWith('@@')) {
@@ -250,6 +246,10 @@ function isScalarArray(object: any) {
   )
 }
 
+interface IDbItem {
+  id?: string
+}
+
 export function diffDB(newObject: any = {}, oldObject: any = {}) {
   // IDs starting with -- are for deletion.
   if (newObject.id && newObject.id.startsWith('--')) {
@@ -288,3 +288,9 @@ export function diffDB(newObject: any = {}, oldObject: any = {}) {
     return undefined
   }
 }
+
+export function prepareDataForDB<T>(formData: T, dbData: T) {
+  const changes = diffDB(formData, dbData)
+  const gqlData = transform(changes, transformArrayToDB)
+  return gqlData
+}

+ 0 - 2
frontend/src/user/hooks.tsx

@@ -1,6 +1,5 @@
 import { createContext, FunctionComponent } from 'react'
 import {
-  CurrentUserQuery,
   useCurrentUserQuery,
   useUserLogoutMutation,
   useUserLoginMutation,
@@ -23,7 +22,6 @@ export const UserProvider: FunctionComponent = ({ children }) => {
   const login = useUserLoginMutation({
     refetchQueries: [{ query: CurrentUserDocument }]
   })
-  console.log('current user', user.data)
   return (
     <UserContext.Provider value={{ user, login, logout }}>
       {children}

+ 10 - 10
frontend/src/user/index.ts

@@ -1,19 +1,19 @@
 import LoginForm from './components/LoginForm'
 import LogoutButton from './components/LogoutButton'
-import RequestPassword from './components/RequestPassword'
-import ResetPassword from './components/ResetPassword'
-import SignupForm from './components/SignupForm'
-import UserAdmin from './components/UserAdmin'
-import UserDetails from './components/UserDetails'
+//import RequestPassword from './components/RequestPassword'
+//import ResetPassword from './components/ResetPassword'
+//import SignupForm from './components/SignupForm'
+//import UserAdmin from './components/UserAdmin'
+//import UserDetails from './components/UserDetails'
 import DeleteUserButton from './components/DeleteUserButton'
 
 export {
   LoginForm,
   LogoutButton,
-  RequestPassword,
-  ResetPassword,
-  SignupForm,
-  UserAdmin,
-  UserDetails,
+  //RequestPassword,
+  //ResetPassword,
+  //SignupForm,
+  //UserAdmin,
+  //UserDetails,
   DeleteUserButton
 }