feat(tags): Test case tag editing UI and tag settings UI (#322)
This commit is contained in:
@@ -1,41 +0,0 @@
|
||||
import express from 'express';
|
||||
const router = express.Router();
|
||||
import { DataTypes } from 'sequelize';
|
||||
import authMiddleware from '../../middleware/auth.js';
|
||||
import editableMiddleware from '../../middleware/verifyEditable.js';
|
||||
import definecaseTags from '../../models/caseTags.js';
|
||||
|
||||
export default function (sequelize) {
|
||||
const { verifySignedIn } = authMiddleware(sequelize);
|
||||
const { verifyProjectDeveloperFromCaseId } = editableMiddleware(sequelize);
|
||||
const CaseTag = definecaseTags(sequelize, DataTypes);
|
||||
|
||||
router.delete('/:id', verifySignedIn, verifyProjectDeveloperFromCaseId, async (req, res) => {
|
||||
const { id } = req.params;
|
||||
|
||||
if (!id) {
|
||||
return res.status(400).json({
|
||||
error: 'id is required',
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
const deletedCaseTag = await CaseTag.destroy({
|
||||
where: {
|
||||
id: id,
|
||||
},
|
||||
});
|
||||
|
||||
if (!deletedCaseTag) {
|
||||
return res.status(404).json({ error: 'Case-tag association not found' });
|
||||
}
|
||||
|
||||
res.status(204).send();
|
||||
} catch (error) {
|
||||
console.error('Error deleting case-tag association:', error);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
}
|
||||
});
|
||||
|
||||
return router;
|
||||
}
|
||||
64
backend/routes/casetags/edit.js
Normal file
64
backend/routes/casetags/edit.js
Normal file
@@ -0,0 +1,64 @@
|
||||
import express from 'express';
|
||||
const router = express.Router();
|
||||
import { DataTypes } from 'sequelize';
|
||||
import authMiddleware from '../../middleware/auth.js';
|
||||
import editableMiddleware from '../../middleware/verifyEditable.js';
|
||||
import defineCaseTag from '../../models/caseTags.js';
|
||||
import defineCase from '../../models/cases.js';
|
||||
import defineTag from '../../models/tags.js';
|
||||
|
||||
export default function (sequelize) {
|
||||
const { verifySignedIn } = authMiddleware(sequelize);
|
||||
const { verifyProjectDeveloperFromCaseId } = editableMiddleware(sequelize);
|
||||
const CaseTag = defineCaseTag(sequelize, DataTypes);
|
||||
const Case = defineCase(sequelize, DataTypes);
|
||||
const Tag = defineTag(sequelize, DataTypes);
|
||||
|
||||
router.post('/update', verifySignedIn, verifyProjectDeveloperFromCaseId, async (req, res) => {
|
||||
const { tagIds } = req.body;
|
||||
const caseId = req.query.caseId;
|
||||
|
||||
if (!caseId || !Array.isArray(tagIds)) {
|
||||
return res.status(400).json({ error: 'caseId and tagIds[] are required' });
|
||||
}
|
||||
|
||||
if (tagIds.length > 5) {
|
||||
return res.status(400).json({ error: 'Maximum of 5 tags allowed' });
|
||||
}
|
||||
|
||||
try {
|
||||
const testCase = await Case.findByPk(caseId);
|
||||
if (!testCase) {
|
||||
return res.status(404).json({ error: 'Case not found' });
|
||||
}
|
||||
|
||||
const currentAssociations = await CaseTag.findAll({
|
||||
where: { caseId },
|
||||
});
|
||||
|
||||
const currentTagIds = currentAssociations.map((ct) => ct.tagId);
|
||||
|
||||
const tagsToAdd = tagIds.filter((id) => !currentTagIds.includes(id));
|
||||
const tagsToRemove = currentTagIds.filter((id) => !tagIds.includes(id));
|
||||
|
||||
if (tagsToAdd.length > 0) {
|
||||
const validTags = await Tag.findAll({ where: { id: tagsToAdd } });
|
||||
const newLinks = validTags.map((tag) => ({ caseId, tagId: tag.id }));
|
||||
await CaseTag.bulkCreate(newLinks);
|
||||
}
|
||||
|
||||
if (tagsToRemove.length > 0) {
|
||||
await CaseTag.destroy({
|
||||
where: { caseId, tagId: tagsToRemove },
|
||||
});
|
||||
}
|
||||
|
||||
res.status(200).json({ message: 'Tags updated successfully' });
|
||||
} catch (error) {
|
||||
console.error('Error updating case tags:', error);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
}
|
||||
});
|
||||
|
||||
return router;
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
import express from 'express';
|
||||
const router = express.Router();
|
||||
import { DataTypes } from 'sequelize';
|
||||
import authMiddleware from '../../middleware/auth.js';
|
||||
import editableMiddleware from '../../middleware/verifyEditable.js';
|
||||
import definecaseTags from '../../models/caseTags.js';
|
||||
import defineCase from '../../models/cases.js';
|
||||
import defineTag from '../../models/tags.js';
|
||||
|
||||
export default function (sequelize) {
|
||||
const { verifySignedIn } = authMiddleware(sequelize);
|
||||
const { verifyProjectDeveloperFromCaseId } = editableMiddleware(sequelize);
|
||||
const CaseTag = definecaseTags(sequelize, DataTypes);
|
||||
const Case = defineCase(sequelize, DataTypes);
|
||||
const Tags = defineTag(sequelize, DataTypes);
|
||||
|
||||
router.post('/', verifySignedIn, verifyProjectDeveloperFromCaseId, async (req, res) => {
|
||||
const { caseId, tagId } = req.body;
|
||||
|
||||
if (!caseId || !tagId) {
|
||||
return res.status(400).json({
|
||||
error: 'caseId and tagId are required',
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
const caseExists = await Case.findByPk(caseId);
|
||||
if (!caseExists) {
|
||||
return res.status(404).json({ error: 'Case not found' });
|
||||
}
|
||||
|
||||
const tagExists = await Tags.findByPk(tagId);
|
||||
if (!tagExists) {
|
||||
return res.status(404).json({ error: 'Tag not found' });
|
||||
}
|
||||
|
||||
const existingAssociation = await CaseTag.findOne({
|
||||
where: { caseId, tagId },
|
||||
});
|
||||
|
||||
if (existingAssociation) {
|
||||
return res.status(409).json({ error: 'Tag is already associated with this case' });
|
||||
}
|
||||
|
||||
const newCaseTag = await CaseTag.create({
|
||||
caseId,
|
||||
tagId,
|
||||
});
|
||||
|
||||
res.status(201).json(newCaseTag);
|
||||
} catch (error) {
|
||||
console.error('Error creating case-tag association:', error);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
}
|
||||
});
|
||||
|
||||
return router;
|
||||
}
|
||||
@@ -3,7 +3,6 @@ const router = express.Router();
|
||||
import { DataTypes, Op } from 'sequelize';
|
||||
import authMiddleware from '../../middleware/auth.js';
|
||||
import editableMiddleware from '../../middleware/verifyEditable.js';
|
||||
|
||||
import defineTag from '../../models/tags.js';
|
||||
|
||||
export default function (sequelize) {
|
||||
@@ -48,9 +47,19 @@ export default function (sequelize) {
|
||||
return res.status(409).json({ error: 'Tag name must be unique' });
|
||||
}
|
||||
|
||||
const [updated, [updatedTag]] = await Tags.update({ name: trimmedName }, { where: { id: tagId, projectId } });
|
||||
const [updated] = await Tags.update({ name: trimmedName }, { where: { id: tagId, projectId } });
|
||||
|
||||
if (updated === 0) return res.status(404).json({ error: 'Tag not found' });
|
||||
if (updated === 0) {
|
||||
return res.status(404).json({ error: 'Tag not found' });
|
||||
}
|
||||
|
||||
const updatedTag = await Tags.findOne({
|
||||
where: { id: tagId, projectId },
|
||||
});
|
||||
|
||||
if (!updatedTag) {
|
||||
return res.status(404).json({ error: 'Tag not found after update' });
|
||||
}
|
||||
|
||||
res.status(200).json(updatedTag);
|
||||
} catch (error) {
|
||||
|
||||
@@ -168,10 +168,8 @@ app.use('/tags', tagsDeleteRoute(sequelize));
|
||||
app.use('/tags', tagsEditRoute(sequelize));
|
||||
|
||||
// "/casetags"
|
||||
import caseTagsNewRoute from './routes/casetags/new.js';
|
||||
import caseTagsDeleteRoute from './routes/casetags/delete.js';
|
||||
app.use('/casetags', caseTagsNewRoute(sequelize));
|
||||
app.use('/casetags', caseTagsDeleteRoute(sequelize));
|
||||
import caseTagsEditRoute from './routes/casetags/edit.js';
|
||||
app.use('/casetags', caseTagsEditRoute(sequelize));
|
||||
|
||||
// "/home"
|
||||
import homeIndexRoute from './routes/home/index.js';
|
||||
|
||||
Reference in New Issue
Block a user