This commit is contained in:
2025-10-08 19:48:48 +02:00
parent cdcd34fa0f
commit 76e3053e50
14 changed files with 587 additions and 52 deletions
+131 -9
View File
@@ -283,6 +283,19 @@
💾
</button>
</div>
<div class="setting-group">
<label for="pickup_confirmation_email_text">Ophaalbevestiging E-mail Tekst</label>
<textarea
id="pickup_confirmation_email_text"
v-model="settings.pickup_confirmation_email_text"
rows="8"
placeholder="Standaard tekst voor ophaalbevestiging e-mail"
></textarea>
<button @click="updateSetting('pickup_confirmation_email_text', settings.pickup_confirmation_email_text)" class="save-btn" title="Opslaan">
💾
</button>
</div>
</div>
</div>
@@ -321,25 +334,34 @@
<strong>Gewenste Datum/Tijd:</strong> {{ formatDateTime(selectedRequest.requested_date, selectedRequest.requested_time) }}
</div>
<div class="detail-row">
<strong>Status:</strong>
<strong>Status:</strong>
<select v-model="selectedRequest.status" @change="updateRequestStatus">
<option value="pending">In afwachting</option>
<option value="confirmed">Bevestigd</option>
<option value="mail_sent">Bevestigd mail verstuurd</option>
<option value="completed">Voltooid</option>
<option value="cancelled">Geannuleerd</option>
</select>
</div>
<div class="detail-row">
<strong>Werkelijke Reparatie Datum:</strong>
<strong>Werkelijke ophaalDatum:</strong>
<input type="date" v-model="selectedRequest.actual_repair_date" @change="updateRequestDate" />
</div>
<div class="detail-row">
<strong>Werkelijke Reparatie Tijd:</strong>
<strong>Werkelijke ophaaltijd:</strong>
<input type="time" v-model="selectedRequest.actual_repair_time" @change="updateRequestTime" />
</div>
</div>
</div>
<div class="modal-footer">
<button
@click="sendPickupConfirmation"
class="btn-pickup-confirmation"
:disabled="!selectedRequest?.email || !selectedRequest?.actual_repair_date || !selectedRequest?.actual_repair_time || pickupConfirmationLoading"
title="Verstuur ophaalbevestiging e-mail naar klant"
>
{{ pickupConfirmationLoading ? '📧 Versturen...' : '📧 Ophaalbevestiging Versturen' }}
</button>
<button @click="closeRequestModal" class="btn-primary">Sluiten</button>
</div>
</div>
@@ -522,6 +544,7 @@ export default {
// Loading states
const emailLoading = ref(false)
const smsLoading = ref(false)
const pickupConfirmationLoading = ref(false)
const tabs = [
{ id: 'requests', label: 'Service Aanvragen' },
@@ -542,6 +565,7 @@ export default {
const statusMap = {
'pending': 'In afwachting',
'confirmed': 'Bevestigd',
'mail_sent': 'Bevestigd mail verstuurd',
'completed': 'Voltooid',
'cancelled': 'Geannuleerd'
}
@@ -658,7 +682,7 @@ export default {
const viewRequest = async (request) => {
requestModalLoading.value = true
showRequestModal.value = true
// Get the latest data from the database to ensure we have the most current information
try {
const response = await fetch(`/api/service-requests/${request.id}`, {
@@ -666,16 +690,29 @@ export default {
})
if (response.ok) {
const latestRequest = await response.json()
// Format dates for HTML date inputs (YYYY-MM-DD)
if (latestRequest.actual_repair_date) {
// Extract just the date part if it's a datetime string
latestRequest.actual_repair_date = latestRequest.actual_repair_date.split('T')[0]
}
selectedRequest.value = { ...latestRequest }
} else {
// Fallback to local data if API call fails
const latestRequest = requests.value.find(r => r.id === request.id)
if (latestRequest && latestRequest.actual_repair_date) {
latestRequest.actual_repair_date = latestRequest.actual_repair_date.split('T')[0]
}
selectedRequest.value = latestRequest ? { ...latestRequest } : { ...request }
}
} catch (error) {
console.error('Failed to fetch latest request data:', error)
// Fallback to local data if API call fails
const latestRequest = requests.value.find(r => r.id === request.id)
if (latestRequest && latestRequest.actual_repair_date) {
latestRequest.actual_repair_date = latestRequest.actual_repair_date.split('T')[0]
}
selectedRequest.value = latestRequest ? { ...latestRequest } : { ...request }
} finally {
requestModalLoading.value = false
@@ -764,7 +801,7 @@ export default {
const submitSms = async () => {
if (!smsForm.value.message.trim()) return
smsLoading.value = true
try {
const response = await fetch(`/api/service-requests/${selectedRequest.value.id}/send-sms`, {
@@ -772,7 +809,7 @@ export default {
headers: authStore.getAuthHeaders(),
body: JSON.stringify({ message: smsForm.value.message })
})
if (response.ok) {
closeSmsModal()
await fetchRequests() // Refresh to show new communication
@@ -783,6 +820,51 @@ export default {
smsLoading.value = false
}
}
const sendPickupConfirmation = async () => {
if (!selectedRequest.value?.email) {
alert('Geen e-mailadres beschikbaar voor deze klant')
return
}
if (!selectedRequest.value?.actual_repair_date || !selectedRequest.value?.actual_repair_time) {
alert('Vul eerst de werkelijke ophaaldatum en tijd in')
return
}
if (!confirm('Weet u zeker dat u de ophaalbevestiging wilt versturen naar ' + selectedRequest.value.email + '?')) {
return
}
pickupConfirmationLoading.value = true
try {
const response = await fetch(`/api/service-requests/${selectedRequest.value.id}/send-pickup-confirmation`, {
method: 'POST',
headers: authStore.getAuthHeaders()
})
if (response.ok) {
const data = await response.json()
alert('Ophaalbevestiging succesvol verzonden!')
// Update the selected request with the new status
if (data.service_request) {
selectedRequest.value = data.service_request
}
// Refresh the requests list
await fetchRequests()
} else {
const error = await response.json()
alert('Fout bij versturen: ' + (error.message || 'Onbekende fout'))
}
} catch (error) {
console.error('Failed to send pickup confirmation:', error)
alert('Fout bij versturen van ophaalbevestiging')
} finally {
pickupConfirmationLoading.value = false
}
}
const editBlockedDate = (date) => {
editingBlockedDate.value = date
@@ -1045,6 +1127,7 @@ export default {
communications,
emailLoading,
smsLoading,
pickupConfirmationLoading,
minDate,
getStatusText,
getCommTypeText,
@@ -1063,6 +1146,7 @@ export default {
viewCommunications,
submitEmail,
submitSms,
sendPickupConfirmation,
editBlockedDate,
saveBlockedDate,
deleteBlockedDate,
@@ -1311,6 +1395,12 @@ export default {
color: #0c5460;
}
.status-badge.mail_sent {
background: #e7d4c5;
color: #6d5637;
font-weight: bold;
}
.status-badge.completed {
background: #d4edda;
color: #155724;
@@ -1604,16 +1694,24 @@ export default {
color: #2c3e50;
}
.setting-group input {
.setting-group input,
.setting-group textarea {
padding: 0.75rem;
border: 2px solid #e1e8ed;
border-radius: 8px;
font-size: 1rem;
font-family: inherit;
}
.setting-group input:focus {
.setting-group input:focus,
.setting-group textarea:focus {
outline: none;
border-color: #3498db;
border-color: var(--primary-color);
}
.setting-group textarea {
resize: vertical;
min-height: 120px;
}
.save-btn {
@@ -1801,6 +1899,30 @@ export default {
background: #5a6268;
}
.btn-pickup-confirmation {
padding: 0.75rem 1.5rem;
border: none;
border-radius: 8px;
cursor: pointer;
font-size: 1rem;
transition: all 0.3s ease;
background: var(--secondary-color);
color: white;
font-weight: bold;
}
.btn-pickup-confirmation:hover:not(:disabled) {
background: var(--secondary-dark);
box-shadow: 0 4px 10px rgba(139, 111, 71, 0.3);
transform: translateY(-2px);
}
.btn-pickup-confirmation:disabled {
background: #bdc3c7;
cursor: not-allowed;
opacity: 0.6;
}
/* Form Styles */
.form-group {
display: flex;
+8 -8
View File
@@ -93,7 +93,7 @@ export default {
.back-btn {
display: inline-block;
background: #6c757d;
background: var(--secondary-color);
color: white;
padding: 0.75rem 1.5rem;
text-decoration: none;
@@ -103,7 +103,7 @@ export default {
}
.back-btn:hover {
background: #5a6268;
background: var(--secondary-dark);
}
.loading-state, .empty-state {
@@ -114,7 +114,7 @@ export default {
.loading-spinner {
border: 4px solid #f3f3f3;
border-top: 4px solid #3498db;
border-top: 4px solid var(--primary-color);
border-radius: 50%;
width: 50px;
height: 50px;
@@ -146,7 +146,7 @@ export default {
.price-card:hover {
transform: translateY(-5px);
box-shadow: 0 8px 25px rgba(0,0,0,0.15);
border-color: #3498db;
border-color: var(--primary-color);
}
.price-header {
@@ -165,7 +165,7 @@ export default {
}
.price {
background: #3498db;
background: var(--primary-color);
color: white;
padding: 0.5rem 1rem;
border-radius: 25px;
@@ -184,7 +184,7 @@ export default {
background: #f8f9fa;
padding: 3rem 2rem;
border-radius: 15px;
border: 2px dashed #3498db;
border: 2px dashed var(--primary-color);
}
.cta-section h2 {
@@ -200,7 +200,7 @@ export default {
.cta-button {
display: inline-block;
background: #3498db;
background: var(--primary-color);
color: white;
padding: 1rem 2rem;
text-decoration: none;
@@ -211,7 +211,7 @@ export default {
}
.cta-button:hover {
background: #2980b9;
background: var(--primary-dark);
}
@media (max-width: 768px) {