Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
0d63a52
added form settings component to forms/create page
LSmith2174 Apr 25, 2024
889fdeb
added upsert form component to forms/create
LSmith2174 Apr 25, 2024
ccdb6ae
FormQuestions component
Apr 27, 2024
95e8c78
CreateFormQuestion component
Apr 27, 2024
b7d1ab3
FormQuestionForm
Apr 27, 2024
73355ba
FormQuestions component on FormTabs component
Apr 27, 2024
f77d577
Merge branch 'main' of https://github.com/hack4impact-utk/internal-ap…
Apr 27, 2024
f69d989
fixy fix
Apr 27, 2024
969f9d3
use typography components
Apr 27, 2024
61dd742
add formsettings to formtabs
Apr 27, 2024
efe4e9b
made multiple choice option component and fixed FormQuestion thing
LSmith2174 Apr 28, 2024
50f0ba9
Merge branch 'integration-demo-prep' of https://github.com/hack4impac…
Apr 29, 2024
bf33430
suppress warning
Apr 29, 2024
7367eec
wip
Apr 30, 2024
2915fbe
Merge branch 'main' of https://github.com/hack4impact-utk/internal-ap…
May 2, 2024
ef3d499
many many good good changes
May 2, 2024
d805964
Merge branch '143-create-multiplechoiceoptions-component' into integr…
LSmith2174 May 2, 2024
6929882
1 responses -> 1 response
LSmith2174 May 2, 2024
7e34d84
tolocaledate string on form/[formId] page
LSmith2174 May 2, 2024
4ba4e15
fixed wording
LSmith2174 May 2, 2024
aab00d3
added form question type options
LSmith2174 May 2, 2024
8fa0c49
added file type options
LSmith2174 May 2, 2024
88e3341
Revert "suppress warning"
May 2, 2024
22a44fd
Merge branch 'integration-demo-prep' of https://github.com/hack4impac…
May 2, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 18 additions & 7 deletions apps/form-builder/src/app/forms/[formId]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { getFormById } from '@/server/actions/forms';
import * as React from 'react';
import FormTabs from '@/components/FormTabs';

