Pdf Changes Done

This commit is contained in:
Utkarsh Khedkar
2026-03-09 10:24:44 +05:30
parent c11467068c
commit 9cc6959396
32 changed files with 3416 additions and 2188 deletions

View File

@@ -98,9 +98,7 @@
gap: 10px;
}
.filter-title i {
color: var(--primary-color);
}
.filter-title i { color: var(--primary-color); }
.filter-grid {
display: grid;
@@ -108,9 +106,7 @@
gap: 15px;
}
.filter-group {
position: relative;
}
.filter-group { position: relative; }
.filter-group label {
display: block;
@@ -139,9 +135,7 @@
box-shadow: 0 0 0 3px rgba(76, 111, 255, 0.1);
}
.filter-input::placeholder {
color: #94a3b8;
}
.filter-input::placeholder { color: #94a3b8; }
.filter-actions {
display: flex;
@@ -212,9 +206,7 @@
gap: 12px;
}
.card-header h2 i {
color: white;
}
.card-header h2 i { color: white; }
.stats-badge {
background: rgba(255, 255, 255, 0.2);
@@ -239,9 +231,7 @@
transform: translateX(4px);
}
.container-item:last-child {
border-bottom: none;
}
.container-item:last-child { border-bottom: none; }
.container-header {
display: flex;
@@ -297,40 +287,92 @@
color: #94a3b8;
}
.status-badge {
padding: 6px 16px;
border-radius: 20px;
font-size: 12px;
/* STATUS DROPDOWN (badge look) */
.status-dropdown {
position: relative;
min-width: 190px;
cursor: pointer;
}
.status-dropdown-toggle {
padding: 8px 16px;
border-radius: 999px;
border: 1px solid var(--border-color);
background: #ffffff;
font-size: 13px;
font-weight: 600;
letter-spacing: 0.3px;
display: inline-flex;
color: var(--dark-text);
display: flex;
align-items: center;
justify-content: space-between;
gap: 6px;
}
.status-badge i {
font-size: 10px;
.status-dropdown-toggle span { white-space: nowrap; }
.status-dropdown-menu {
position: absolute;
top: -230%;
right: 0;
z-index: 30;
background: #ffffff;
border-radius: 14px;
padding: 8px 0;
box-shadow: var(--shadow-lg);
border: 1px solid var(--border-color);
width: 220px;
max-height: 340px;
overflow-y: auto;
display: none;
}
.status-pending {
background: #fef3c7;
color: #d97706;
.status-dropdown-menu.open { display: block; }
.status-option {
padding: 6px 14px;
font-size: 13px;
color: var(--dark-text);
cursor: pointer;
display: flex;
align-items: center;
gap: 8px;
transition: background 0.15s;
line-height: 1.3;
}
.status-in-progress {
background: #dbeafe;
color: #1d4ed8;
.status-option:hover { background: #eef2ff; }
.status-option .dot {
width: 8px;
height: 8px;
border-radius: 999px;
background: #9ca3af;
}
.status-completed {
background: #d1fae5;
color: #065f46;
.status-option.active .dot {
background: #22c55e;
}
.status-cancelled {
background: #fee2e2;
color: #991b1b;
}
/* COLOR MAPPING per status dropdown tint + main toggle text color */
.status-option.status-container-ready { background: #eff6ff; color: #1d4ed8; }
.status-option.status-export-custom { background: #fff7ed; color: #b45309; }
.status-option.status-international-transit { background: #f5f3ff; color: #4c1d95; }
.status-option.status-arrived-at-india { background: #ecfdf5; color: #15803d; }
.status-option.status-import-custom { background: #fffbeb; color: #92400e; }
.status-option.status-warehouse { background: #f4f4f5; color: #374151; }
.status-option.status-domestic-distribution { background: #faf5ff; color: #6d28d9; }
.status-option.status-out-for-delivery { background: #eff6ff; color: #1d4ed8; }
.status-option.status-delivered { background: #ecfdf5; color: #15803d; }
.status-dropdown-toggle.status-container-ready { background: #eff6ff; color: #1d4ed8; }
.status-dropdown-toggle.status-export-custom { background: #fff7ed; color: #b45309; }
.status-dropdown-toggle.status-international-transit { background: #f5f3ff; color: #4c1d95; }
.status-dropdown-toggle.status-arrived-at-india { background: #ecfdf5; color: #15803d; }
.status-dropdown-toggle.status-import-custom { background: #fffbeb; color: #92400e; }
.status-dropdown-toggle.status-warehouse { background: #f4f4f5; color: #374151; }
.status-dropdown-toggle.status-domestic-distribution { background: #faf5ff; color: #6d28d9; }
.status-dropdown-toggle.status-out-for-delivery { background: #eff6ff; color: #1d4ed8; }
.status-dropdown-toggle.status-delivered { background: #ecfdf5; color: #15803d; }
.action-buttons {
display: flex;
@@ -373,39 +415,7 @@
color: white;
}
.update-form {
display: flex;
align-items: center;
gap: 8px;
}
.status-select {
padding: 8px 12px;
border: 1px solid var(--border-color);
border-radius: var(--radius-md);
font-size: 13px;
color: var(--dark-text);
background: white;
min-width: 140px;
cursor: pointer;
}
.update-btn {
background: var(--primary-color);
color: white;
border: none;
padding: 8px 16px;
border-radius: var(--radius-md);
font-size: 13px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
white-space: nowrap;
}
.update-btn:hover {
background: #3b5de6;
}
.update-form { position: relative; }
.no-results {
text-align: center;
@@ -444,16 +454,13 @@
animation: slideIn 0.3s ease;
}
.success-message i {
font-size: 20px;
}
.success-message i { font-size: 20px; }
@keyframes slideIn {
from { opacity: 0; transform: translateY(-10px); }
to { opacity: 1; transform: translateY(0); }
}
/* 🔥 Totals section */
.totals-section {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
@@ -504,9 +511,7 @@
width: 100%;
justify-content: center;
}
.filter-grid {
grid-template-columns: 1fr;
}
.filter-grid { grid-template-columns: 1fr; }
.container-header {
flex-direction: column;
align-items: flex-start;
@@ -519,13 +524,8 @@
}
@media (max-width: 576px) {
.update-form {
flex-direction: column;
width: 100%;
}
.status-select, .update-btn {
width: 100%;
}
.update-form { width: 100%; }
.status-dropdown { width: 100%; }
}
</style>
@@ -569,10 +569,15 @@
<label><i class="fas fa-tag"></i> Status</label>
<select name="status" class="filter-select">
<option value="">All Status</option>
<option value="pending" {{ request('status') == 'pending' ? 'selected' : '' }}>Pending</option>
<option value="in-progress" {{ request('status') == 'in-progress' ? 'selected' : '' }}>In Progress</option>
<option value="completed" {{ request('status') == 'completed' ? 'selected' : '' }}>Completed</option>
<option value="cancelled" {{ request('status') == 'cancelled' ? 'selected' : '' }}>Cancelled</option>
<option value="container-ready" {{ request('status') == 'container-ready' ? 'selected' : '' }}>Container Ready</option>
<option value="export-custom" {{ request('status') == 'export-custom' ? 'selected' : '' }}>Export Custom</option>
<option value="international-transit" {{ request('status') == 'international-transit' ? 'selected' : '' }}>International Transit</option>
<option value="arrived-at-india" {{ request('status') == 'arrived-at-india' ? 'selected' : '' }}>Arrived at India</option>
<option value="import-custom" {{ request('status') == 'import-custom' ? 'selected' : '' }}>Import Custom</option>
<option value="warehouse" {{ request('status') == 'warehouse' ? 'selected' : '' }}>Warehouse</option>
<option value="domestic-distribution" {{ request('status') == 'domestic-distribution' ? 'selected' : '' }}>Domestic Distribution</option>
<option value="out-for-delivery" {{ request('status') == 'out-for-delivery' ? 'selected' : '' }}>Out for Delivery</option>
<option value="delivered" {{ request('status') == 'delivered' ? 'selected' : '' }}>Delivered</option>
</select>
</div>
@@ -610,22 +615,31 @@
<p>Get started by creating your first container</p>
</div>
@else
@php
$labels = [
'container-ready' => 'Container Ready',
'export-custom' => 'Export Custom',
'international-transit' => 'International Transit',
'arrived-at-india' => 'Arrived at India',
'import-custom' => 'Import Custom',
'warehouse' => 'Warehouse',
'domestic-distribution' => 'Domestic Distribution',
'out-for-delivery' => 'Out for Delivery',
'delivered' => 'Delivered',
];
@endphp
@foreach($containers as $container)
@php
$status = $container->status;
$statusClass = match ($status) {
'completed' => 'status-completed',
'in-progress' => 'status-in-progress',
'cancelled' => 'status-cancelled',
default => 'status-pending',
};
$status = $container->status ?? 'container-ready';
$statusLabel = $labels[$status] ?? ucfirst(str_replace('-', ' ', $status));
@endphp
<div class="container-item">
<div class="container-header">
<div class="container-info">
<div class="container-avatar">
{{ substr($container->container_name, 0, 2) }}
{{ strtoupper(substr($container->container_name, 0, 2)) }}
</div>
<div class="container-details">
<h3>{{ $container->container_name }}</h3>
@@ -647,51 +661,56 @@
</div>
<div class="action-buttons">
<span class="status-badge {{ $statusClass }}">
<i class="fas fa-circle"></i>
<span class="status-text">
{{ ucfirst(str_replace('-', ' ', $status)) }}
</span>
</span>
@can('container.update')
<a href="{{ route('containers.show', $container->id) }}" class="action-btn view-btn">
<i class="fas fa-eye"></i> View
</a>
@endcan
@can('containers.update_status')
<form action="{{ route('containers.update-status', $container->id) }}"
method="POST"
class="update-form ajax-status-form"
data-container-id="{{ $container->id }}">
method="POST"
class="update-form ajax-status-form"
data-container-id="{{ $container->id }}">
@csrf
<select name="status" class="status-select">
<option value="pending" {{ $status === 'pending' ? 'selected' : '' }}>Pending</option>
<option value="in-progress" {{ $status === 'in-progress' ? 'selected' : '' }}>In Progress</option>
<option value="completed" {{ $status === 'completed' ? 'selected' : '' }}>Completed</option>
<option value="cancelled" {{ $status === 'cancelled' ? 'selected' : '' }}>Cancelled</option>
</select>
@php $statusClass = 'status-' . $status; @endphp
<div class="status-dropdown">
<div class="status-dropdown-toggle {{ $statusClass }}">
<span class="status-dropdown-label">
{{ $statusLabel }}
</span>
<i class="fas fa-chevron-down" style="font-size:11px;color:#4b5563;"></i>
</div>
<div class="status-dropdown-menu">
@foreach($labels as $value => $label)
@php $optClass = 'status-' . $value; @endphp
<div class="status-option {{ $optClass }} {{ $status === $value ? 'active' : '' }}"
data-status="{{ $value }}">
<span class="dot"></span>
<span>{{ $label }}</span>
</div>
@endforeach
</div>
</div>
</form>
@endcan
@endcan
@can('container.update')
<a href="{{ route('containers.show', $container->id) }}" class="action-btn view-btn">
<i class="fas fa-eye"></i> View
</a>
@endcan
@can('container.delete')
<form action="{{ route('containers.destroy', $container->id) }}" method="POST"
onsubmit="return confirm('Are you sure you want to delete this container and all its entries?');">
@csrf
@method('DELETE')
<button type="submit" class="action-btn delete-btn">
<i class="fas fa-trash"></i> Delete
</button>
</form>
<form action="{{ route('containers.destroy', $container->id) }}" method="POST"
class="delete-form"
data-container-id="{{ $container->id }}">
@csrf
@method('DELETE')
<button type="submit" class="action-btn delete-btn">
<i class="fas fa-trash"></i> Delete
</button>
</form>
@endcan
</div>
</div>
<!-- 🔥 Totals instead of first row preview -->
<div class="totals-section">
<div class="total-card">
<div class="total-value">{{ number_format($container->summary['total_ctn'], 1) }}</div>
@@ -716,87 +735,121 @@
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function () {
document.querySelectorAll('.ajax-status-form .status-select').forEach(function (selectEl) {
selectEl.addEventListener('change', function (event) {
const form = event.target.closest('form');
const url = form.getAttribute('action');
const token = form.querySelector('input[name="_token"]').value;
const status = event.target.value;
// STATUS DROPDOWN
document.querySelectorAll('.status-dropdown').forEach(function (wrapper) {
const toggle = wrapper.querySelector('.status-dropdown-toggle');
const menu = wrapper.querySelector('.status-dropdown-menu');
console.log('Sending status update:', url, status);
toggle.addEventListener('click', function (e) {
e.stopPropagation();
document.querySelectorAll('.status-dropdown-menu.open').forEach(m => {
if (m !== menu) m.classList.remove('open');
});
menu.classList.toggle('open');
});
menu.querySelectorAll('.status-option').forEach(function (opt) {
opt.addEventListener('click', function () {
const status = this.dataset.status;
const form = wrapper.closest('form');
const labelEl = wrapper.querySelector('.status-dropdown-label');
const toggleEl= wrapper.querySelector('.status-dropdown-toggle');
// UI: dropdown label + active item
menu.querySelectorAll('.status-option').forEach(o => o.classList.remove('active'));
this.classList.add('active');
labelEl.textContent = this.querySelector('span:nth-child(2)').textContent;
menu.classList.remove('open');
// toggle रंग class reset करून नवा status-* दे
toggleEl.className = 'status-dropdown-toggle';
toggleEl.classList.add('status-' + status);
const url = form.getAttribute('action');
const token = form.querySelector('input[name="_token"]').value;
const formData = new FormData();
formData.append('_token', token);
formData.append('status', status);
fetch(url, {
method: 'POST',
headers: {
'X-Requested-With': 'XMLHttpRequest',
'Accept': 'application/json',
},
body: formData
})
.then(async res => {
let data = null;
try { data = await res.json(); } catch (e) {}
if (!res.ok || !data || !data.success) {
alert('Status update failed');
return;
}
})
.catch(() => {
alert('Network error while updating status');
});
});
});
});
document.addEventListener('click', function () {
document.querySelectorAll('.status-dropdown-menu.open')
.forEach(m => m.classList.remove('open'));
});
// DELETE VIA AJAX
document.querySelectorAll('.delete-form').forEach(function (form) {
form.addEventListener('submit', function (e) {
e.preventDefault();
if (!confirm('Are you sure you want to delete this container and all its entries?')) {
return;
}
const url = form.getAttribute('action');
const token = form.querySelector('input[name="_token"]').value;
const item = form.closest('.container-item');
const formData = new FormData();
formData.append('_token', token);
formData.append('_method', 'DELETE');
fetch(url, {
method: 'POST',
headers: {
'X-CSRF-TOKEN': token,
'X-Requested-With': 'XMLHttpRequest',
'Content-Type': 'application/json',
'Accept': 'application/json',
},
body: JSON.stringify({ status: status })
body: formData
})
.then(async res => {
console.log('Response status:', res.status);
let data = null;
try { data = await res.json(); } catch (e) {}
console.log('Response body:', data);
if (!res.ok || !data || !data.success) {
alert('Status update failed');
alert('Delete failed');
return;
}
// 👉 UI update (badge text + color)
const item = form.closest('.container-item');
const badge = item.querySelector('.status-badge');
// text
// text
const pretty = data.status.replace('-', ' ');
const textEl = badge.querySelector('.status-text');
if (textEl) {
textEl.textContent =
pretty.charAt(0).toUpperCase() + pretty.slice(1);
}
// classes
badge.classList.remove(
'status-pending',
'status-in-progress',
'status-completed',
'status-cancelled'
);
if (data.status === 'pending') {
badge.classList.add('status-pending');
} else if (data.status === 'in-progress') {
badge.classList.add('status-in-progress');
} else if (data.status === 'completed') {
badge.classList.add('status-completed');
} else if (data.status === 'cancelled') {
badge.classList.add('status-cancelled');
if (item) {
item.style.opacity = '0';
item.style.transform = 'translateX(-10px)';
setTimeout(() => item.remove(), 200);
}
})
.catch(err => {
console.error('Error:', err);
alert('Network error while updating status');
.catch(() => {
alert('Network error while deleting container');
});
});
});
});
</script>
<link rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
@endsection