Files
smanab/backend/routes/staff.js
2026-02-22 14:54:55 +08:00

208 lines
7.1 KiB
JavaScript
Executable File

// Staff Users API Routes - Dynamic User Management
import express from 'express';
import pool from '../db.js';
import crypto from 'crypto';
const router = express.Router();
// Simple password hashing (for demo - in production use bcrypt)
const hashPassword = (password) => {
return crypto.createHash('sha256').update(password).digest('hex');
};
// Helper to parse assigned_classes JSON
const parseAssignedClasses = (row) => {
try {
return row.assigned_classes ? JSON.parse(row.assigned_classes) : [];
} catch {
return [];
}
};
// GET /api/staff - Get all staff users
router.get('/', async (req, res) => {
try {
const [rows] = await pool.query(
'SELECT id, username, name, role, phone, assigned_classes, is_active, created_at FROM staff_users ORDER BY created_at DESC'
);
// Parse assigned_classes for each staff
const result = rows.map(row => ({
...row,
assignedClasses: parseAssignedClasses(row),
assigned_classes: undefined // Remove raw field
}));
res.json(result);
} catch (error) {
console.error('GET /api/staff Error:', error);
res.status(500).json({ error: error.message });
}
});
// GET /api/staff/guru-bk/by-class/:className - Find Guru BK by class
router.get('/guru-bk/by-class/:className', async (req, res) => {
try {
const { className } = req.params;
// Find all GURU_BK who have this class in their assigned_classes
const [rows] = await pool.query(
`SELECT id, username, name, role, phone, assigned_classes, is_active
FROM staff_users
WHERE role = 'GURU_BK' AND is_active = TRUE`
);
// Filter to find Guru BK with matching class
const matchingGuruBK = rows.filter(row => {
const assignedClasses = parseAssignedClasses(row);
return assignedClasses.includes(className);
}).map(row => ({
id: row.id,
name: row.name,
phone: row.phone,
assignedClasses: parseAssignedClasses(row)
}));
if (matchingGuruBK.length > 0) {
// Return the first matching Guru BK
res.json({ found: true, guruBK: matchingGuruBK[0] });
} else {
// No matching Guru BK found, return first active GURU_BK as fallback
const [fallback] = await pool.query(
`SELECT id, name, phone, assigned_classes
FROM staff_users
WHERE role = 'GURU_BK' AND is_active = TRUE
LIMIT 1`
);
if (fallback.length > 0) {
res.json({
found: false,
fallback: true,
guruBK: {
id: fallback[0].id,
name: fallback[0].name,
phone: fallback[0].phone,
assignedClasses: parseAssignedClasses(fallback[0])
}
});
} else {
res.json({ found: false, guruBK: null });
}
}
} catch (error) {
console.error('GET /api/staff/guru-bk/by-class Error:', error);
res.status(500).json({ error: error.message });
}
});
// POST /api/staff - Create new staff user
router.post('/', async (req, res) => {
try {
const { username, password, name, role, phone, assignedClasses } = req.body;
if (!username || !password || !name || !role) {
return res.status(400).json({ error: 'Username, password, name, dan role wajib diisi.' });
}
// Check if username already exists
const [existing] = await pool.query('SELECT id FROM staff_users WHERE username = ?', [username]);
if (existing.length > 0) {
return res.status(400).json({ error: 'Username sudah digunakan.' });
}
const id = crypto.randomUUID();
const hashedPassword = hashPassword(password);
const assignedClassesJson = assignedClasses && Array.isArray(assignedClasses)
? JSON.stringify(assignedClasses)
: null;
await pool.query(
'INSERT INTO staff_users (id, username, password, name, role, phone, assigned_classes) VALUES (?, ?, ?, ?, ?, ?, ?)',
[id, username, hashedPassword, name, role, phone || null, assignedClassesJson]
);
res.json({ success: true, id, message: 'Staff berhasil ditambahkan.' });
} catch (error) {
console.error('POST /api/staff Error:', error);
res.status(500).json({ error: error.message });
}
});
// PUT /api/staff/:id - Update staff user
router.put('/:id', async (req, res) => {
try {
const { id } = req.params;
const { username, password, name, role, phone, assignedClasses, is_active } = req.body;
// Build dynamic update query
const updates = [];
const params = [];
if (username) {
updates.push('username = ?');
params.push(username);
}
if (password) {
updates.push('password = ?');
params.push(hashPassword(password));
}
if (name) {
updates.push('name = ?');
params.push(name);
}
if (role) {
updates.push('role = ?');
params.push(role);
}
if (typeof is_active === 'boolean') {
updates.push('is_active = ?');
params.push(is_active);
}
if (phone !== undefined) {
updates.push('phone = ?');
params.push(phone || null);
}
if (assignedClasses !== undefined) {
updates.push('assigned_classes = ?');
params.push(Array.isArray(assignedClasses) ? JSON.stringify(assignedClasses) : null);
}
if (updates.length === 0) {
return res.status(400).json({ error: 'Tidak ada data yang diubah.' });
}
params.push(id);
await pool.query(
`UPDATE staff_users SET ${updates.join(', ')} WHERE id = ?`,
params
);
res.json({ success: true, message: 'Staff berhasil diupdate.' });
} catch (error) {
console.error('PUT /api/staff Error:', error);
res.status(500).json({ error: error.message });
}
});
// DELETE /api/staff/:id - Delete staff user
router.delete('/:id', async (req, res) => {
try {
const { id } = req.params;
// Prevent deleting the last admin
const [admins] = await pool.query("SELECT COUNT(*) as count FROM staff_users WHERE role = 'ADMIN'");
const [targetUser] = await pool.query('SELECT role FROM staff_users WHERE id = ?', [id]);
if (targetUser.length > 0 && targetUser[0].role === 'ADMIN' && admins[0].count <= 1) {
return res.status(400).json({ error: 'Tidak dapat menghapus admin terakhir.' });
}
await pool.query('DELETE FROM staff_users WHERE id = ?', [id]);
res.json({ success: true, message: 'Staff berhasil dihapus.' });
} catch (error) {
console.error('DELETE /api/staff Error:', error);
res.status(500).json({ error: error.message });
}
});
export default router;