feat(tags): Test case tag editing UI and tag settings UI (#322)

This commit is contained in:
Eliezer Castro
2025-11-02 04:52:15 -03:00
committed by GitHub
parent b71f3e99cc
commit 3fd226bdbd
21 changed files with 896 additions and 143 deletions

View File

@@ -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;
}

View 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;
}

View File

@@ -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;
}

View File

@@ -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) {