import { Typography } from '@mui/material';
export default async function FormPage({
params,
}: {
Expand All @@ -13,15 +13,26 @@ export default async function FormPage({
return;
}

const updatedAtDate: Date = new Date(form.updatedAt);
const createdAtDate: Date = new Date(form.createdAt);

return (
<div>
{/* display properties of a form */}
<p>Title: {form.title}</p>
<p>Description: {form.description}</p>
<p>Responder Type: {form.responderType}</p>
<p>Anonymous: {form.isAnonymous.toLocaleString()}</p>
<p>Created At: {form.createdAt.toLocaleString()}</p>
<p>Updated At: {form.updatedAt.toLocaleString()}</p>
<Typography sx={{ pt: 2 }}>Title: {form.title}</Typography>
<Typography sx={{ pt: 2 }}>Description: {form.description}</Typography>
<Typography sx={{ pt: 2 }}>
Responder Type: {form.responderType}
</Typography>
<Typography sx={{ pt: 2 }}>
Anonymous: {form.isAnonymous.toLocaleString()}
</Typography>
<Typography sx={{ pt: 2 }}>
Created At: {createdAtDate.toLocaleString()}
</Typography>
<Typography sx={{ pt: 2 }}>
Last Updated At: {updatedAtDate.toLocaleString()}
</Typography>
{/* //added tabs and a button */}
<FormTabs form={form}></FormTabs>
</div>
Expand Down
9 changes: 8 additions & 1 deletion apps/form-builder/src/app/forms/create/page.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
import UpsertForm from '@/components/UpsertForm/UpsertForm';
import { Box } from '@mui/material';

export default async function NewFormPage() {
return <h1>New form page</h1>;
return (
<Box>
<UpsertForm></UpsertForm>
</Box>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import {
Box,
Checkbox,
FormControl,
InputLabel,
ListItemText,
MenuItem,
OutlinedInput,
Select,
SelectChangeEvent,
TextField,
} from '@mui/material';
import React from 'react';

export default function FileUploadOptions() {
const [value, setValue] = React.useState('KB');

const handleChange = (event: SelectChangeEvent) => {
setValue(event.target.value);
};

const fileTypes = [
'PDF',
'TXT',
'DOC/DOCX',
'XLS',
'XLSX',
'HTML',
'PPT/PPTX',
'JPEG',
'PNG',
'GIF',
'MP4',
'AVI',
'MOV',
'MP3',
];

const [fileType, setFileType] = React.useState<string[]>([]);

const handleFileChange = (event: SelectChangeEvent<typeof fileType>) => {
const {
target: { value },
} = event;
setFileType(typeof value === 'string' ? value.split(',') : value);
};

return (
<Box>
<Box>
<FormControl sx={{ mt: 2, minWidth: 175 }}>
<InputLabel id="fileType">File Type</InputLabel>
<Select
labelId="fileType"
id="fileType"
multiple
value={fileType}
onChange={handleFileChange}
input={<OutlinedInput label="File Type" />}
renderValue={(selected) => selected.join(', ')}
>
{fileTypes.map((type) => (
<MenuItem key={type} value={type}>
<Checkbox checked={fileType.indexOf(type) > -1} />
<ListItemText primary={type} />
</MenuItem>
))}
</Select>
</FormControl>
</Box>
<Box sx={{ display: 'flex', flexDirection: 'row' }}>
<FormControl sx={{ mt: 2, maxWidth: 170, minWidth: 150 }}>
<TextField id="size" label="File Size" variant="outlined"></TextField>
</FormControl>

<FormControl sx={{ mt: 2, maxWidth: 170, minWidth: 80 }}>
<InputLabel id="select-label">File Size</InputLabel>
<Select
labelId="select-label"
id="select"
value={value}
onChange={handleChange}
autoWidth
label="FileSize"
>
<MenuItem value="KB">KB</MenuItem>
<MenuItem value="MB">MB</MenuItem>
<MenuItem value="GB">GB</MenuItem>
</Select>
</FormControl>
</Box>
</Box>
);
}
13 changes: 12 additions & 1 deletion apps/form-builder/src/components/FormAnalytics/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,18 @@ export default function FormAnalytics(props: FormAnalyticsProps) {
<Typography variant="h5" sx={{ fontWeight: 'bold' }}>
{props.question.title}
</Typography>
<Typography>{props.responses.reduce(numAnswers, 0)} Responses</Typography>
<Typography>
{props.responses.reduce(numAnswers, 0) === 1 && (
<Typography>
{props.responses.reduce(numAnswers, 0)} Response
</Typography>
)}
{props.responses.reduce(numAnswers, 0) > 1 && (
<Typography>
{props.responses.reduce(numAnswers, 0)} Responses
</Typography>
)}
</Typography>
{questionType === 'Text' && (
<TextQuestionAnalytics
question={props.question}
Expand Down
97 changes: 97 additions & 0 deletions apps/form-builder/src/components/FormQuestionForm/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
'use client';
import {
Box,
FormControl,
FormControlLabel,
InputLabel,
MenuItem,
Select,
SelectChangeEvent,
Switch,
TextField,
} from '@mui/material';
import React from 'react';
import MultipleChoiceOptions from '../MultipleChoiceOptions';
import TextOptions from '../TextOptions';
import NumericOptions from '../NumericOptions/NumericOptions';
import FileUploadOptions from '../FileUploadOptions/FileUploadOptions';

export interface FormQuestionFormData {
title: string;
description?: string;
isRequired: boolean;
}

interface CreateFormQuestionProps {
formData: FormQuestionFormData;
onChange: (formData: FormQuestionFormData) => void;
}

export default function FormQuestionForm({
formData,
onChange,
}: CreateFormQuestionProps) {
const [value, setValue] = React.useState('');

const [questionSettings, setQuestionSettings] = React.useState('');

const handleChange = (event: SelectChangeEvent) => {
setValue(event.target.value);
setQuestionSettings(event.target.value);
};

return (
<Box sx={{ display: 'flex', flexDirection: 'column' }}>
<TextField
label="Title"
value={formData.title}
onChange={(e) => onChange({ ...formData, title: e.target.value })}
/>
<TextField
sx={{ mt: 2 }}
label="Description"
value={formData.description}
onChange={(e) => onChange({ ...formData, description: e.target.value })}
/>
<FormControlLabel
sx={{ mt: 2 }}
control={
<Switch
checked={formData.isRequired}
value={formData.isRequired}
onChange={(e) =>
onChange({ ...formData, isRequired: e.target.checked })
}
/>
}
label="Required?"
/>
<FormControl sx={{ mt: 2, maxWidth: 170 }}>
<InputLabel id="select-label">Question Type</InputLabel>
<Select
labelId="select-label"
id="select"
value={value}
onChange={handleChange}
autoWidth
label="Question Type"
>
<MenuItem value="Multiple">Multiple Choice</MenuItem>
<MenuItem value="Numeric">Numeric Ranking</MenuItem>
<MenuItem value="Text">Text</MenuItem>
<MenuItem value="FileUpload">File Upload</MenuItem>
</Select>
</FormControl>
<Box>
{questionSettings === 'Multiple' && (
<MultipleChoiceOptions></MultipleChoiceOptions>
)}
{questionSettings === 'Numeric' && <NumericOptions></NumericOptions>}
{questionSettings === 'Text' && <TextOptions></TextOptions>}
{questionSettings === 'FileUpload' && (
<FileUploadOptions></FileUploadOptions>
)}
</Box>
</Box>
);
}
73 changes: 73 additions & 0 deletions apps/form-builder/src/components/FormQuestions/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
'use client';
import AddIcon from '@mui/icons-material/Add';
import { Box, Divider, IconButton, Typography } from '@mui/material';
import React, { useState } from 'react';
import FormQuestionForm, { FormQuestionFormData } from '../FormQuestionForm';
import {
FormQuestionResponse,
FormResponse,
} from '@hack4impact-utk/internal-models';

interface FormQuestionsProps {
form?: FormResponse;
}

function FormQuestionResponsesToFormQuestionFormData(
formQuestionResponses: FormQuestionResponse[]
) {
return formQuestionResponses.map(
(formQuestionResponse): FormQuestionFormData => {
return {
title: formQuestionResponse.title,
description: formQuestionResponse.description,
isRequired: formQuestionResponse.isRequired,
};
}
);
}

export default function FormQuestions(props: FormQuestionsProps) {
const [questionsFormData, setQuestionsFormData] = useState<
FormQuestionFormData[]
>(FormQuestionResponsesToFormQuestionFormData(props.form?.questions || []));

function addFormQuestion() {
const newQuestion: FormQuestionFormData = {
isRequired: false,
title: '',
description: '',
};
setQuestionsFormData((currQuestions) => [...currQuestions, newQuestion]);
}

function onFormQuestionChange(index: number, newData: FormQuestionFormData) {
setQuestionsFormData((currFormDataArr) =>
currFormDataArr.map((formData, i) => (i === index ? newData : formData))
);
}

return (
<Box>
{questionsFormData.map((question, i) => (
<Box key={i} sx={{ pb: 3 }}>
<Typography fontWeight={'bold'} sx={{ pb: 2 }}>
Question {i + 1}
</Typography>
<FormQuestionForm
formData={question}
onChange={(formData: FormQuestionFormData) => {
onFormQuestionChange(i, formData);
}}
/>
<Divider sx={{ borderBottomWidth: 3, pt: 3 }} />
</Box>
))}
<Box sx={{ display: 'flex', alignItems: 'center' }}>
<Typography>Add new question</Typography>
<IconButton onClick={addFormQuestion}>
<AddIcon />
</IconButton>
</Box>
</Box>
);
}
Loading