mirror of
https://github.com/Alvin-Zilverstand/Challenge_15_Magazijn_App_Maken.git
synced 2026-03-06 11:06:34 +01:00
Add return pending status and related functionality for reservations
This commit is contained in:
@@ -19,7 +19,7 @@ const reservationSchema = new mongoose.Schema({
|
|||||||
},
|
},
|
||||||
status: {
|
status: {
|
||||||
type: String,
|
type: String,
|
||||||
enum: ['PENDING', 'APPROVED', 'REJECTED', 'RETURNED', 'ARCHIVED'],
|
enum: ['PENDING', 'APPROVED', 'REJECTED', 'RETURN_PENDING', 'RETURNED', 'ARCHIVED'],
|
||||||
default: 'PENDING'
|
default: 'PENDING'
|
||||||
},
|
},
|
||||||
reservedDate: {
|
reservedDate: {
|
||||||
|
|||||||
@@ -61,6 +61,7 @@
|
|||||||
<option value="PENDING">Pending</option>
|
<option value="PENDING">Pending</option>
|
||||||
<option value="APPROVED">Approved</option>
|
<option value="APPROVED">Approved</option>
|
||||||
<option value="REJECTED">Rejected</option>
|
<option value="REJECTED">Rejected</option>
|
||||||
|
<option value="RETURN_PENDING">Return Pending</option>
|
||||||
<option value="RETURNED">Returned</option>
|
<option value="RETURNED">Returned</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -299,6 +299,12 @@ body {
|
|||||||
border: 1px solid var(--vista-grey);
|
border: 1px solid var(--vista-grey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.reservation-return_pending {
|
||||||
|
background-color: #ffc107 !important;
|
||||||
|
color: var(--vista-blue) !important;
|
||||||
|
border: 1px solid var(--vista-peach);
|
||||||
|
}
|
||||||
|
|
||||||
.reservation-returned {
|
.reservation-returned {
|
||||||
background-color: var(--vista-peach) !important;
|
background-color: var(--vista-peach) !important;
|
||||||
color: var(--vista-white) !important;
|
color: var(--vista-white) !important;
|
||||||
|
|||||||
@@ -75,10 +75,15 @@ function filterAndDisplayReservations() {
|
|||||||
<button class="btn btn-warning" onclick="updateReservation('${reservation._id}', 'REJECTED')" title="Reject">
|
<button class="btn btn-warning" onclick="updateReservation('${reservation._id}', 'REJECTED')" title="Reject">
|
||||||
<i class="bi bi-x-lg"></i><span class="btn-text"> Reject</span>
|
<i class="bi bi-x-lg"></i><span class="btn-text"> Reject</span>
|
||||||
</button>
|
</button>
|
||||||
` : reservation.status === 'APPROVED' ? `
|
` : reservation.status === 'RETURN_PENDING' ? `
|
||||||
<button class="btn btn-info" onclick="updateReservation('${reservation._id}', 'RETURNED')" title="Mark as Returned">
|
<button class="btn btn-success" onclick="updateReservation('${reservation._id}', 'RETURNED')" title="Approve Return">
|
||||||
<i class="bi bi-arrow-return-left"></i><span class="btn-text"> Return</span>
|
<i class="bi bi-check-lg"></i><span class="btn-text"> Approve Return</span>
|
||||||
</button>
|
</button>
|
||||||
|
<button class="btn btn-warning" onclick="updateReservation('${reservation._id}', 'APPROVED')" title="Reject Return">
|
||||||
|
<i class="bi bi-x-lg"></i><span class="btn-text"> Reject Return</span>
|
||||||
|
</button>
|
||||||
|
` : reservation.status === 'APPROVED' ? `
|
||||||
|
<span class="text-info"><i class="bi bi-check-circle"></i><span class="btn-text"> Item Loaned</span></span>
|
||||||
` : reservation.status === 'RETURNED' ? `
|
` : reservation.status === 'RETURNED' ? `
|
||||||
<button class="btn btn-secondary" onclick="archiveReservation('${reservation._id}')" title="Archive Reservation">
|
<button class="btn btn-secondary" onclick="archiveReservation('${reservation._id}')" title="Archive Reservation">
|
||||||
<i class="bi bi-archive"></i><span class="btn-text"> Archive</span>
|
<i class="bi bi-archive"></i><span class="btn-text"> Archive</span>
|
||||||
|
|||||||
@@ -87,11 +87,13 @@ function filterAndDisplayReservations() {
|
|||||||
<i class="bi bi-x-circle"></i> Cancel
|
<i class="bi bi-x-circle"></i> Cancel
|
||||||
</button>
|
</button>
|
||||||
` : reservation.status === 'APPROVED' ? `
|
` : reservation.status === 'APPROVED' ? `
|
||||||
<button class="btn btn-sm btn-info" onclick="returnReservation('${reservation._id}')" title="Return Item">
|
<button class="btn btn-sm btn-info" onclick="returnReservation('${reservation._id}')" title="Request Return">
|
||||||
<i class="bi bi-arrow-return-left"></i> Return
|
<i class="bi bi-arrow-return-left"></i> Request Return
|
||||||
</button>
|
</button>
|
||||||
` : reservation.status === 'REJECTED' ? `
|
` : reservation.status === 'REJECTED' ? `
|
||||||
<span class="text-muted"><i class="bi bi-x-circle"></i> Rejected</span>
|
<span class="text-muted"><i class="bi bi-x-circle"></i> Rejected</span>
|
||||||
|
` : reservation.status === 'RETURN_PENDING' ? `
|
||||||
|
<span class="text-warning"><i class="bi bi-clock"></i> Return Pending Approval</span>
|
||||||
` : reservation.status === 'RETURNED' ? `
|
` : reservation.status === 'RETURNED' ? `
|
||||||
<span class="text-success"><i class="bi bi-check-circle"></i> Returned</span>
|
<span class="text-success"><i class="bi bi-check-circle"></i> Returned</span>
|
||||||
` : ''}
|
` : ''}
|
||||||
@@ -126,7 +128,7 @@ async function deleteReservation(reservationId) {
|
|||||||
|
|
||||||
// Return reservation (mark as returned)
|
// Return reservation (mark as returned)
|
||||||
async function returnReservation(reservationId) {
|
async function returnReservation(reservationId) {
|
||||||
if (!confirm('Are you sure you want to return this item?')) return;
|
if (!confirm('Are you sure you want to request return for this item? An admin will need to approve the return.')) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`/api/reservations/${reservationId}`, {
|
const response = await fetch(`/api/reservations/${reservationId}`, {
|
||||||
@@ -135,29 +137,29 @@ async function returnReservation(reservationId) {
|
|||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
'Authorization': `Bearer ${localStorage.getItem('token')}`
|
'Authorization': `Bearer ${localStorage.getItem('token')}`
|
||||||
},
|
},
|
||||||
body: JSON.stringify({ status: 'RETURNED' })
|
body: JSON.stringify({ status: 'RETURN_PENDING' })
|
||||||
});
|
});
|
||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
// Successfully returned, reload reservations
|
// Successfully requested return, reload reservations
|
||||||
await loadReservations();
|
await loadReservations();
|
||||||
// Show success message
|
// Show success message
|
||||||
const alert = document.createElement('div');
|
const alert = document.createElement('div');
|
||||||
alert.className = 'alert alert-success alert-dismissible fade show';
|
alert.className = 'alert alert-success alert-dismissible fade show';
|
||||||
alert.innerHTML = `
|
alert.innerHTML = `
|
||||||
Item returned successfully!
|
Return requested successfully! An admin will review your request.
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||||
`;
|
`;
|
||||||
const container = document.querySelector('.container');
|
const container = document.querySelector('.container');
|
||||||
container.prepend(alert);
|
container.prepend(alert);
|
||||||
setTimeout(() => alert.remove(), 3000);
|
setTimeout(() => alert.remove(), 5000);
|
||||||
} else {
|
} else {
|
||||||
const error = await response.json();
|
const error = await response.json();
|
||||||
throw new Error(error.message || 'Failed to return item');
|
throw new Error(error.message || 'Failed to request return');
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error returning item:', error);
|
console.error('Error requesting return:', error);
|
||||||
alert(`Failed to return item: ${error.message}`);
|
alert(`Failed to request return: ${error.message}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -58,6 +58,7 @@
|
|||||||
<option value="PENDING">Pending</option>
|
<option value="PENDING">Pending</option>
|
||||||
<option value="APPROVED">Approved</option>
|
<option value="APPROVED">Approved</option>
|
||||||
<option value="REJECTED">Rejected</option>
|
<option value="REJECTED">Rejected</option>
|
||||||
|
<option value="RETURN_PENDING">Return Pending</option>
|
||||||
<option value="RETURNED">Returned</option>
|
<option value="RETURNED">Returned</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -108,18 +108,18 @@ router.patch('/:id', auth, async (req, res) => {
|
|||||||
// Check authorization
|
// Check authorization
|
||||||
const isAdmin = req.user.role === 'admin';
|
const isAdmin = req.user.role === 'admin';
|
||||||
const isOwner = reservation.userId._id.toString() === req.user._id.toString();
|
const isOwner = reservation.userId._id.toString() === req.user._id.toString();
|
||||||
const isReturning = req.body.status === 'RETURNED';
|
const isReturning = req.body.status === 'RETURN_PENDING';
|
||||||
|
|
||||||
if (!isAdmin && (!isOwner || !isReturning)) {
|
if (!isAdmin && (!isOwner || !isReturning)) {
|
||||||
return res.status(403).json({
|
return res.status(403).json({
|
||||||
message: 'Not authorized. Students can only return their own items.'
|
message: 'Not authorized. Students can only request return of their own items.'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Additional validation for students
|
// Additional validation for students
|
||||||
if (!isAdmin && isReturning && reservation.status !== 'APPROVED') {
|
if (!isAdmin && isReturning && reservation.status !== 'APPROVED') {
|
||||||
return res.status(400).json({
|
return res.status(400).json({
|
||||||
message: 'Can only return approved items'
|
message: 'Can only request return for approved items'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,7 +143,8 @@ router.patch('/:id', auth, async (req, res) => {
|
|||||||
if (oldStatus === 'PENDING' && newStatus === 'REJECTED') {
|
if (oldStatus === 'PENDING' && newStatus === 'REJECTED') {
|
||||||
item.reserved = Math.max(0, item.reserved - (reservation.quantity || 1));
|
item.reserved = Math.max(0, item.reserved - (reservation.quantity || 1));
|
||||||
await item.save();
|
await item.save();
|
||||||
} else if (oldStatus === 'APPROVED' && newStatus === 'RETURNED') {
|
} else if (oldStatus === 'RETURN_PENDING' && newStatus === 'RETURNED') {
|
||||||
|
// Admin approved the return
|
||||||
item.reserved = Math.max(0, item.reserved - (reservation.quantity || 1));
|
item.reserved = Math.max(0, item.reserved - (reservation.quantity || 1));
|
||||||
await item.save();
|
await item.save();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user