Merge pull request #32 from internship-humic/feat/notification-system

feat: nambahin link field di Notification
This commit is contained in:
Rafi Athallah
2026-07-06 21:10:25 +07:00
committed by GitHub
7 changed files with 24 additions and 9 deletions
@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE `notification` ADD COLUMN `link` TEXT NULL;
+1
View File
@@ -380,6 +380,7 @@ model Notification {
id_admin Int? // For mentors/admins id_admin Int? // For mentors/admins
title String @db.VarChar(255) title String @db.VarChar(255)
message String @db.Text message String @db.Text
link String? @db.Text
is_read Boolean @default(false) is_read Boolean @default(false)
created_at DateTime @default(now()) created_at DateTime @default(now())
@@ -5,13 +5,14 @@ const { data, error } = require('../../../helpers/utils/wrapper');
const { NotFoundError, ForbiddenError, BadRequestError } = require('../../../helpers/error'); const { NotFoundError, ForbiddenError, BadRequestError } = require('../../../helpers/error');
class NotificationService { class NotificationService {
async createNotificationForIntern(userId, title, message) { async createNotificationForIntern(userId, title, message, link = '') {
try { try {
const notification = await notificationRepository.create({ const notification = await notificationRepository.create({
id_user: parseInt(userId), id_user: parseInt(userId),
id_admin: null, id_admin: null,
title, title,
message, message,
link,
is_read: false is_read: false
}); });
@@ -26,13 +27,14 @@ class NotificationService {
} }
} }
async createNotificationForMentor(adminId, title, message) { async createNotificationForMentor(adminId, title, message, link = '') {
try { try {
const notification = await notificationRepository.create({ const notification = await notificationRepository.create({
id_user: null, id_user: null,
id_admin: parseInt(adminId), id_admin: parseInt(adminId),
title, title,
message, message,
link,
is_read: false is_read: false
}); });
@@ -47,7 +49,7 @@ class NotificationService {
} }
} }
async createNotificationForProjectInterns(projectId, title, message) { async createNotificationForProjectInterns(projectId, title, message, link = '') {
try { try {
const project = await taskRepository.findProjectById(projectId); const project = await taskRepository.findProjectById(projectId);
if (!project || !project.members) { if (!project || !project.members) {
@@ -56,7 +58,7 @@ class NotificationService {
const results = []; const results = [];
for (const member of project.members) { for (const member of project.members) {
const res = await this.createNotificationForIntern(member.id_user, title, message); const res = await this.createNotificationForIntern(member.id_user, title, message, link);
if (res.data) { if (res.data) {
results.push(res.data); results.push(res.data);
} }
@@ -97,7 +97,8 @@ class ProjectService {
await notificationService.createNotificationForIntern( await notificationService.createNotificationForIntern(
idUser, idUser,
"Diterima ke Proyek", "Diterima ke Proyek",
`Anda telah bergabung ke proyek '${project.project_name}'.` `Anda telah bergabung ke proyek '${project.project_name}'.`,
`/intern/projects/${project.slug}`
).catch(err => console.error("Failed to send project assignment notification:", err)); ).catch(err => console.error("Failed to send project assignment notification:", err));
} }
@@ -711,7 +712,8 @@ class ProjectService {
await notificationService.createNotificationForIntern( await notificationService.createNotificationForIntern(
id_user, id_user,
"Diterima ke Proyek", "Diterima ke Proyek",
`Anda telah bergabung ke proyek '${project.project_name}'.` `Anda telah bergabung ke proyek '${project.project_name}'.`,
`/intern/projects/${project.slug}`
).catch(err => console.error("Failed to send project assignment notification:", err)); ).catch(err => console.error("Failed to send project assignment notification:", err));
return data(membership); return data(membership);
+2 -1
View File
@@ -55,7 +55,8 @@ class TaskService {
await notificationService.createNotificationForProjectInterns( await notificationService.createNotificationForProjectInterns(
project.id, project.id,
"Tugas Baru Ditambahkan", "Tugas Baru Ditambahkan",
`Mentor telah membuat tugas baru: '${task.title}' di proyek '${project.project_name}'.` `Mentor telah membuat tugas baru: '${task.title}' di proyek '${project.project_name}'.`,
`/intern/projects/${project.slug}/${task.slug}`
).catch(err => console.error("Failed to send task creation notifications:", err)); ).catch(err => console.error("Failed to send task creation notifications:", err));
return data(this.mapTask(task)); return data(this.mapTask(task));
+4
View File
@@ -28,6 +28,10 @@ const router = express.Router();
* message: * message:
* type: string * type: string
* example: "Mentor telah menambahkan tugas baru." * example: "Mentor telah menambahkan tugas baru."
* link:
* type: string
* nullable: true
* example: "/intern/projects/some-project-slug/some-task-slug"
* is_read: * is_read:
* type: boolean * type: boolean
* example: false * example: false
+5 -2
View File
@@ -7,11 +7,14 @@ const BASE_URL = 'http://localhost:9000';
async function runTests() { async function runTests() {
console.log('=== STARTING INTEGRATION TESTS FOR NOTIFICATION SYSTEM ===\n'); console.log('=== STARTING INTEGRATION TESTS FOR NOTIFICATION SYSTEM ===\n');
// 0. Clean up existing memberships for rafi@student.com (user ID 1) to allow project assignment // 0. Clean up existing memberships and notifications for rafi@student.com (user ID 1)
console.log('0. Cleaning up existing project memberships for rafi@student.com...'); console.log('0. Cleaning up existing project memberships and notifications for rafi@student.com...');
await prisma.projectMember.deleteMany({ await prisma.projectMember.deleteMany({
where: { id_user: 1 } where: { id_user: 1 }
}); });
await prisma.notification.deleteMany({
where: { id_user: 1 }
});
console.log(' Cleanup successful!\n'); console.log(' Cleanup successful!\n');
let adminToken = ''; let adminToken = '';