From ef7a52434e5b6fbfb9c31af0172861a9e2bb0351 Mon Sep 17 00:00:00 2001
From: eug-vs <eug-vs@keemail.me>
Date: Fri, 4 Dec 2020 03:19:16 +0300
Subject: feat: validate form with yup

---
 src/containers/BsuFantomSection/EventForm.tsx | 49 +++++++++++++++++++++++++--
 1 file changed, 47 insertions(+), 2 deletions(-)

(limited to 'src/containers/BsuFantomSection/EventForm.tsx')

diff --git a/src/containers/BsuFantomSection/EventForm.tsx b/src/containers/BsuFantomSection/EventForm.tsx
index 09bb87b..9fc18b5 100644
--- a/src/containers/BsuFantomSection/EventForm.tsx
+++ b/src/containers/BsuFantomSection/EventForm.tsx
@@ -1,6 +1,9 @@
 import React from 'react';
-import { Grid, TextField, Button } from '@material-ui/core';
+import _ from 'lodash';
+import cronstrue from 'cronstrue';
+import * as Yup from 'yup';
 import { Formik, Form, Field } from 'formik';
+import { Grid, TextField, Button, Link } from '@material-ui/core';
 import { post } from '../../requests';
 import { Event } from '../../types';
 
@@ -15,7 +18,36 @@ interface Fields {
   conferenceId: string;
 }
 
+const describeCrontab = (crontab: string) => {
+  try {
+    return cronstrue.toString(crontab);
+  } catch {
+    return false;
+  }
+};
+
+const validationSchema = Yup.object({
+  name: Yup.string().required('This field is required'),
+  conferenceId: Yup.string().required('This field is required'),
+  attendanceId: Yup.string(),
+  schedule: Yup.string()
+    .required('This field is required')
+    .test('cron', 'Invalid crontab', value => !!describeCrontab(value || ''))
+});
+
 const EventForm: React.FC<PropTypes> = ({ mutate }) => {
+
+  const describeSchedule = (schedule: string) => {
+    const description = describeCrontab(schedule);
+    if (description) return `Event will run ${_.lowerFirst(description)}`;
+    const link = <Link href="https://crontab.guru">crontab.guru</Link>;
+    return (
+      <>
+        The schedule is invalid. Check out {link} for more help with cron scheduling.
+      </>
+    );
+  };
+
   const handleSubmit = (fields: Fields, { resetForm }: any) => {
     const { name, schedule, attendanceId, conferenceId } = fields;
 
@@ -41,9 +73,10 @@ const EventForm: React.FC<PropTypes> = ({ mutate }) => {
   return (
     <Formik
       initialValues={{ name: '', schedule: '* * * * *', attendanceId: '', conferenceId: '' }}
+      validationSchema={validationSchema}
       onSubmit={handleSubmit}
     >
-      {({ values, errors, touched, isSubmitting }) => (
+      {({ values, errors, touched, isValid }) => (
         <Form autoComplete="off">
           <Grid container spacing={2}>
             <Grid item sm={6} xs={12}>
@@ -51,6 +84,8 @@ const EventForm: React.FC<PropTypes> = ({ mutate }) => {
                 name="name"
                 label="Name"
                 value={values.name}
+                error={touched.name && !!errors.name}
+                helperText={touched.name && errors.name}
                 variant="outlined"
                 fullWidth
                 required
@@ -62,6 +97,10 @@ const EventForm: React.FC<PropTypes> = ({ mutate }) => {
                 name="schedule"
                 label="Schedule"
                 value={values.schedule}
+                error={touched.schedule && !!errors.schedule}
+                helperText={
+                  (touched.schedule && errors.schedule) || "Should be a valid crontab, for example: 30 12 * * Mon"
+                }
                 variant="outlined"
                 fullWidth
                 required
@@ -73,6 +112,8 @@ const EventForm: React.FC<PropTypes> = ({ mutate }) => {
                 name="conferenceId"
                 label="Conference ID"
                 value={values.conferenceId}
+                error={touched.conferenceId && !!errors.conferenceId}
+                helperText={touched.conferenceId && errors.conferenceId}
                 variant="outlined"
                 fullWidth
                 required
@@ -89,9 +130,13 @@ const EventForm: React.FC<PropTypes> = ({ mutate }) => {
                 as={TextField}
               />
             </Grid>
+            <Grid item xs={12}>
+              {describeSchedule(values.schedule)}
+            </Grid>
             <Grid item sm={2} xs={12}>
               <Button
                 type="submit"
+                disabled={!isValid}
                 variant="contained"
                 size="large"
                 color="primary"
-- 
cgit v1.2.3

