Skip to content

Commit

Permalink
Merge branch 'backend_delete_form' into backend_create_form
Browse files Browse the repository at this point in the history
  • Loading branch information
SteveShengStar committed Apr 2, 2023
2 parents f419b1f + 4cdf948 commit 0845c64
Show file tree
Hide file tree
Showing 5 changed files with 201 additions and 46 deletions.
86 changes: 79 additions & 7 deletions backend/data/handlers/forms.js
Original file line number Diff line number Diff line change
Expand Up @@ -191,22 +191,23 @@ forms.createForm = async (formData, res) => {
* @param {Object} formData: new metadata for the form.
*/
forms.updateFormMetadata = async (formName, formData, res) => {

const existingForm = await Form.findOne({ name: formName });

if (existingForm) {
const currentFormSectionIds = (existingForm).sections.map((section) => section.section);
const currentFormSectionNames = (
const existingFormSectionIds = existingForm.sections.map(
(section) => section.section
);
const existingFormSectionNames = (
await FormSection.find({
_id: { $in: currentFormSectionIds },
_id: { $in: existingFormSectionIds },
}).select(['name'])
).map((section) => section.name);
const newFormSectionNames = formData.sections.map(
(section) => section.name
);

const sectionsToDelete = _.difference(
currentFormSectionNames,
existingFormSectionNames,
newFormSectionNames
);
const toDelete = {};
Expand Down Expand Up @@ -244,7 +245,7 @@ forms.updateFormMetadata = async (formName, formData, res) => {
try {
await Form.updateOne({ name: formName }, _.omit(formData, '_id'), {
runValidators: true,
upsert: true
upsert: true,
});
} catch (err) {
console.error(err);
Expand Down Expand Up @@ -273,4 +274,75 @@ const getFormSectionNamesToIdsMap = async () => {
return formSectionNamesToIds;
};

/**
* Delete a form.
*
* @param {Object} formName: Unique name key of the form to update
* @param {Object} formData: new metadata for the form.
*/
forms.deleteForm = async (formName, res) => {
const sectionsOnDeletedForm = await Form.findOne(
{ name: formName },
{ sections: 1, _id: 0 }
).exec();
const otherForms = await Form.find(
{ name: { $ne: formName } },
{ sections: 1, _id: 0 }
).exec();
const otherFormSections = new Set(
otherForms.reduce((acc, curr) => {
return acc.concat(
curr.sections.map((section) => section.section.toString())
);
}, [])
);

return util.handleWrapper(async () => {
// formSections that are not in any other forms, must be deleted
const sectionsToDelete = sectionsOnDeletedForm.sections
.filter((value) => !otherFormSections.has(value.section.toString()))
.map((formSection) => formSection.section);

if (sectionsToDelete.length > 0) {
const findQuery = sectionsToDelete.map((section) => {
return { _id: section };
});
const sectionNamesToDelete = await FormSection.find({
$or: findQuery,
}).select({ name: 1, _id: 0 });

const unsetQuery = {};
sectionNamesToDelete.map((value) => (unsetQuery[value.name] = 1));

// remove references to the deleted form sections in all UserDetails documents
UserDetails.updateMany({}, { $unset: unsetQuery }, (err) => {
if (err) {
console.error(err);
res.statusCode = 500;
throw err;
}
});

FormSection.deleteMany(
{ _id: { $in: sectionsToDelete } },
(err) => {
if (err) {
console.error(err);
res.statusCode = 500;
throw err;
}
}
);
}

Form.deleteOne({ name: formName }, (err) => {
if (err) {
console.error(err);
res.statusCode = 500;
throw err;
}
});
});
};

module.exports = forms;
45 changes: 45 additions & 0 deletions pages/api/form/[name]/delete.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
const data = require('../../../../backend/data/index');
const cookie = require('cookie');


module.exports = async (req, res) => {
await data.initIfNotStarted();

if (req.method === 'DELETE') {
// Get the Access Token from the request headers
const token = cookie.parse(req.headers.cookie).token;
const authStatus = await data.auth.checkAnyAdminUser(token, res);

if (authStatus) {
res.setHeader('Content-Type', 'application/json');

if (!req.query.name) {
res.statusCode = 400;
res.end(
JSON.stringify(
await data.util.resWrapper(async () => {
throw Error('name URL param must be specified.');
})
)
);
return;
}

res.statusCode = 200;

res.end(
JSON.stringify(
await data.util.resWrapper(async () => {
return await data.forms.deleteForm(
req.query.name,
res
);
})
)
);
}
} else {
res.statusCode = 404;
res.end();
}
};
101 changes: 71 additions & 30 deletions pages/form/admin/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,26 +13,37 @@ import { v4 as uuidv4 } from 'uuid';

const EditFormButton = styled(Button)`
width: 100%;
height: 48px;
margin-bottom: ${(props) => props.theme.space.headerBottomMargin}px;
margin-top: 16px;
height: 60px;
margin-bottom: 10px;
margin-top: 10px;
border-radius: 5px;
`;

const ExportRespButton = styled(Button)`
const ExportButton = styled(Button)`
width: 100%;
height: 48px;
height: 60px;
border-radius: 5px;
`;

const CreateFormButton = styled(Button)`
width: 20%;
height: 48px;
height: 40px;
margin-bottom: ${(props) => props.theme.space.headerBottomMargin}px;
margin-top: 16px;
border-radius: 5px;
`;

const DeleteFormButton = styled.div`
margin-left: auto;
cursor: pointer;
`;

const DeleteIcon = styled.img`
justify-content: center;
height: 30px;
width: 30px;
`;

const CheckmarkRow = styled.div`
display: flex;
align-items: flex-start;
Expand All @@ -48,7 +59,7 @@ const Text = styled.div`
color: #000000;
`;

const BigIconWrapper = styled.div`
const IconWrapper = styled.div`
height: 57px;
width: 57px;
display: flex;
Expand All @@ -59,7 +70,8 @@ const BigIconWrapper = styled.div`
justify-content: center;
box-sizing: border-box;
`;
const Svg = styled.img`

const Icon = styled.img`
justify-content: center;
height: 30px;
width: 30px;
Expand Down Expand Up @@ -93,12 +105,12 @@ const FormInfoCard = styled(Card)`
margin-left: auto;
margin-right: auto;
padding: 90px 30px 30px 30px;
padding: 30px 30px 30px 30px;
${(props) => props.theme.mediaQueries.mobile} {
padding: 90px 20px 20px 20px;
padding: 20px 20px 20px 20px;
}
@media screen and (min-width: 1400px) {
padding: 90px 30px 30px 30px;
padding: 30px 30px 30px 30px;
}
`;

Expand Down Expand Up @@ -130,7 +142,13 @@ const EXPORT_SUCCESS_MSG = 'Form Responses Exported to Google Drive.';
const EXPORT_ERROR_MSG =
'Error occurred. Please contact Waterloop Web Team for assistance.';

const FormMetadataSection = ({ src, title, bulletPoints, formName = '' }) => {
const FormMetadataSection = ({
src,
title,
bulletPoints,
formName = '',
onDelete,
}) => {
const router = useRouter();
const {
renderSuccessBanner,
Expand All @@ -144,10 +162,26 @@ const FormMetadataSection = ({ src, title, bulletPoints, formName = '' }) => {
{renderSuccessBanner()}
{renderErrorBanner()}
<FormInfoCard>
<SystemComponent>
<BigIconWrapper>
<Svg src={src}></Svg>
</BigIconWrapper>
<DeleteFormButton
onClick={(e) => {
e.preventDefault();
fetch('/api/form/' + formName + '/delete', {
method: 'DELETE',
})
.then((res) => {
onDelete(formName);
})
.catch((e) => {
console.error(e);
});
}}
>
<DeleteIcon src='/static/trash-solid.svg' />
</DeleteFormButton>
<SystemComponent marginTop='60px'>
<IconWrapper>
<Icon src={src}></Icon>
</IconWrapper>
</SystemComponent>
<SystemComponent>
<TitleText>{title}</TitleText>
Expand All @@ -165,7 +199,7 @@ const FormMetadataSection = ({ src, title, bulletPoints, formName = '' }) => {
>
Edit Form
</EditFormButton>
<ExportRespButton
<ExportButton
onClick={(e) => {
e.preventDefault();
fetch('/api/google/export/' + formName, {
Expand All @@ -186,7 +220,7 @@ const FormMetadataSection = ({ src, title, bulletPoints, formName = '' }) => {
variant='white'
>
Export Responses
</ExportRespButton>
</ExportButton>
</FormInfoCard>
</>
);
Expand All @@ -198,6 +232,10 @@ const DashboardPanel = () => {
const dispatch = useDispatch();
const router = useRouter();

const handleDelete = (formName) => {
setFormData(formData.filter((form) => form.name !== formName));
};

useEffect(() => {
showLoader();
useForms(dispatch, router)
Expand All @@ -211,16 +249,19 @@ const DashboardPanel = () => {

return (
<PageTemplate title='Waterloop Forms'>
<React.Fragment>

<CreateFormButton
onClick={(e) => {
e.preventDefault();
router.push('/form/edit/' + uuidv4());
}}
>
Create New Form
</CreateFormButton>
<SystemComponent>
{loader}
<SystemComponent>
<CreateFormButton
onClick={(e) => {
e.preventDefault();
router.push('/form/edit/' + uuidv4());
}}
>
<i class='fa fa-plus' aria-hidden='true' /> Create New
Form
</CreateFormButton>
</SystemComponent>
<SystemComponent
display='grid'
gridTemplateColumns={[
Expand All @@ -233,17 +274,17 @@ const DashboardPanel = () => {
gridColumnGap={5}
gridRowGap={6}
>
{loader}
{formData?.map((data) => (
<FormMetadataSection
src={data.imageSrc}
title={data.title}
bulletPoints={data.bulletPoints}
formName={data.name}
onDelete={handleDelete}
/>
))}
</SystemComponent>
</React.Fragment>
</SystemComponent>
</PageTemplate>
);
};
Expand Down

0 comments on commit 0845c64

Please sign in to comment.