swamphacks / HaXr

SwampHacks portal for Hackers and Admins!
https://app.swamphacks.com
1 stars 3 forks source link

Matthewdeguzman/form creator #44

Open matthewdeguzman opened 1 month ago

matthewdeguzman commented 1 month ago

Description

Added the ability for admin to create forms and for hackers to fill out forms.

Form Creator

The form creator allows admin to view and create forms for competitions.

Dashboard

image

A dashboard was created to let admin view forms for a competition. The flow is user selects a competition on the sidebar > Applications > Edit Forms: a prisma call retrieves the forms for the specified competition and sorts them in descending order by updated.

The forms are then displayed with their name, date modified, and date created. When a form name is clicked on, it brings them to the editor for that form. The route to the editor is /admin/comp/forms/edit/{form.id} where form.id is the id of the form.

The user also has the ability to create a new form with the "Create Form" button. This invokes the function handleCreateForm which invokes the the function createForm located in app/actions/forms.ts to create a new form record in the forms table for the associated competition. The user is then redirected to the editor for the form with router.push.

Form Editor

The form editor allows admin to create and update form questions, settings, and review responses (coming in another PR). The form editor component is in app/components/formCreator/FormCreator.tsx and is handed the id of the form as a prop. FormCreator maintains state of the form object, the sections of the form, any errors with creating the form, and the status of the autosave indicator.

The useEffect function is used to fetch the currently saved state of the form with getForm. Once the form object is retrieved, the form, section, and status state are updated and the autosave timer is started for every second. If there is an error retrieving the form, an error is printed to the console.

Since there are several components involved with the form creator, I decided to create a FormCreatorContext that contains the setter and getters for the form, error, and section states as well as the save function and its timer.

Application Creator

image

The application creator is the component that allows admin to edit the form questions, title, and sections. It grabs and loads the sections from the FormCreatorContext. Each section is it's own accordion and admin can add any number of questions to each section. The admin can add a title, description, question type, and choose the settings for the question. There are 9 question types which each have their own settings:

All questions have the ability to be required for submission in their settings. Paragraph and short answer have a maximum character limit. File upload has a file type selector where admin can select if they would like to accept PDF, image, or video type (more than one type can be selected) and maximum file size.

The checkbox, multiple choice, and dropdown all have options to add choices.

Form Settings

image

This component handles the form settings for the form and has general and release settings. The component uses the FormCreator context for form validation which we will discuss later.

The general settings has a switch for including MLH questions which are required for MLH events. There is also a switch for required the form submission for event check-in. This option is useful when checking hackers in and ensuring they've filled out forms such as a meal preference, confirmation, and application before they check in.

The release settings dictates when the form will be available to hackers. The release date is optional, if one is not set then the form will be publicly available as soon as the form is published. The close date is required and must be after the current date and release date (if set).

There are two options: Save Settings, Save and Publish Form. The save settings option is more useful once the form is published.

Once a form is published, the admin will see the following screen with the Include MLH Questions setting grayed out and the "Save and Publish Form" button changed to "Unpublish Form". Admin cannot edit questions once the form is released, so the settings for including MLH questions cannot be edited. However, the other settings can be edited.

Note: The form creator has an autosave feature which I will get into next but does not autosave settings. This decision was intentional so admin don't accidentally change date settings while a form is active and break the application.

image

Admin can also unpublish forms. This removes hacker access to the form and if the hacker tries to access the form, there will be an error page that appears telling users that the form is no longer publicly available. Admins should use this with caution.

image

Autosave

In the FormCreator component, once the form data is fetched from the database and loaded, an save function is called on a timer every second. The save function calls the updateForm which saves the form in the database with the current sections and questions (not settings, those must be intentionally saved). The save function includes logic for checking if the data has changed between the current and previous call, if it has then the form will be saved. Otherwise, there is nothing to be saved and no updateForm is not called.

There is a loading indicator in the top right of the form to display the save status of the form.

image

There are three status indicators for the form:

image image image

The loading indicator is displayed when the form is initially being loaded. The save indicator is displayed when the user is using the form and the form is being saved successfully. The failed indicator is displayed if there is an error saving the form for some reason (bad wifi).

Hacker Forms

image

The hacker will be able to fill out forms at /hacker/forms/[id] and can fill out the application for a competition at /hacker/forms/application/[code] where code is the code of the competition (i.e. swamphacks-x). The questions that were created in the form creator are rendered with mantine components. The application gets the current user session via nextauth which will be used for saving their response.

The Form.tsx file contains the component ViewForm which holds the logic for filling out the form. Upon loading the form, the application is loaded via the competition code with the getApplication function which specifies that the form opens before or at the current date and it closes at or after the current date and returns the competition along with its application.

Once the form and application are received, they are saved with state functions. There is another db call to retrieve the users previous response to the form. If there is no previous response, then an empty response is created and returned. The autosave timer is started and a save function is called every second. Similar to the autosave function in the form creator, the save function only invokes a db call if there was a change to the form between the current and last call to save.

Sidenote: MLH forms display the MLH questions at the beginning and the agreements at the end, then display the admin created sections in the middle.

Form State

The form state is kept track of with the mantine hook use-form and is loaded with the user's response.

Validation

The responses are validated against the question settings specified in the form creator once the user submits the form. If there are any errors in the form, the borders are turned red and text is displayed below the question describing the error. The top-most error is scrolled to as well. This is done with the setErrors handler in mantine.

https://github.com/user-attachments/assets/f4e41856-0cf2-420b-a002-94caf511f0f1

File Uploads

File uploads use vercel blob storage to store the files. When a user enters a file, it is uploaded to the blob storage upon the next autosave invocation. The response stores the url of the resource in the storage. The url is public but secure and encrypted so we don't have to worry about vulnerabilities with leaking urls. The user has the option to clear the previous entry, if cleared the file is removed from storage. Further, if the user uploads another file to the same question, the previous entry is deleted and replaced with the current file. A notification also appears if the file upload was successful.

https://github.com/user-attachments/assets/d99e0fc1-6a25-4136-a40d-22be7e1829a0

Form submission

Once the user submits the form and there are no errors returned, the submitted field in the response record for the user is set to true and a notification appears telling the user they submitted the form. The user can no longer edit the form but they can still view their responses.

https://github.com/user-attachments/assets/a9110176-5ff1-4c58-90f6-6f6804c7527a

AlexanderWangY commented 1 month ago

this is a wild PR, big balls

RobertConde commented 1 month ago

I don't care about migrations rn