(Feat): Initial Commit

This commit is contained in:
2025-11-27 22:50:08 +00:00
commit 00f9ed128b
79 changed files with 17413 additions and 0 deletions

View File

@@ -0,0 +1,259 @@
import express from 'express';
import db from '../config/database.js';
import { authenticateToken, authorize } from '../middleware/auth.js';
const router = express.Router();
// Get all attendance records
router.get('/', authenticateToken, async (req, res) => {
try {
const { employeeId, startDate, endDate, status } = req.query;
let query = `
SELECT a.*,
e.name as employee_name, e.username as employee_username,
s.name as supervisor_name,
d.name as department_name,
c.name as contractor_name
FROM attendance a
JOIN users e ON a.employee_id = e.id
JOIN users s ON a.supervisor_id = s.id
LEFT JOIN departments d ON e.department_id = d.id
LEFT JOIN users c ON e.contractor_id = c.id
WHERE 1=1
`;
const params = [];
// Role-based filtering
if (req.user.role === 'Supervisor') {
query += ' AND a.supervisor_id = ?';
params.push(req.user.id);
} else if (req.user.role === 'Employee') {
query += ' AND a.employee_id = ?';
params.push(req.user.id);
}
if (employeeId) {
query += ' AND a.employee_id = ?';
params.push(employeeId);
}
if (startDate) {
query += ' AND a.work_date >= ?';
params.push(startDate);
}
if (endDate) {
query += ' AND a.work_date <= ?';
params.push(endDate);
}
if (status) {
query += ' AND a.status = ?';
params.push(status);
}
query += ' ORDER BY a.work_date DESC, a.check_in_time DESC';
const [records] = await db.query(query, params);
res.json(records);
} catch (error) {
console.error('Get attendance error:', error);
res.status(500).json({ error: 'Internal server error' });
}
});
// Get attendance by ID
router.get('/:id', authenticateToken, async (req, res) => {
try {
const [records] = await db.query(
`SELECT a.*,
e.name as employee_name, e.username as employee_username,
s.name as supervisor_name,
d.name as department_name,
c.name as contractor_name
FROM attendance a
JOIN users e ON a.employee_id = e.id
JOIN users s ON a.supervisor_id = s.id
LEFT JOIN departments d ON e.department_id = d.id
LEFT JOIN users c ON e.contractor_id = c.id
WHERE a.id = ?`,
[req.params.id]
);
if (records.length === 0) {
return res.status(404).json({ error: 'Attendance record not found' });
}
res.json(records[0]);
} catch (error) {
console.error('Get attendance error:', error);
res.status(500).json({ error: 'Internal server error' });
}
});
// Check in employee (Supervisor or SuperAdmin)
router.post('/check-in', authenticateToken, authorize('Supervisor', 'SuperAdmin'), async (req, res) => {
try {
const { employeeId, workDate } = req.body;
if (!employeeId || !workDate) {
return res.status(400).json({ error: 'Employee ID and work date required' });
}
// Verify employee exists (SuperAdmin can check in any employee, Supervisor only their department)
let employeeQuery = 'SELECT * FROM users WHERE id = ? AND role = ?';
let employeeParams = [employeeId, 'Employee'];
if (req.user.role === 'Supervisor') {
employeeQuery += ' AND department_id = ?';
employeeParams.push(req.user.departmentId);
}
const [employees] = await db.query(employeeQuery, employeeParams);
if (employees.length === 0) {
return res.status(403).json({ error: 'Employee not found or not in your department' });
}
// Check if already checked in today
const [existing] = await db.query(
'SELECT * FROM attendance WHERE employee_id = ? AND work_date = ? AND status = ?',
[employeeId, workDate, 'CheckedIn']
);
if (existing.length > 0) {
return res.status(400).json({ error: 'Employee already checked in today' });
}
const checkInTime = new Date();
const [result] = await db.query(
'INSERT INTO attendance (employee_id, supervisor_id, check_in_time, work_date, status) VALUES (?, ?, ?, ?, ?)',
[employeeId, req.user.id, checkInTime, workDate, 'CheckedIn']
);
const [newRecord] = await db.query(
`SELECT a.*,
e.name as employee_name, e.username as employee_username,
s.name as supervisor_name,
d.name as department_name,
c.name as contractor_name
FROM attendance a
JOIN users e ON a.employee_id = e.id
JOIN users s ON a.supervisor_id = s.id
LEFT JOIN departments d ON e.department_id = d.id
LEFT JOIN users c ON e.contractor_id = c.id
WHERE a.id = ?`,
[result.insertId]
);
res.status(201).json(newRecord[0]);
} catch (error) {
console.error('Check in error:', error);
res.status(500).json({ error: 'Internal server error' });
}
});
// Check out employee (Supervisor or SuperAdmin)
router.post('/check-out', authenticateToken, authorize('Supervisor', 'SuperAdmin'), async (req, res) => {
try {
const { employeeId, workDate } = req.body;
if (!employeeId || !workDate) {
return res.status(400).json({ error: 'Employee ID and work date required' });
}
// Find the check-in record (SuperAdmin can check out any, Supervisor only their own)
let query = 'SELECT * FROM attendance WHERE employee_id = ? AND work_date = ? AND status = ?';
let params = [employeeId, workDate, 'CheckedIn'];
if (req.user.role === 'Supervisor') {
query += ' AND supervisor_id = ?';
params.push(req.user.id);
}
const [records] = await db.query(query, params);
if (records.length === 0) {
return res.status(404).json({ error: 'No check-in record found for today' });
}
const checkOutTime = new Date();
await db.query(
'UPDATE attendance SET check_out_time = ?, status = ? WHERE id = ?',
[checkOutTime, 'CheckedOut', records[0].id]
);
const [updatedRecord] = await db.query(
`SELECT a.*,
e.name as employee_name, e.username as employee_username,
s.name as supervisor_name,
d.name as department_name,
c.name as contractor_name
FROM attendance a
JOIN users e ON a.employee_id = e.id
JOIN users s ON a.supervisor_id = s.id
LEFT JOIN departments d ON e.department_id = d.id
LEFT JOIN users c ON e.contractor_id = c.id
WHERE a.id = ?`,
[records[0].id]
);
res.json(updatedRecord[0]);
} catch (error) {
console.error('Check out error:', error);
res.status(500).json({ error: 'Internal server error' });
}
});
// Get attendance summary
router.get('/summary/stats', authenticateToken, async (req, res) => {
try {
const { startDate, endDate, departmentId } = req.query;
let query = `
SELECT
COUNT(DISTINCT a.employee_id) as total_employees,
COUNT(DISTINCT CASE WHEN a.status = 'CheckedIn' THEN a.employee_id END) as checked_in,
COUNT(DISTINCT CASE WHEN a.status = 'CheckedOut' THEN a.employee_id END) as checked_out,
d.name as department_name
FROM attendance a
JOIN users e ON a.employee_id = e.id
LEFT JOIN departments d ON e.department_id = d.id
WHERE 1=1
`;
const params = [];
if (req.user.role === 'Supervisor') {
query += ' AND a.supervisor_id = ?';
params.push(req.user.id);
}
if (startDate) {
query += ' AND a.work_date >= ?';
params.push(startDate);
}
if (endDate) {
query += ' AND a.work_date <= ?';
params.push(endDate);
}
if (departmentId) {
query += ' AND e.department_id = ?';
params.push(departmentId);
}
query += ' GROUP BY d.id, d.name';
const [summary] = await db.query(query, params);
res.json(summary);
} catch (error) {
console.error('Get attendance summary error:', error);
res.status(500).json({ error: 'Internal server error' });
}
});
export default router;

