eSPJ/Views/Admin/Transport/SpjDriverUpst/DetailPenjemputan/Batal.cshtml

301 lines
14 KiB
Plaintext

@{
Layout = "~/Views/Admin/Transport/SpjDriverUpst/Shared/_Layout.cshtml";
ViewData["Title"] = "Batal Penjemputan";
}
<div class="w-full lg:max-w-sm mx-auto bg-gray-50 min-h-screen">
<div class="bg-upst text-white px-6 pt-8 pb-16 rounded-b-[40px] shadow-lg relative">
<div class="flex items-center justify-between relative z-10">
<a href="@Url.Action("Index", "Home")" class="w-10 h-10 flex items-center justify-center bg-white/10 rounded-xl backdrop-blur-md">
<i class="w-5 h-5" data-lucide="chevron-left"></i>
</a>
<h1 class="text-lg font-black uppercase tracking-tight">Batal Angkut</h1>
<div class="w-10"></div>
</div>
</div>
<div class="px-4 pt-4">
<div class="bg-white rounded-2xl p-4 shadow-sm border border-gray-100 mb-4">
<div class="flex items-center gap-3 mb-3">
<div class="w-10 h-10 bg-gray-100 rounded-full flex items-center justify-center">
<i class="w-5 h-5 text-gray-600" data-lucide="map-pin"></i>
</div>
<div>
<h3 class="font-bold text-gray-900">CV Tri Berkah Sejahtera</h3>
<p class="text-xs text-gray-500">Lokasi yang dibatalkan</p>
</div>
</div>
<p class="text-sm text-gray-700 leading-relaxed">
Kp. Pertanian II Rt.004 Rw.001 Kel. Klender Kec, Duren Sawit, Kota Adm. Jakarta Timur 13470
</p>
</div>
</div>
<div class="px-4 pb-6">
<div class="bg-white rounded-2xl p-5 shadow-sm border border-gray-100">
<div class="flex items-center gap-3 mb-4">
<div class="w-10 h-10 bg-gray-100 rounded-full flex items-center justify-center">
<i class="w-5 h-5 text-gray-600" data-lucide="file-text"></i>
</div>
<div>
<h2 class="text-lg font-bold text-gray-900">Form Pembatalan</h2>
<p class="text-xs text-gray-500">Berikan keterangan</p>
</div>
</div>
<form asp-action="Batal" method="post" class="space-y-5">
<!-- Alasan Pembatalan -->
<div>
<label class="block text-sm font-semibold text-gray-700 mb-3">Alasan Pembatalan</label>
<textarea name="AlasanPembatalan"
class="w-full rounded-xl text-sm border border-gray-300 focus:border-red-500 focus:ring-2 focus:ring-red-200 p-4 text-gray-700 resize-none transition-all duration-200"
rows="5"
placeholder="Jelaskan alasan pembatalan penjemputan..."
required></textarea>
<div class="text-red-500 text-sm mt-2" id="validation-message" style="display: none;">
Harap isi alasan pembatalan
</div>
</div>
<!-- Upload Foto Bukti -->
<div>
<label class="block text-sm font-semibold text-gray-700 mb-3">Foto Bukti Pembatalan</label>
<input type="file"
name="FotoBukti"
id="fotoBukti"
accept="image/*"
multiple
class="block w-full text-sm text-gray-700 border border-gray-300 rounded-xl p-2 file:mr-3 file:rounded-lg file:border-0 file:bg-red-500 file:px-4 file:py-2 file:text-xs file:font-bold file:text-white hover:file:bg-red-600 transition-all duration-200" />
<p class="text-xs text-gray-500 mt-2">Upload foto sebagai bukti pembatalan
<div id="preview-container" class="mt-3 space-y-2"></div>
</div>
<div class="flex gap-3 pt-4">
<a href="@Url.Action("Index", "Home")"
class="flex-1 bg-gray-200 text-gray-700 font-semibold py-3 rounded-xl text-center hover:bg-gray-300 transition-colors">
Batal
</a>
<button type="submit"
class="flex-1 bg-upst text-white font-semibold py-3 rounded-xl hover:from-gray-600 hover:to-gray-700 transition-all duration-200 shadow-lg">
Konfirmasi
</button>
</div>
</form>
</div>
</div>
<!-- Bottom Navigation -->
<partial name="~/Views/Admin/Transport/SpjDriverUpst/Shared/Components/_Navigation.cshtml" />
</div>
<register-block dynamic-section="scripts" key="jsDetailBatal">
<script>
document.addEventListener('DOMContentLoaded', function() {
const alasanTextarea = document.querySelector('textarea[name="AlasanPembatalan"]');
const form = document.querySelector('form');
const validationMessage = document.getElementById('validation-message');
const fotoBuktiInput = document.getElementById('fotoBukti');
const previewContainer = document.getElementById('preview-container');
// Fungsi watermark untuk foto pembatalan
async function applyWatermark(file, photoNumber) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = function(e) {
const img = new Image();
img.onload = function() {
const canvas = document.createElement('canvas');
canvas.width = img.width;
canvas.height = img.height;
const ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0);
const now = new Date();
const days = ['Minggu', 'Senin', 'Selasa', 'Rabu', 'Kamis', 'Jumat', 'Sabtu'];
const months = ['Jan', 'Feb', 'Mar', 'Apr', 'Mei', 'Jun', 'Jul', 'Agu', 'Sep', 'Okt', 'Nov', 'Des'];
const dayName = days[now.getDay()];
const date = now.getDate().toString().padStart(2, '0');
const month = months[now.getMonth()];
const year = now.getFullYear();
const hours = now.getHours().toString().padStart(2, '0');
const minutes = now.getMinutes().toString().padStart(2, '0');
const seconds = now.getSeconds().toString().padStart(2, '0');
const timestamp = `${dayName}, ${date} ${month} ${year} • ${hours}:${minutes}:${seconds}`;
const baseFontSize = Math.max(16, Math.min(img.width, img.height) * 0.025);
const fontFamily = "'Montserrat', 'Segoe UI', 'Roboto', sans-serif";
const lines = [
{ text: `FOTO PEMBATALAN #${photoNumber}`, size: baseFontSize * 1.1, weight: '900', color: '#EF4444' },
{ text: 'SPJ/07-2025/PKM/000476', size: baseFontSize * 0.9, weight: '700', color: '#FFFFFF' },
{ text: timestamp, size: baseFontSize * 0.75, weight: '500', color: '#E2E8F0' }
];
const paddingX = baseFontSize * 1.2;
const paddingY = baseFontSize * 1.0;
const lineGap = baseFontSize * 0.4;
let maxWidth = 0;
let totalHeight = 0;
lines.forEach(line => {
ctx.font = `${line.weight} ${line.size}px ${fontFamily}`;
const metrics = ctx.measureText(line.text);
maxWidth = Math.max(maxWidth, metrics.width);
totalHeight += line.size + lineGap;
});
totalHeight -= lineGap;
const margin = baseFontSize * 1.5;
const boxWidth = maxWidth + (paddingX * 2);
const boxHeight = totalHeight + (paddingY * 2);
const boxX = img.width - boxWidth - margin;
const boxY = img.height - boxHeight - margin;
ctx.save();
ctx.beginPath();
if (ctx.roundRect) {
ctx.roundRect(boxX, boxY, boxWidth, boxHeight, baseFontSize * 0.8);
} else {
ctx.rect(boxX, boxY, boxWidth, boxHeight);
}
ctx.fillStyle = 'rgba(15, 23, 42, 0.85)';
ctx.fill();
// Garis aksen merah untuk pembatalan
ctx.beginPath();
const accentWidth = baseFontSize * 0.3;
if (ctx.roundRect) {
ctx.roundRect(boxX + boxWidth - accentWidth, boxY, accentWidth, boxHeight, [0, baseFontSize * 0.8, baseFontSize * 0.8, 0]);
} else {
ctx.rect(boxX + boxWidth - accentWidth, boxY, accentWidth, boxHeight);
}
ctx.fillStyle = '#EF4444';
ctx.fill();
ctx.restore();
ctx.shadowColor = 'rgba(0, 0, 0, 0.6)';
ctx.shadowBlur = 4;
ctx.shadowOffsetX = 1;
ctx.shadowOffsetY = 1;
ctx.textAlign = 'right';
ctx.textBaseline = 'top';
let currentY = boxY + paddingY;
const textRightLimit = boxX + boxWidth - paddingX - accentWidth;
lines.forEach(line => {
ctx.font = `${line.weight} ${line.size}px ${fontFamily}`;
ctx.fillStyle = line.color;
ctx.fillText(line.text, textRightLimit, currentY);
currentY += line.size + lineGap;
});
ctx.shadowColor = 'transparent';
canvas.toBlob(function(blob) {
const watermarkedFile = new File([blob], file.name, {
type: 'image/jpeg',
lastModified: Date.now()
});
resolve(watermarkedFile);
}, 'image/jpeg', 0.95);
};
img.onerror = reject;
img.src = e.target.result;
};
reader.onerror = reject;
reader.readAsDataURL(file);
});
}
// Preview foto dengan watermark
fotoBuktiInput.addEventListener('change', async function() {
previewContainer.innerHTML = '';
if (this.files.length > 5) {
alert('Maksimal 5 foto yang dapat diupload');
this.value = '';
return;
}
// Tampilkan loading
previewContainer.innerHTML = '<div class="text-center py-4"><div class="inline-block animate-spin rounded-full h-8 w-8 border-4 border-gray-300 border-t-red-500"></div><p class="text-sm text-gray-600 mt-2">Memproses foto...</p></div>';
const watermarkedFiles = [];
for (let i = 0; i < this.files.length; i++) {
const file = this.files[i];
try {
const watermarkedFile = await applyWatermark(file, i + 1);
watermarkedFiles.push(watermarkedFile);
} catch (error) {
console.error('Error applying watermark:', error);
watermarkedFiles.push(file);
}
}
// Hapus loading dan tampilkan preview
previewContainer.innerHTML = '';
watermarkedFiles.forEach((file, index) => {
const reader = new FileReader();
reader.onload = function(e) {
const previewItem = document.createElement('div');
previewItem.className = 'rounded-xl border border-red-200 overflow-hidden bg-black shadow-sm hover:shadow-md transition-shadow';
previewItem.innerHTML = `
<div class="h-40 bg-black/80">
<img src="${e.target.result}" alt="Preview ${index + 1}" class="w-full h-full object-contain" />
</div>
<div class="px-3 py-2 bg-white">
<div class="flex items-center gap-2 mb-1">
<div class="flex-shrink-0 w-6 h-6 bg-red-500 rounded-full flex items-center justify-center">
<span class="text-white text-xs font-bold">${index + 1}</span>
</div>
<p class="text-xs font-semibold text-gray-700 truncate flex-1">${file.name}</p>
</div>
<div class="flex items-center justify-between text-[10px] text-gray-500">
<span>${(file.size / (1024 * 1024)).toFixed(2)} MB</span>
<span class="text-red-600 font-semibold">✓ Watermark</span>
</div>
</div>
`;
previewContainer.appendChild(previewItem);
};
reader.readAsDataURL(file);
});
// Update file input dengan watermarked files
const dataTransfer = new DataTransfer();
watermarkedFiles.forEach(file => dataTransfer.items.add(file));
this.files = dataTransfer.files;
});
form.addEventListener('submit', function(e) {
if (!alasanTextarea.value.trim()) {
e.preventDefault();
validationMessage.style.display = 'block';
alasanTextarea.focus();
alasanTextarea.style.borderColor = '#ef4444';
return false;
}
validationMessage.style.display = 'none';
return true;
});
alasanTextarea.addEventListener('input', function() {
if (this.value.trim()) {
validationMessage.style.display = 'none';
this.style.borderColor = '';
}
});
});
</script>
</register-block>