github.com/quickfeed/quickfeed@v0.0.0-20240507093252-ed8ca812a09c/public/src/components/forms/CourseForm.tsx (about)

     1  import React, { useState } from "react"
     2  import { useActions } from "../../overmind"
     3  import { Course } from "../../../proto/qf/types_pb"
     4  import { Organization } from "../../../proto/qf/requests_pb"
     5  import FormInput from "./FormInput"
     6  import CourseCreationInfo from "../admin/CourseCreationInfo"
     7  import { useHistory } from "react-router"
     8  import { defaultTag, defaultYear } from "../../Helpers"
     9  
    10  
    11  // TODO: There are currently issues with navigating a new course without refreshing the page to trigger a state reload.
    12  // TODO: Plenty of required fields are undefined across multiple components.
    13  
    14  /** CourseForm is used to create a new course or edit an existing course.
    15   *  If `editCourse` is provided, the existing course will be modified.
    16   *  If no course is provided, a new course will be created. */
    17  const CourseForm = ({ editCourse }: { editCourse?: Course }): JSX.Element | null => {
    18      const actions = useActions()
    19      const history = useHistory()
    20  
    21      // TODO: This could go in go in a course-specific Overmind namespace rather than local state.
    22      // Local state for organization name to be checked against the server
    23      const [orgName, setOrgName] = useState("")
    24      const [org, setOrg] = useState<Organization>()
    25  
    26      // Local state containing the course to be created or edited (if any)
    27      const [course, setCourse] = useState(editCourse ? editCourse.clone() : new Course)
    28  
    29      // Local state containing a boolean indicating whether the organization is valid. Courses that are being edited do not need to be validated.
    30      const [orgFound, setOrgFound] = useState<boolean>(editCourse ? true : false)
    31  
    32      /* Date object used to fill in certain default values for new courses */
    33      const date = new Date(Date.now())
    34      if (!editCourse) {
    35          course.year = defaultYear(date)
    36          course.tag = defaultTag(date)
    37      }
    38  
    39      const handleChange = (event: React.FormEvent<HTMLInputElement>) => {
    40          const { name, value } = event.currentTarget
    41          switch (name) {
    42              case "courseName":
    43                  course.name = value
    44                  break
    45              case "courseTag":
    46                  course.tag = value
    47                  break
    48              case "courseCode":
    49                  course.code = value
    50                  break
    51              case "courseYear":
    52                  course.year = Number(value)
    53                  break
    54              case "slipDays":
    55                  course.slipDays = Number(value)
    56                  break
    57          }
    58          setCourse(course)
    59      }
    60  
    61      // Creates a new course if no course is being edited, otherwise updates the existing course
    62      const submitHandler = async (e: React.FormEvent<HTMLFormElement>) => {
    63          e.preventDefault()
    64          if (editCourse) {
    65              actions.editCourse({ course })
    66          } else {
    67              if (org) {
    68                  const success = await actions.createCourse({ course, org })
    69                  // If course creation was successful, redirect to the course page
    70                  if (success) {
    71                      history.push("/courses")
    72                  }
    73              } else {
    74                  //org not found
    75              }
    76          }
    77      }
    78  
    79      // Trigger grpc call to check if org exists
    80      const getOrganization = async () => {
    81          const org = (await actions.getOrganization(orgName)).message
    82          if (org) {
    83              setOrg(org)
    84              setOrgFound(true)
    85          } else {
    86              setOrgFound(false)
    87          }
    88      }
    89  
    90      return (
    91          <div className="container">
    92              {editCourse ? null : <CourseCreationInfo />}
    93              <div className="row" hidden={editCourse ? true : false}>
    94                  <div className="col input-group mb-3">
    95                      <div className="input-group-prepend">
    96                          <div className="input-group-text">Organization</div>
    97                      </div>
    98                      <input className="form-control" disabled={orgFound ? true : false} onKeyUp={e => setOrgName(e.currentTarget.value)} />
    99                      <span className={orgFound ? "btn btn-success disabled" : "btn btn-primary"} onClick={!orgFound ? () => getOrganization() : () => { return }}>
   100                          {orgFound ? <i className="fa fa-check" /> : "Find"}
   101                      </span>
   102                  </div>
   103              </div>
   104              {orgFound &&
   105                  <form className="form-group" onSubmit={async e => await submitHandler(e)}>
   106                      <div className="row">
   107                          <FormInput prepend="Name"
   108                              name="courseName"
   109                              placeholder={"Course Name"}
   110                              defaultValue={editCourse?.name}
   111                              onChange={handleChange}
   112                          />
   113                      </div>
   114                      <div className="row">
   115                          <FormInput
   116                              prepend="Code"
   117                              name="courseCode"
   118                              placeholder={"(ex. DAT320)"}
   119                              defaultValue={editCourse?.code}
   120                              onChange={handleChange}
   121                          />
   122                          <FormInput
   123                              prepend="Tag"
   124                              name="courseTag"
   125                              placeholder={"(ex. Fall / Spring)"}
   126                              defaultValue={editCourse ? editCourse.tag : defaultTag(date)}
   127                              onChange={handleChange}
   128                          />
   129                      </div>
   130                      <div className="row">
   131                          <FormInput
   132                              prepend="Slip days"
   133                              name="slipDays"
   134                              placeholder={"(ex. 7)"}
   135                              defaultValue={editCourse?.slipDays.toString()}
   136                              onChange={handleChange}
   137                              type="number"
   138                          />
   139                          <FormInput
   140                              prepend="Year"
   141                              name="courseYear"
   142                              placeholder={"(ex. 2021)"}
   143                              defaultValue={editCourse ? editCourse.year.toString() : defaultYear(date).toString()}
   144                              onChange={handleChange}
   145                              type="number"
   146                          />
   147                      </div>
   148                      <input className="btn btn-primary" type="submit" value={editCourse ? "Edit Course" : "Create Course"} />
   149                  </form>
   150              }
   151          </div>
   152      )
   153  }
   154  
   155  export default CourseForm