mirror of
https://github.com/rgrgogu/new_starr.git
synced 2026-09-27 00:12:54 +08:00
127 lines
6.2 KiB
JavaScript
127 lines
6.2 KiB
JavaScript
'use strict';
|
|
|
|
const mdl_AchievementDefinitions = require('../../models/users/achievement_definitions.mdl');
|
|
const CourseAchievement = require('../../models/courses/course_achievement.mdl');
|
|
const R = require('../../utils/response.util');
|
|
const logActivity = require('../../utils/logActivity.util');
|
|
|
|
const VALID_TYPES = ['badge', 'milestone'];
|
|
|
|
// ─── GET /admin/achievements ──────────────────────────────────────────────────
|
|
|
|
exports.getAchievements = async (req, res) => {
|
|
try {
|
|
const achievements = await mdl_AchievementDefinitions.findAll({
|
|
order: [['createdAt', 'ASC']],
|
|
});
|
|
return R.success(res, 'Achievements retrieved.', achievements);
|
|
} catch (err) {
|
|
console.error('[ADMIN][GET ACHIEVEMENTS]', err);
|
|
return R.error(res, 'Could not retrieve achievements.', 500);
|
|
}
|
|
};
|
|
|
|
// ─── GET /admin/achievements/:id ──────────────────────────────────────────────
|
|
|
|
exports.getAchievement = async (req, res) => {
|
|
try {
|
|
const achievement = await mdl_AchievementDefinitions.findByPk(req.params.id);
|
|
if (!achievement) return R.error(res, 'Achievement not found.', 404);
|
|
return R.success(res, 'Achievement retrieved.', achievement);
|
|
} catch (err) {
|
|
console.error('[ADMIN][GET ACHIEVEMENT]', err);
|
|
return R.error(res, 'Could not retrieve achievement.', 500);
|
|
}
|
|
};
|
|
|
|
// ─── POST /admin/achievements ─────────────────────────────────────────────────
|
|
|
|
exports.createAchievement = async (req, res) => {
|
|
try {
|
|
const { key, type, label, description, icon, trigger, is_active } = req.body;
|
|
if (!key || !label) return R.error(res, 'key and label are required.', 400);
|
|
if (type && !VALID_TYPES.includes(type)) return R.error(res, `type must be one of: ${VALID_TYPES.join(', ')}.`, 400);
|
|
|
|
const exists = await mdl_AchievementDefinitions.findOne({ where: { key } });
|
|
if (exists) return R.error(res, `An achievement with key "${key}" already exists.`, 409);
|
|
|
|
const achievement = await mdl_AchievementDefinitions.create({
|
|
key,
|
|
type: type || 'badge',
|
|
label,
|
|
description: description ?? null,
|
|
icon: icon || null,
|
|
trigger: trigger || null,
|
|
is_active: is_active !== undefined ? !!is_active : true,
|
|
is_system: false, // only seed data may be system-protected
|
|
});
|
|
|
|
logActivity(req.user?.user_id, 'create_achievement', { entityType: 'achievement', details: { key, label } });
|
|
|
|
return R.success(res, 'Achievement created.', achievement, 201);
|
|
} catch (err) {
|
|
console.error('[ADMIN][CREATE ACHIEVEMENT]', err);
|
|
return R.error(res, 'Could not create achievement.', 500);
|
|
}
|
|
};
|
|
|
|
// ─── PUT /admin/achievements/:id ──────────────────────────────────────────────
|
|
|
|
exports.updateAchievement = async (req, res) => {
|
|
try {
|
|
const achievement = await mdl_AchievementDefinitions.findByPk(req.params.id);
|
|
if (!achievement) return R.error(res, 'Achievement not found.', 404);
|
|
|
|
const { key, type, label, description, icon, trigger, is_active } = req.body;
|
|
|
|
if (achievement.is_system && key !== undefined && key !== achievement.key)
|
|
return R.error(res, 'The key of a system achievement cannot be changed.', 400);
|
|
if (achievement.is_system && type !== undefined && type !== achievement.type)
|
|
return R.error(res, 'The type of a system achievement cannot be changed.', 400);
|
|
if (type && !VALID_TYPES.includes(type)) return R.error(res, `type must be one of: ${VALID_TYPES.join(', ')}.`, 400);
|
|
|
|
if (!achievement.is_system && key !== undefined && key !== achievement.key) {
|
|
const exists = await mdl_AchievementDefinitions.findOne({ where: { key } });
|
|
if (exists) return R.error(res, `An achievement with key "${key}" already exists.`, 409);
|
|
}
|
|
|
|
await achievement.update({
|
|
key: (!achievement.is_system && key !== undefined) ? key : achievement.key,
|
|
type: (!achievement.is_system && type !== undefined) ? type : achievement.type,
|
|
label: label ?? achievement.label,
|
|
description: description !== undefined ? (description || null) : achievement.description,
|
|
icon: icon !== undefined ? (icon || null) : achievement.icon,
|
|
trigger: trigger !== undefined ? (trigger || null) : achievement.trigger,
|
|
is_active: is_active !== undefined ? !!is_active : achievement.is_active,
|
|
});
|
|
|
|
logActivity(req.user?.user_id, 'update_achievement', { entityType: 'achievement', details: { id: achievement.achievement_definition_id, key: achievement.key } });
|
|
|
|
return R.success(res, 'Achievement updated.', achievement);
|
|
} catch (err) {
|
|
console.error('[ADMIN][UPDATE ACHIEVEMENT]', err);
|
|
return R.error(res, 'Could not update achievement.', 500);
|
|
}
|
|
};
|
|
|
|
// ─── DELETE /admin/achievements/:id ───────────────────────────────────────────
|
|
|
|
exports.deleteAchievement = async (req, res) => {
|
|
try {
|
|
const achievement = await mdl_AchievementDefinitions.findByPk(req.params.id);
|
|
if (!achievement) return R.error(res, 'Achievement not found.', 404);
|
|
if (achievement.is_system) return R.error(res, 'Built-in system achievements cannot be deleted.', 400);
|
|
|
|
const assignedCourses = await CourseAchievement.count({ where: { achievement_key: achievement.key } });
|
|
if (assignedCourses > 0)
|
|
return R.error(res, `Cannot delete — ${assignedCourses} course(s) still reference this achievement. Unassign it first.`, 409);
|
|
|
|
await achievement.destroy();
|
|
logActivity(req.user?.user_id, 'delete_achievement', { entityType: 'achievement', details: { key: achievement.key } });
|
|
return R.success(res, 'Achievement deleted.');
|
|
} catch (err) {
|
|
console.error('[ADMIN][DELETE ACHIEVEMENT]', err);
|
|
return R.error(res, 'Could not delete achievement.', 500);
|
|
}
|
|
};
|