114
backend/routes/auth.js Normal file
View File

@@ -0,0 +1,114 @@
import express from 'express';
import bcrypt from 'bcryptjs';
import jwt from 'jsonwebtoken';
import db from '../config/database.js';
import { authenticateToken } from '../middleware/auth.js';
const router = express.Router();
// Login
router.post('/login', async (req, res) => {
try {
const { username, password } = req.body;
if (!username || !password) {
return res.status(400).json({ error: 'Username and password required' });
}
const [users] = await db.query(
'SELECT * FROM users WHERE username = ? AND is_active = TRUE',
[username]
);
if (users.length === 0) {
return res.status(401).json({ error: 'Invalid credentials' });
}
const user = users[0];
const validPassword = await bcrypt.compare(password, user.password);
if (!validPassword) {
return res.status(401).json({ error: 'Invalid credentials' });
}
const token = jwt.sign(
{
id: user.id,
username: user.username,
role: user.role,
departmentId: user.department_id
},
process.env.JWT_SECRET,
{ expiresIn: process.env.JWT_EXPIRES_IN || '7d' }
);
res.json({
token,
user: {
id: user.id,
username: user.username,
name: user.name,
email: user.email,
role: user.role,
department_id: user.department_id,
contractor_id: user.contractor_id,
is_active: user.is_active
}
});
} catch (error) {
console.error('Login error:', error);
res.status(500).json({ error: 'Internal server error' });
}
});
// Get current user
router.get('/me', authenticateToken, async (req, res) => {
try {
const [users] = await db.query(
'SELECT id, username, name, email, role, department_id, contractor_id FROM users WHERE id = ?',
[req.user.id]
);
if (users.length === 0) {
return res.status(404).json({ error: 'User not found' });
}
res.json(users[0]);
} catch (error) {
console.error('Get user error:', error);
res.status(500).json({ error: 'Internal server error' });
}
});
// Change password
router.post('/change-password', authenticateToken, async (req, res) => {
try {
const { currentPassword, newPassword } = req.body;
if (!currentPassword || !newPassword) {
return res.status(400).json({ error: 'Current and new password required' });
}
const [users] = await db.query('SELECT password FROM users WHERE id = ?', [req.user.id]);
if (users.length === 0) {
return res.status(404).json({ error: 'User not found' });
}
const validPassword = await bcrypt.compare(currentPassword, users[0].password);
if (!validPassword) {
return res.status(401).json({ error: 'Current password is incorrect' });
}
const hashedPassword = await bcrypt.hash(newPassword, 10);
await db.query('UPDATE users SET password = ? WHERE id = ?', [hashedPassword, req.user.id]);
res.json({ message: 'Password changed successfully' });
} catch (error) {
console.error('Change password error:', error);
res.status(500).json({ error: 'Internal server error' });p
}
});
export default router;

