feat(考勤管理): 新增考勤记录查看、人员管理和报表生成功能
添加考勤记录查看页面,支持按日期筛选和展示员工考勤数据 实现人员管理组件,包含添加员工、搜索分页和删除功能 新增考勤报表生成组件,支持多员工筛选和导出CSV
This commit is contained in:
+303
-258
@@ -2,269 +2,314 @@ import express from 'express'
|
||||
import cors from 'cors'
|
||||
import { Parser } from 'json2csv'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import mysql from 'mysql2/promise'
|
||||
import dotenv from 'dotenv'
|
||||
|
||||
const app = express()
|
||||
const port = 3000
|
||||
// Main function to start the server
|
||||
async function startServer() {
|
||||
dotenv.config()
|
||||
|
||||
app.use(cors())
|
||||
app.use(express.json())
|
||||
const app = express()
|
||||
const port = 3000
|
||||
|
||||
// --- In-memory database for MVP ---
|
||||
const users = [
|
||||
{ id: 1, username: 'worker', password: 'password', role: 'worker', fullName: 'John Doe' },
|
||||
{ id: 2, username: 'worker2', password: 'password', role: 'worker', fullName: 'Jane Smith' },
|
||||
{ id: 3, username: 'manager', password: 'password', role: 'manager', fullName: 'Manager Bob' },
|
||||
]
|
||||
|
||||
let qrCodes = [
|
||||
{
|
||||
id: 'FACTORY-MAIN-ENTRANCE',
|
||||
name: 'Factory Main Entrance',
|
||||
isActive: true,
|
||||
createdAt: new Date().toISOString(),
|
||||
},
|
||||
{
|
||||
id: 'WAREHOUSE-SECTION-A',
|
||||
name: 'Warehouse Section A',
|
||||
isActive: true,
|
||||
createdAt: new Date().toISOString(),
|
||||
},
|
||||
{
|
||||
id: 'ASSEMBLY-LINE-1',
|
||||
name: 'Assembly Line 1',
|
||||
isActive: false,
|
||||
createdAt: new Date().toISOString(),
|
||||
},
|
||||
]
|
||||
|
||||
let clockEvents = [
|
||||
// Sample data for testing reports
|
||||
{
|
||||
id: 1,
|
||||
userId: 1,
|
||||
eventType: 'clock_in',
|
||||
timestamp: '2025-06-10T09:00:00.000Z',
|
||||
qrCodeUsedId: 'FACTORY-MAIN-ENTRANCE',
|
||||
qrCodeUsedName: 'Factory Main Entrance',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
userId: 1,
|
||||
eventType: 'clock_out',
|
||||
timestamp: '2025-06-10T17:30:00.000Z',
|
||||
qrCodeUsedId: 'FACTORY-MAIN-ENTRANCE',
|
||||
qrCodeUsedName: 'Factory Main Entrance',
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
userId: 2,
|
||||
eventType: 'clock_in',
|
||||
timestamp: '2025-06-10T09:05:00.000Z',
|
||||
qrCodeUsedId: 'WAREHOUSE-SECTION-A',
|
||||
qrCodeUsedName: 'Warehouse Section A',
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
userId: 2,
|
||||
eventType: 'clock_out',
|
||||
timestamp: '2025-06-10T17:35:00.000Z',
|
||||
qrCodeUsedId: 'WAREHOUSE-SECTION-A',
|
||||
qrCodeUsedName: 'Warehouse Section A',
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
userId: 1,
|
||||
eventType: 'clock_in',
|
||||
timestamp: '2025-06-11T08:58:00.000Z',
|
||||
qrCodeUsedId: 'FACTORY-MAIN-ENTRANCE',
|
||||
qrCodeUsedName: 'Factory Main Entrance',
|
||||
}, // Missing clock out
|
||||
]
|
||||
|
||||
let eventId = clockEvents.length + 1
|
||||
|
||||
// --- Helper Functions ---
|
||||
const calculateHours = (events) => {
|
||||
const userHours = {}
|
||||
const pairedEvents = {}
|
||||
|
||||
// Group events by user
|
||||
events.forEach((event) => {
|
||||
if (!pairedEvents[event.userId]) {
|
||||
pairedEvents[event.userId] = []
|
||||
}
|
||||
pairedEvents[event.userId].push(event)
|
||||
// --- Database Connection ---
|
||||
const db = mysql.createPool({
|
||||
host: process.env.DB_HOST,
|
||||
user: process.env.DB_USER,
|
||||
password: process.env.DB_PASSWORD,
|
||||
database: process.env.DB_NAME,
|
||||
port: process.env.DB_PORT || 3306,
|
||||
waitForConnections: true,
|
||||
connectionLimit: 10,
|
||||
queueLimit: 0,
|
||||
})
|
||||
|
||||
for (const userId in pairedEvents) {
|
||||
const userEvents = pairedEvents[userId].sort(
|
||||
(a, b) => new Date(a.timestamp) - new Date(b.timestamp),
|
||||
)
|
||||
let totalHours = 0
|
||||
let clockInTime = null
|
||||
|
||||
userEvents.forEach((event) => {
|
||||
if (event.eventType === 'clock_in' && !clockInTime) {
|
||||
clockInTime = new Date(event.timestamp)
|
||||
} else if (event.eventType === 'clock_out' && clockInTime) {
|
||||
const clockOutTime = new Date(event.timestamp)
|
||||
const diffMs = clockOutTime - clockInTime
|
||||
totalHours += diffMs / (1000 * 60 * 60)
|
||||
clockInTime = null // Reset for next pair
|
||||
}
|
||||
})
|
||||
|
||||
const worker = users.find((u) => u.id === parseInt(userId))
|
||||
userHours[userId] = {
|
||||
userId,
|
||||
fullName: worker ? worker.fullName : `User ${userId}`,
|
||||
totalHours: parseFloat(totalHours.toFixed(2)),
|
||||
hasIncomplete: clockInTime !== null, // Mark if there's a pending clock-in
|
||||
}
|
||||
try {
|
||||
const connection = await db.getConnection()
|
||||
console.log('Database connected successfully!')
|
||||
connection.release()
|
||||
} catch (error) {
|
||||
console.error('!!! DATABASE CONNECTION FAILED !!!')
|
||||
console.error('Error:', error.message)
|
||||
process.exit(1)
|
||||
}
|
||||
return Object.values(userHours)
|
||||
|
||||
app.use(cors())
|
||||
app.use(express.json())
|
||||
|
||||
// Helper functions can be placed here if any are needed in the future
|
||||
|
||||
// --- API Endpoints ---
|
||||
|
||||
// Auth Endpoint
|
||||
app.post('/api/auth/login', async (req, res) => {
|
||||
try {
|
||||
const { username, password } = req.body
|
||||
// In a real app, use a secure hashing library like bcrypt to compare passwords
|
||||
const [rows] = await db.execute(
|
||||
'SELECT id, role FROM workers WHERE username = ? AND password_hash = ?',
|
||||
[username, password],
|
||||
)
|
||||
if (rows.length > 0) {
|
||||
const user = rows[0]
|
||||
res.json({ message: 'Login successful', role: user.role, userId: user.id })
|
||||
} else {
|
||||
res.status(401).json({ message: 'Invalid credentials' })
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Login error:', error)
|
||||
res.status(500).json({ message: 'Database error during login.' })
|
||||
}
|
||||
})
|
||||
|
||||
// Worker Clock In/Out Endpoint
|
||||
app.post('/api/clock', async (req, res) => {
|
||||
try {
|
||||
const { userId, eventType, qrCodeValue, latitude, longitude } = req.body
|
||||
const [qrRows] = await db.execute('SELECT name, is_active FROM qr_codes WHERE id = ?', [
|
||||
qrCodeValue,
|
||||
])
|
||||
if (qrRows.length === 0 || !qrRows[0].is_active) {
|
||||
return res.status(400).json({ message: 'Invalid or inactive QR Code.' })
|
||||
}
|
||||
const [lastEventRows] = await db.execute(
|
||||
'SELECT event_type FROM clock_records WHERE worker_id = ? ORDER BY timestamp DESC LIMIT 1',
|
||||
[userId],
|
||||
)
|
||||
if (lastEventRows.length > 0 && lastEventRows[0].event_type === eventType) {
|
||||
return res
|
||||
.status(400)
|
||||
.json({ message: `You are already clocked ${eventType === 'clock_in' ? 'in' : 'out'}.` })
|
||||
}
|
||||
const timestamp = new Date()
|
||||
await db.execute(
|
||||
'INSERT INTO clock_records (worker_id, event_type, timestamp, qr_code_id, latitude, longitude) VALUES (?, ?, ?, ?, ?, ?)',
|
||||
[userId, eventType, timestamp, qrCodeValue, latitude, longitude],
|
||||
)
|
||||
res.status(201).json({ message: 'Clock event recorded successfully' })
|
||||
} catch (error) {
|
||||
console.error('Clock event error:', error)
|
||||
res.status(500).json({ message: 'Database error during clock event.' })
|
||||
}
|
||||
})
|
||||
|
||||
// Worker Status Endpoint
|
||||
app.get('/api/worker/status/:userId', async (req, res) => {
|
||||
try {
|
||||
const { userId } = req.params
|
||||
const [rows] = await db.execute(
|
||||
'SELECT event_type FROM clock_records WHERE worker_id = ? ORDER BY timestamp DESC LIMIT 1',
|
||||
[userId],
|
||||
)
|
||||
if (rows.length > 0) {
|
||||
res.json({ eventType: rows[0].event_type })
|
||||
} else {
|
||||
res.json({ eventType: 'clock_out' })
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Worker status error:', error)
|
||||
res.status(500).json({ message: 'Database error fetching status.' })
|
||||
}
|
||||
})
|
||||
|
||||
// Worker History Endpoint
|
||||
app.get('/api/worker/clock-history/:userId', async (req, res) => {
|
||||
try {
|
||||
const { userId } = req.params
|
||||
const [rows] = await db.execute(
|
||||
`SELECT cr.id, cr.event_type, cr.timestamp, qc.name as qrCodeUsedName, cr.latitude, cr.longitude FROM clock_records cr JOIN qr_codes qc ON cr.qr_code_id = qc.id WHERE cr.worker_id = ? ORDER BY cr.timestamp DESC`,
|
||||
[userId],
|
||||
)
|
||||
res.json(rows)
|
||||
} catch (error) {
|
||||
console.error('Worker history error:', error)
|
||||
res.status(500).json({ message: 'Database error fetching history.' })
|
||||
}
|
||||
})
|
||||
|
||||
// Manager: GET All Workers with Search and Pagination
|
||||
app.get('/api/managers/workers', async (req, res) => {
|
||||
try {
|
||||
const { search = '', page = 1, limit = 20 } = req.query
|
||||
const offset = (parseInt(page) - 1) * parseInt(limit)
|
||||
const searchTerm = `%${search}%`
|
||||
const [workers] = await db.execute(
|
||||
`SELECT id, username, full_name, created_at FROM workers WHERE role = 'worker' AND (full_name LIKE ? OR username LIKE ?) ORDER BY created_at DESC LIMIT ? OFFSET ?`,
|
||||
[searchTerm, searchTerm, parseInt(limit), offset],
|
||||
)
|
||||
const [[{ totalCount }]] = await db.execute(
|
||||
`SELECT COUNT(*) as totalCount FROM workers WHERE role = 'worker' AND (full_name LIKE ? OR username LIKE ?)`,
|
||||
[searchTerm, searchTerm],
|
||||
)
|
||||
res.json({ workers, totalCount })
|
||||
} catch (error) {
|
||||
console.error('Get workers error:', error)
|
||||
res.status(500).json({ message: 'Database error fetching workers.' })
|
||||
}
|
||||
})
|
||||
|
||||
// Manager: POST (Add new) Worker
|
||||
app.post('/api/managers/workers', async (req, res) => {
|
||||
try {
|
||||
const { username, password, fullName } = req.body
|
||||
if (!username || !password || !fullName) {
|
||||
return res.status(400).json({ message: 'Username, password, and full name are required.' })
|
||||
}
|
||||
const [result] = await db.execute(
|
||||
"INSERT INTO workers (username, password_hash, full_name, role) VALUES (?, ?, ?, 'worker')",
|
||||
[username, password, fullName],
|
||||
)
|
||||
res.status(201).json({ id: result.insertId, username, fullName })
|
||||
} catch (error) {
|
||||
if (error.code === 'ER_DUP_ENTRY') {
|
||||
return res.status(409).json({ message: 'Username already exists.' })
|
||||
}
|
||||
console.error('Add worker error:', error)
|
||||
res.status(500).json({ message: 'Database error adding worker.' })
|
||||
}
|
||||
})
|
||||
|
||||
// Manager: DELETE Worker
|
||||
app.delete('/api/managers/workers/:id', async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params
|
||||
const [result] = await db.execute("DELETE FROM workers WHERE id = ? AND role = 'worker'", [
|
||||
id,
|
||||
])
|
||||
if (result.affectedRows === 0) {
|
||||
return res.status(404).json({ message: 'Worker not found or user is not a worker.' })
|
||||
}
|
||||
res.status(204).send()
|
||||
} catch (error) {
|
||||
console.error('Delete worker error:', error)
|
||||
res.status(500).json({ message: 'Database error deleting worker.' })
|
||||
}
|
||||
})
|
||||
|
||||
// Manager: GET Attendance Records
|
||||
app.get('/api/managers/attendance-records', async (req, res) => {
|
||||
try {
|
||||
const { workerIds, startDate, endDate, format } = req.query
|
||||
if (!workerIds) {
|
||||
return res.status(400).json({ message: 'Worker IDs are required.' })
|
||||
}
|
||||
const idsArray = workerIds.split(',').map(Number)
|
||||
if (idsArray.length === 0) return res.json([])
|
||||
const placeholders = idsArray.map(() => '?').join(',')
|
||||
let query = `SELECT cr.id, w.full_name, cr.event_type, cr.timestamp, qc.name as qrCodeUsedName, cr.latitude, cr.longitude FROM clock_records cr JOIN qr_codes qc ON cr.qr_code_id = qc.id JOIN workers w ON cr.worker_id = w.id WHERE cr.worker_id IN (${placeholders})`
|
||||
const params = [...idsArray]
|
||||
if (startDate && endDate) {
|
||||
const endOfDay = new Date(endDate)
|
||||
endOfDay.setHours(23, 59, 59, 999)
|
||||
query += ' AND cr.timestamp BETWEEN ? AND ?'
|
||||
params.push(startDate, endOfDay)
|
||||
}
|
||||
query += ' ORDER BY w.full_name, cr.timestamp DESC'
|
||||
const [rows] = await db.execute(query, params)
|
||||
if (format === 'csv') {
|
||||
const json2csvParser = new Parser({
|
||||
fields: ['full_name', 'event_type', 'timestamp', 'qrCodeUsedName'],
|
||||
})
|
||||
const csv = json2csvParser.parse(rows)
|
||||
res.header('Content-Type', 'text/csv')
|
||||
res.attachment(`attendance-report-${new Date().toISOString().split('T')[0]}.csv`)
|
||||
return res.send(csv)
|
||||
}
|
||||
res.json(rows)
|
||||
} catch (error) {
|
||||
console.error('Attendance records error:', error)
|
||||
res.status(500).json({ message: 'Database error fetching attendance records.' })
|
||||
}
|
||||
})
|
||||
|
||||
// Manager: GET QR Codes
|
||||
app.get('/api/managers/qr-codes', async (req, res) => {
|
||||
try {
|
||||
const [rows] = await db.execute(
|
||||
'SELECT id, name, is_active, created_at FROM qr_codes ORDER BY created_at DESC',
|
||||
)
|
||||
res.json(rows)
|
||||
} catch (error) {
|
||||
console.error('Get QR codes error:', error)
|
||||
res.status(500).json({ message: 'Database error fetching QR codes.' })
|
||||
}
|
||||
})
|
||||
|
||||
// Manager: POST QR Code
|
||||
app.post('/api/managers/qr-codes', async (req, res) => {
|
||||
try {
|
||||
const { name } = req.body
|
||||
if (!name) return res.status(400).json({ message: 'QR Code name is required.' })
|
||||
const newQrCode = { id: uuidv4(), name, isActive: true }
|
||||
await db.execute('INSERT INTO qr_codes (id, name, is_active) VALUES (?, ?, ?)', [
|
||||
newQrCode.id,
|
||||
newQrCode.name,
|
||||
newQrCode.isActive,
|
||||
])
|
||||
res
|
||||
.status(201)
|
||||
.json({ id: newQrCode.id, name: newQrCode.name, is_active: newQrCode.isActive })
|
||||
} catch (error) {
|
||||
console.error('Add QR code error:', error)
|
||||
res.status(500).json({ message: 'Database error adding QR code.' })
|
||||
}
|
||||
})
|
||||
|
||||
// Manager: PUT QR Code
|
||||
app.put('/api/managers/qr-codes/:id', async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params
|
||||
const { isActive } = req.body
|
||||
if (typeof isActive !== 'boolean')
|
||||
return res.status(400).json({ message: 'isActive must be a boolean.' })
|
||||
const [result] = await db.execute('UPDATE qr_codes SET is_active = ? WHERE id = ?', [
|
||||
isActive,
|
||||
id,
|
||||
])
|
||||
if (result.affectedRows === 0) return res.status(404).json({ message: 'QR Code not found.' })
|
||||
res.json({ id, isActive })
|
||||
} catch (error) {
|
||||
console.error('Update QR code error:', error)
|
||||
res.status(500).json({ message: 'Database error updating QR code.' })
|
||||
}
|
||||
})
|
||||
|
||||
// Manager: DELETE QR Code
|
||||
app.delete('/api/managers/qr-codes/:id', async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params
|
||||
const [result] = await db.execute('DELETE FROM qr_codes WHERE id = ?', [id])
|
||||
if (result.affectedRows === 0) return res.status(404).json({ message: 'QR Code not found.' })
|
||||
res.status(204).send()
|
||||
} catch (error) {
|
||||
console.error('Delete QR code error:', error)
|
||||
res.status(500).json({ message: 'Database error deleting QR code.' })
|
||||
}
|
||||
})
|
||||
|
||||
// Manager: GET single worker's details
|
||||
app.get('/api/managers/worker/:id', async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params
|
||||
const [rows] = await db.execute(
|
||||
"SELECT full_name FROM workers WHERE id = ? AND role = 'worker'",
|
||||
[id],
|
||||
)
|
||||
if (rows.length > 0) {
|
||||
res.json(rows[0])
|
||||
} else {
|
||||
res.status(404).json({ message: 'Worker not found.' })
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Get single worker error:', error)
|
||||
res.status(500).json({ message: 'Database error fetching worker details.' })
|
||||
}
|
||||
})
|
||||
|
||||
// --- Server Start ---
|
||||
app.listen(port, () => {
|
||||
console.log(`Server is running on http://localhost:${port}`)
|
||||
})
|
||||
}
|
||||
|
||||
// --- API Endpoints ---
|
||||
|
||||
// Auth Endpoint
|
||||
app.post('/api/auth/login', (req, res) => {
|
||||
const { username, password } = req.body
|
||||
const user = users.find((u) => u.username === username && u.password === password)
|
||||
if (user) {
|
||||
res.json({ message: 'Login successful', role: user.role, userId: user.id })
|
||||
} else {
|
||||
res.status(401).json({ message: 'Invalid credentials' })
|
||||
}
|
||||
})
|
||||
|
||||
// Worker Clock In/Out Endpoint
|
||||
app.post('/api/clock', (req, res) => {
|
||||
const { userId, eventType, qrCodeValue, latitude, longitude } = req.body
|
||||
const validQrCode = qrCodes.find((qr) => qr.id === qrCodeValue && qr.isActive)
|
||||
if (!validQrCode) return res.status(400).json({ message: 'Invalid or inactive QR Code.' })
|
||||
|
||||
const lastEvent = clockEvents.filter((e) => e.userId === userId).pop()
|
||||
if (lastEvent && lastEvent.eventType === eventType)
|
||||
return res
|
||||
.status(400)
|
||||
.json({ message: `You are already clocked ${eventType === 'clock_in' ? 'in' : 'out'}.` })
|
||||
|
||||
const newEvent = {
|
||||
id: eventId++,
|
||||
userId,
|
||||
eventType,
|
||||
timestamp: new Date().toISOString(),
|
||||
qrCodeUsedId: qrCodeValue,
|
||||
qrCodeUsedName: validQrCode.name, // Add human-readable name
|
||||
latitude,
|
||||
longitude,
|
||||
}
|
||||
clockEvents.push(newEvent)
|
||||
res.status(201).json(newEvent)
|
||||
})
|
||||
|
||||
// Worker Status Endpoint
|
||||
app.get('/api/worker/status/:userId', (req, res) => {
|
||||
const userId = parseInt(req.params.userId, 10)
|
||||
const lastEvent = clockEvents.filter((event) => event.userId === userId).pop()
|
||||
if (lastEvent) {
|
||||
res.json(lastEvent)
|
||||
} else {
|
||||
res.json({ eventType: 'clock_out' })
|
||||
}
|
||||
})
|
||||
|
||||
// Worker History Endpoint
|
||||
app.get('/api/worker/clock-history/:userId', (req, res) => {
|
||||
const userId = parseInt(req.params.userId, 10)
|
||||
const userEvents = clockEvents.filter((event) => event.userId === userId)
|
||||
res.json(userEvents)
|
||||
})
|
||||
|
||||
// --- Manager Endpoints ---
|
||||
|
||||
// Reporting Endpoint
|
||||
app.get('/api/managers/hours-report', (req, res) => {
|
||||
const { startDate, endDate, format } = req.query
|
||||
|
||||
let filteredEvents = clockEvents
|
||||
if (startDate && endDate) {
|
||||
const endOfDay = new Date(endDate)
|
||||
endOfDay.setHours(23, 59, 59, 999) // Include the whole end day
|
||||
filteredEvents = clockEvents.filter((event) => {
|
||||
const eventDate = new Date(event.timestamp)
|
||||
return eventDate >= new Date(startDate) && eventDate <= endOfDay
|
||||
})
|
||||
}
|
||||
|
||||
const reportData = calculateHours(filteredEvents)
|
||||
|
||||
if (format === 'csv') {
|
||||
const json2csvParser = new Parser({
|
||||
fields: ['userId', 'fullName', 'totalHours', 'hasIncomplete'],
|
||||
})
|
||||
const csv = json2csvParser.parse(reportData)
|
||||
res.header('Content-Type', 'text/csv')
|
||||
res.attachment(`hours-report-${new Date().toISOString().split('T')[0]}.csv`)
|
||||
return res.send(csv)
|
||||
}
|
||||
|
||||
res.json(reportData)
|
||||
})
|
||||
|
||||
// GET QR Codes Endpoint
|
||||
app.get('/api/managers/qr-codes', (req, res) => {
|
||||
res.json(qrCodes.sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt)))
|
||||
})
|
||||
|
||||
// POST (Add new) QR Code Endpoint
|
||||
app.post('/api/managers/qr-codes', (req, res) => {
|
||||
const { name } = req.body
|
||||
if (!name) {
|
||||
return res.status(400).json({ message: 'QR Code name is required.' })
|
||||
}
|
||||
const newQrCode = {
|
||||
id: uuidv4(),
|
||||
name,
|
||||
isActive: true,
|
||||
createdAt: new Date().toISOString(),
|
||||
}
|
||||
qrCodes.push(newQrCode)
|
||||
res.status(201).json(newQrCode)
|
||||
})
|
||||
|
||||
// PUT (Update) QR Code Endpoint
|
||||
app.put('/api/managers/qr-codes/:id', (req, res) => {
|
||||
const { id } = req.params
|
||||
const { isActive } = req.body
|
||||
const qrCodeIndex = qrCodes.findIndex((qr) => qr.id === id)
|
||||
|
||||
if (qrCodeIndex === -1) {
|
||||
return res.status(404).json({ message: 'QR Code not found.' })
|
||||
}
|
||||
if (typeof isActive !== 'boolean') {
|
||||
return res.status(400).json({ message: 'isActive must be a boolean.' })
|
||||
}
|
||||
|
||||
qrCodes[qrCodeIndex].isActive = isActive
|
||||
res.json(qrCodes[qrCodeIndex])
|
||||
})
|
||||
|
||||
// DELETE QR Code Endpoint
|
||||
app.delete('/api/managers/qr-codes/:id', (req, res) => {
|
||||
const { id } = req.params
|
||||
const initialLength = qrCodes.length
|
||||
qrCodes = qrCodes.filter((qr) => qr.id !== id)
|
||||
|
||||
if (qrCodes.length === initialLength) {
|
||||
return res.status(404).json({ message: 'QR Code not found.' })
|
||||
}
|
||||
|
||||
res.status(204).send()
|
||||
})
|
||||
|
||||
// --- Server Start ---
|
||||
app.listen(port, () => {
|
||||
console.log(`Server is running on http://localhost:${port}`)
|
||||
})
|
||||
startServer()
|
||||
|
||||
Reference in New Issue
Block a user