View File

@@ -0,0 +1,198 @@
import express from 'express';
import db from '../config/database.js';
import { authenticateToken, authorize } from '../middleware/auth.js';
const router = express.Router();
// Get contractor rates
router.get('/', authenticateToken, async (req, res) => {
try {
const { contractorId, subDepartmentId } = req.query;
let query = `
SELECT cr.*,
u.name as contractor_name, u.username as contractor_username,
sd.name as sub_department_name,
d.name as department_name
FROM contractor_rates cr
JOIN users u ON cr.contractor_id = u.id
LEFT JOIN sub_departments sd ON cr.sub_department_id = sd.id
LEFT JOIN departments d ON sd.department_id = d.id
WHERE 1=1
`;
const params = [];
if (contractorId) {
query += ' AND cr.contractor_id = ?';
params.push(contractorId);
}
if (subDepartmentId) {
query += ' AND cr.sub_department_id = ?';
params.push(subDepartmentId);
}
query += ' ORDER BY cr.effective_date DESC, cr.created_at DESC';
const [rates] = await db.query(query, params);
res.json(rates);
} catch (error) {
console.error('Get contractor rates error:', error);
res.status(500).json({ error: 'Internal server error' });
}
});
// Get current rate for a contractor + sub-department combination
router.get('/contractor/:contractorId/current', authenticateToken, async (req, res) => {
try {
const { subDepartmentId } = req.query;
let query = `
SELECT cr.*,
u.name as contractor_name, u.username as contractor_username,
sd.name as sub_department_name
FROM contractor_rates cr
JOIN users u ON cr.contractor_id = u.id
LEFT JOIN sub_departments sd ON cr.sub_department_id = sd.id
WHERE cr.contractor_id = ?
`;
const params = [req.params.contractorId];
if (subDepartmentId) {
query += ' AND cr.sub_department_id = ?';
params.push(subDepartmentId);
}
query += ' ORDER BY cr.effective_date DESC LIMIT 1';
const [rates] = await db.query(query, params);
if (rates.length === 0) {
return res.status(404).json({ error: 'No rate found for contractor' });
}
res.json(rates[0]);
} catch (error) {
console.error('Get current rate error:', error);
res.status(500).json({ error: 'Internal server error' });
}
});
// Set contractor rate (Supervisor or SuperAdmin)
router.post('/', authenticateToken, authorize('Supervisor', 'SuperAdmin'), async (req, res) => {
try {
const { contractorId, subDepartmentId, activity, rate, effectiveDate } = req.body;
if (!contractorId || !rate || !effectiveDate) {
return res.status(400).json({ error: 'Missing required fields (contractorId, rate, effectiveDate)' });
}
// Verify contractor exists
const [contractors] = await db.query(
'SELECT * FROM users WHERE id = ? AND role = ?',
[contractorId, 'Contractor']
);
if (contractors.length === 0) {
return res.status(404).json({ error: 'Contractor not found' });
}
// Supervisors can only set rates for contractors in their department
if (req.user.role === 'Supervisor' && contractors[0].department_id !== req.user.departmentId) {
return res.status(403).json({ error: 'Contractor not in your department' });
}
const [result] = await db.query(
'INSERT INTO contractor_rates (contractor_id, sub_department_id, activity, rate, effective_date) VALUES (?, ?, ?, ?, ?)',
[contractorId, subDepartmentId || null, activity || null, rate, effectiveDate]
);
const [newRate] = await db.query(
`SELECT cr.*,
u.name as contractor_name, u.username as contractor_username,
sd.name as sub_department_name
FROM contractor_rates cr
JOIN users u ON cr.contractor_id = u.id
LEFT JOIN sub_departments sd ON cr.sub_department_id = sd.id
WHERE cr.id = ?`,
[result.insertId]
);
res.status(201).json(newRate[0]);
} catch (error) {
console.error('Set contractor rate error:', error);
res.status(500).json({ error: 'Internal server error' });
}
});
// Update contractor rate
router.put('/:id', authenticateToken, authorize('Supervisor', 'SuperAdmin'), async (req, res) => {
try {
const { rate, activity, effectiveDate } = req.body;
const [existing] = await db.query('SELECT * FROM contractor_rates WHERE id = ?', [req.params.id]);
if (existing.length === 0) {
return res.status(404).json({ error: 'Rate not found' });
}
const updates = [];
const params = [];
if (rate !== undefined) {
updates.push('rate = ?');
params.push(rate);
}
if (activity !== undefined) {
updates.push('activity = ?');
params.push(activity);
}
if (effectiveDate !== undefined) {
updates.push('effective_date = ?');
params.push(effectiveDate);
}
if (updates.length === 0) {
return res.status(400).json({ error: 'No fields to update' });
}
params.push(req.params.id);
await db.query(`UPDATE contractor_rates SET ${updates.join(', ')} WHERE id = ?`, params);
const [updatedRate] = await db.query(
`SELECT cr.*,
u.name as contractor_name, u.username as contractor_username,
sd.name as sub_department_name
FROM contractor_rates cr
JOIN users u ON cr.contractor_id = u.id
LEFT JOIN sub_departments sd ON cr.sub_department_id = sd.id
WHERE cr.id = ?`,
[req.params.id]
);
res.json(updatedRate[0]);
} catch (error) {
console.error('Update contractor rate error:', error);
res.status(500).json({ error: 'Internal server error' });
}
});
// Delete contractor rate
router.delete('/:id', authenticateToken, authorize('Supervisor', 'SuperAdmin'), async (req, res) => {
try {
const [existing] = await db.query('SELECT * FROM contractor_rates WHERE id = ?', [req.params.id]);
if (existing.length === 0) {
return res.status(404).json({ error: 'Rate not found' });
}
await db.query('DELETE FROM contractor_rates WHERE id = ?', [req.params.id]);
res.json({ message: 'Rate deleted successfully' });
} catch (error) {
console.error('Delete contractor rate error:', error);
res.status(500).json({ error: 'Internal server error' });
}
});
export default router;

View File

@@ -0,0 +1,96 @@
import express from 'express';
import db from '../config/database.js';
import { authenticateToken, authorize } from '../middleware/auth.js';
const router = express.Router();
// Get all departments
router.get('/', authenticateToken, async (req, res) => {
try {
const [departments] = await db.query('SELECT * FROM departments ORDER BY name');
res.json(departments);
} catch (error) {
console.error('Get departments error:', error);
res.status(500).json({ error: 'Internal server error' });
}
});
// Get department by ID
router.get('/:id', authenticateToken, async (req, res) => {
try {
const [departments] = await db.query('SELECT * FROM departments WHERE id = ?', [req.params.id]);
if (departments.length === 0) {
return res.status(404).json({ error: 'Department not found' });
}
res.json(departments[0]);
} catch (error) {
console.error('Get department error:', error);
res.status(500).json({ error: 'Internal server error' });
}
});
// Get sub-departments by department ID
router.get('/:id/sub-departments', authenticateToken, async (req, res) => {
try {
const [subDepartments] = await db.query(
'SELECT * FROM sub_departments WHERE department_id = ? ORDER BY name',
[req.params.id]
);
res.json(subDepartments);
} catch (error) {
console.error('Get sub-departments error:', error);
res.status(500).json({ error: 'Internal server error' });
}
});
// Create department (SuperAdmin only)
router.post('/', authenticateToken, authorize('SuperAdmin'), async (req, res) => {
try {
const { name } = req.body;
if (!name) {
return res.status(400).json({ error: 'Department name required' });
}
const [result] = await db.query('INSERT INTO departments (name) VALUES (?)', [name]);
const [newDepartment] = await db.query('SELECT * FROM departments WHERE id = ?', [result.insertId]);
res.status(201).json(newDepartment[0]);
} catch (error) {
if (error.code === 'ER_DUP_ENTRY') {
return res.status(400).json({ error: 'Department already exists' });
}
console.error('Create department error:', error);
res.status(500).json({ error: 'Internal server error' });
}
});
// Create sub-department (SuperAdmin only)
router.post('/:id/sub-departments', authenticateToken, authorize('SuperAdmin'), async (req, res) => {
try {
const { name, primaryActivity } = req.body;
if (!name || !primaryActivity) {
return res.status(400).json({ error: 'Name and primary activity required' });
}
const [result] = await db.query(
'INSERT INTO sub_departments (department_id, name, primary_activity) VALUES (?, ?, ?)',
[req.params.id, name, primaryActivity]
);
const [newSubDepartment] = await db.query(
'SELECT * FROM sub_departments WHERE id = ?',
[result.insertId]
);
res.status(201).json(newSubDepartment[0]);
} catch (error) {
console.error('Create sub-department error:', error);
res.status(500).json({ error: 'Internal server error' });
}
});
export default router;

236
backend/routes/users.js Normal file
View File

@@ -0,0 +1,236 @@
import express from 'express';
import bcrypt from 'bcryptjs';
import db from '../config/database.js';
import { authenticateToken, authorize } from '../middleware/auth.js';
const router = express.Router();
// Get all users (with filters)
router.get('/', authenticateToken, async (req, res) => {
try {
const { role, departmentId } = req.query;
let query = `
SELECT u.id, u.username, u.name, u.email, u.role, u.department_id,
u.contractor_id, u.is_active, u.created_at,
d.name as department_name,
c.name as contractor_name
FROM users u
LEFT JOIN departments d ON u.department_id = d.id
LEFT JOIN users c ON u.contractor_id = c.id
WHERE 1=1
`;
const params = [];
// Supervisors can only see users in their department
if (req.user.role === 'Supervisor') {
query += ' AND u.department_id = ?';
params.push(req.user.departmentId);
}
if (role) {
query += ' AND u.role = ?';
params.push(role);
}
if (departmentId) {
query += ' AND u.department_id = ?';
params.push(departmentId);
}
query += ' ORDER BY u.created_at DESC';
const [users] = await db.query(query, params);
res.json(users);
} catch (error) {
console.error('Get users error:', error);
res.status(500).json({ error: 'Internal server error' });
}
});
// Get user by ID
router.get('/:id', authenticateToken, async (req, res) => {
try {
const [users] = await db.query(
`SELECT u.id, u.username, u.name, u.email, u.role, u.department_id,
u.contractor_id, u.is_active, u.created_at,
d.name as department_name,
c.name as contractor_name
FROM users u
LEFT JOIN departments d ON u.department_id = d.id
LEFT JOIN users c ON u.contractor_id = c.id
WHERE u.id = ?`,
[req.params.id]
);
if (users.length === 0) {
return res.status(404).json({ error: 'User not found' });
}
// Supervisors can only view users in their department
if (req.user.role === 'Supervisor' && users[0].department_id !== req.user.departmentId) {
return res.status(403).json({ error: 'Access denied' });
}
res.json(users[0]);
} catch (error) {
console.error('Get user error:', error);
res.status(500).json({ error: 'Internal server error' });
}
});
// Create user
router.post('/', authenticateToken, authorize('SuperAdmin', 'Supervisor'), async (req, res) => {
try {
const { username, name, email, password, role, departmentId, contractorId } = req.body;
if (!username || !name || !email || !password || !role) {
return res.status(400).json({ error: 'Missing required fields' });
}
// Supervisors can only create users in their department
if (req.user.role === 'Supervisor') {
if (departmentId !== req.user.departmentId) {
return res.status(403).json({ error: 'Can only create users in your department' });
}
if (role === 'SuperAdmin' || role === 'Supervisor') {
return res.status(403).json({ error: 'Cannot create admin or supervisor users' });
}
}
const hashedPassword = await bcrypt.hash(password, 10);
const [result] = await db.query(
'INSERT INTO users (username, name, email, password, role, department_id, contractor_id) VALUES (?, ?, ?, ?, ?, ?, ?)',
[username, name, email, hashedPassword, role, departmentId || null, contractorId || null]
);
const [newUser] = await db.query(
`SELECT u.id, u.username, u.name, u.email, u.role, u.department_id,
u.contractor_id, u.is_active, u.created_at,
d.name as department_name,
c.name as contractor_name
FROM users u
LEFT JOIN departments d ON u.department_id = d.id
LEFT JOIN users c ON u.contractor_id = c.id
WHERE u.id = ?`,
[result.insertId]
);
res.status(201).json(newUser[0]);
} catch (error) {
if (error.code === 'ER_DUP_ENTRY') {
return res.status(400).json({ error: 'Username or email already exists' });
}
console.error('Create user error:', error);
res.status(500).json({ error: 'Internal server error' });
}
});
// Update user
router.put('/:id', authenticateToken, authorize('SuperAdmin', 'Supervisor'), async (req, res) => {
try {
const { name, email, role, departmentId, contractorId, isActive } = req.body;
// Check if user exists
const [existingUsers] = await db.query('SELECT * FROM users WHERE id = ?', [req.params.id]);
if (existingUsers.length === 0) {
return res.status(404).json({ error: 'User not found' });
}
// Supervisors can only update users in their department
if (req.user.role === 'Supervisor') {
if (existingUsers[0].department_id !== req.user.departmentId) {
return res.status(403).json({ error: 'Can only update users in your department' });
}
if (role === 'SuperAdmin' || role === 'Supervisor') {
return res.status(403).json({ error: 'Cannot modify admin or supervisor roles' });
}
}
const updates = [];
const params = [];
if (name !== undefined) {
updates.push('name = ?');
params.push(name);
}
if (email !== undefined) {
updates.push('email = ?');
params.push(email);
}
if (role !== undefined) {
updates.push('role = ?');
params.push(role);
}
if (departmentId !== undefined) {
updates.push('department_id = ?');
params.push(departmentId);
}
if (contractorId !== undefined) {
updates.push('contractor_id = ?');
params.push(contractorId);
}
if (isActive !== undefined) {
updates.push('is_active = ?');
params.push(isActive);
}
if (updates.length === 0) {
return res.status(400).json({ error: 'No fields to update' });
}
params.push(req.params.id);
await db.query(
`UPDATE users SET ${updates.join(', ')} WHERE id = ?`,
params
);
const [updatedUser] = await db.query(
`SELECT u.id, u.username, u.name, u.email, u.role, u.department_id,
u.contractor_id, u.is_active, u.created_at,
d.name as department_name,
c.name as contractor_name
FROM users u
LEFT JOIN departments d ON u.department_id = d.id
LEFT JOIN users c ON u.contractor_id = c.id
WHERE u.id = ?`,
[req.params.id]
);
res.json(updatedUser[0]);
} catch (error) {
console.error('Update user error:', error);
res.status(500).json({ error: 'Internal server error' });
}
});
// Delete user
router.delete('/:id', authenticateToken, authorize('SuperAdmin', 'Supervisor'), async (req, res) => {
try {
const [users] = await db.query('SELECT * FROM users WHERE id = ?', [req.params.id]);
if (users.length === 0) {
return res.status(404).json({ error: 'User not found' });
}
// Supervisors can only delete users in their department
if (req.user.role === 'Supervisor') {
if (users[0].department_id !== req.user.departmentId) {
return res.status(403).json({ error: 'Can only delete users in your department' });
}
if (users[0].role === 'SuperAdmin' || users[0].role === 'Supervisor') {
return res.status(403).json({ error: 'Cannot delete admin or supervisor users' });
}
}
await db.query('DELETE FROM users WHERE id = ?', [req.params.id]);
res.json({ message: 'User deleted successfully' });
} catch (error) {
console.error('Delete user error:', error);
res.status(500).json({ error: 'Internal server error' });
}
});
export default router;

View File

@@ -0,0 +1,244 @@
import express from 'express';
import db from '../config/database.js';
import { authenticateToken, authorize } from '../middleware/auth.js';
const router = express.Router();
// Get all work allocations
router.get('/', authenticateToken, async (req, res) => {
try {
const { employeeId, status, departmentId } = req.query;
let query = `
SELECT wa.*,
e.name as employee_name, e.username as employee_username,
s.name as supervisor_name,
c.name as contractor_name,
sd.name as sub_department_name,
d.name as department_name
FROM work_allocations wa
JOIN users e ON wa.employee_id = e.id
JOIN users s ON wa.supervisor_id = s.id
JOIN users c ON wa.contractor_id = c.id
LEFT JOIN sub_departments sd ON wa.sub_department_id = sd.id
LEFT JOIN departments d ON e.department_id = d.id
WHERE 1=1
`;
const params = [];
// Role-based filtering
if (req.user.role === 'Supervisor') {
query += ' AND wa.supervisor_id = ?';
params.push(req.user.id);
} else if (req.user.role === 'Employee') {
query += ' AND wa.employee_id = ?';
params.push(req.user.id);
} else if (req.user.role === 'Contractor') {
query += ' AND wa.contractor_id = ?';
params.push(req.user.id);
}
if (employeeId) {
query += ' AND wa.employee_id = ?';
params.push(employeeId);
}
if (status) {
query += ' AND wa.status = ?';
params.push(status);
}
if (departmentId) {
query += ' AND e.department_id = ?';
params.push(departmentId);
}
query += ' ORDER BY wa.assigned_date DESC, wa.created_at DESC';
const [allocations] = await db.query(query, params);
res.json(allocations);
} catch (error) {
console.error('Get work allocations error:', error);
res.status(500).json({ error: 'Internal server error' });
}
});
// Get work allocation by ID
router.get('/:id', authenticateToken, async (req, res) => {
try {
const [allocations] = await db.query(
`SELECT wa.*,
e.name as employee_name, e.username as employee_username,
s.name as supervisor_name,
c.name as contractor_name,
sd.name as sub_department_name,
d.name as department_name
FROM work_allocations wa
JOIN users e ON wa.employee_id = e.id
JOIN users s ON wa.supervisor_id = s.id
JOIN users c ON wa.contractor_id = c.id
LEFT JOIN sub_departments sd ON wa.sub_department_id = sd.id
LEFT JOIN departments d ON e.department_id = d.id
WHERE wa.id = ?`,
[req.params.id]
);
if (allocations.length === 0) {
return res.status(404).json({ error: 'Work allocation not found' });
}
res.json(allocations[0]);
} catch (error) {
console.error('Get work allocation error:', error);
res.status(500).json({ error: 'Internal server error' });
}
});
// Create work allocation (Supervisor or SuperAdmin)
router.post('/', authenticateToken, authorize('Supervisor', 'SuperAdmin'), async (req, res) => {
try {
const { employeeId, contractorId, subDepartmentId, activity, description, assignedDate, rate, units, totalAmount, departmentId } = req.body;
if (!employeeId || !contractorId || !assignedDate) {
return res.status(400).json({ error: 'Missing required fields' });
}
// SuperAdmin can create for any department, Supervisor only for their own
let targetDepartmentId = req.user.role === 'SuperAdmin' ? departmentId : req.user.departmentId;
// Verify employee exists (SuperAdmin can assign any employee, Supervisor only their department)
let employeeQuery = 'SELECT * FROM users WHERE id = ?';
let employeeParams = [employeeId];
if (req.user.role === 'Supervisor') {
employeeQuery += ' AND department_id = ?';
employeeParams.push(req.user.departmentId);
}
const [employees] = await db.query(employeeQuery, employeeParams);
if (employees.length === 0) {
return res.status(403).json({ error: 'Employee not found or not in your department' });
}
// Use provided rate or get contractor's current rate
let finalRate = rate;
if (!finalRate) {
const [rates] = await db.query(
'SELECT rate FROM contractor_rates WHERE contractor_id = ? ORDER BY effective_date DESC LIMIT 1',
[contractorId]
);
finalRate = rates.length > 0 ? rates[0].rate : null;
}
const [result] = await db.query(
`INSERT INTO work_allocations
(employee_id, supervisor_id, contractor_id, sub_department_id, activity, description, assigned_date, rate, units, total_amount)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
[employeeId, req.user.id, contractorId, subDepartmentId || null, activity || null, description || null, assignedDate, finalRate, units || null, totalAmount || null]
);
const [newAllocation] = await db.query(
`SELECT wa.*,
e.name as employee_name, e.username as employee_username,
s.name as supervisor_name,
c.name as contractor_name,
sd.name as sub_department_name,
d.name as department_name
FROM work_allocations wa
JOIN users e ON wa.employee_id = e.id
JOIN users s ON wa.supervisor_id = s.id
JOIN users c ON wa.contractor_id = c.id
LEFT JOIN sub_departments sd ON wa.sub_department_id = sd.id
LEFT JOIN departments d ON e.department_id = d.id
WHERE wa.id = ?`,
[result.insertId]
);
res.status(201).json(newAllocation[0]);
} catch (error) {
console.error('Create work allocation error:', error);
res.status(500).json({ error: 'Internal server error' });
}
});
// Update work allocation status (Supervisor or SuperAdmin)
router.put('/:id/status', authenticateToken, authorize('Supervisor', 'SuperAdmin'), async (req, res) => {
try {
const { status, completionDate } = req.body;
if (!status) {
return res.status(400).json({ error: 'Status required' });
}
// SuperAdmin can update any allocation, Supervisor only their own
let query = 'SELECT * FROM work_allocations WHERE id = ?';
let params = [req.params.id];
if (req.user.role === 'Supervisor') {
query += ' AND supervisor_id = ?';
params.push(req.user.id);
}
const [allocations] = await db.query(query, params);
if (allocations.length === 0) {
return res.status(403).json({ error: 'Work allocation not found or access denied' });
}
await db.query(
'UPDATE work_allocations SET status = ?, completion_date = ? WHERE id = ?',
[status, completionDate || null, req.params.id]
);
const [updatedAllocation] = await db.query(
`SELECT wa.*,
e.name as employee_name, e.username as employee_username,
s.name as supervisor_name,
c.name as contractor_name,
sd.name as sub_department_name,
d.name as department_name
FROM work_allocations wa
JOIN users e ON wa.employee_id = e.id
JOIN users s ON wa.supervisor_id = s.id
JOIN users c ON wa.contractor_id = c.id
LEFT JOIN sub_departments sd ON wa.sub_department_id = sd.id
LEFT JOIN departments d ON e.department_id = d.id
WHERE wa.id = ?`,
[req.params.id]
);
res.json(updatedAllocation[0]);
} catch (error) {
console.error('Update work allocation error:', error);
res.status(500).json({ error: 'Internal server error' });
}
});
// Delete work allocation (Supervisor or SuperAdmin)
router.delete('/:id', authenticateToken, authorize('Supervisor', 'SuperAdmin'), async (req, res) => {
try {
// SuperAdmin can delete any allocation, Supervisor only their own
let query = 'SELECT * FROM work_allocations WHERE id = ?';
let params = [req.params.id];
if (req.user.role === 'Supervisor') {
query += ' AND supervisor_id = ?';
params.push(req.user.id);
}
const [allocations] = await db.query(query, params);
if (allocations.length === 0) {
return res.status(403).json({ error: 'Work allocation not found or access denied' });
}
await db.query('DELETE FROM work_allocations WHERE id = ?', [req.params.id]);
res.json({ message: 'Work allocation deleted successfully' });
} catch (error) {
console.error('Delete work allocation error:', error);
res.status(500).json({ error: 'Internal server error' });
}
});
export default router;