Files
Kent-logistics-Laravel/resources/views/admin/container.blade.php
Utkarsh Khedkar 9cc6959396 Pdf Changes Done
2026-03-09 10:24:44 +05:30

856 lines
28 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

@extends('admin.layouts.app')
@section('page-title', 'Containers')
@section('content')
<style>
:root {
--primary-color: #4c6fff;
--primary-gradient: linear-gradient(135deg, #4c6fff, #8e54e9);
--success-color: #10b981;
--warning-color: #f59e0b;
--danger-color: #ef4444;
--info-color: #3b82f6;
--light-bg: #f8fafc;
--dark-text: #1e293b;
--gray-text: #64748b;
--border-color: #e2e8f0;
--shadow-sm: 0 1px 3px rgba(0,0,0,0.1);
--shadow-md: 0 4px 6px -1px rgba(0,0,0,0.1);
--shadow-lg: 0 10px 25px -5px rgba(0,0,0,0.1);
--radius-lg: 16px;
--radius-md: 12px;
--radius-sm: 8px;
}
.containers-wrapper {
min-height: calc(100vh - 180px);
padding: 20px 15px;
background: linear-gradient(135deg, #f6f9ff 0%, #f0f4ff 100%);
}
.page-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 30px;
padding: 0 10px;
}
.header-content h1 {
font-size: 28px;
font-weight: 700;
background: var(--primary-gradient);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
margin-bottom: 5px;
}
.header-subtitle {
color: var(--gray-text);
font-size: 14px;
font-weight: 500;
}
.add-container-btn {
background: var(--primary-gradient);
color: white;
border: none;
padding: 12px 28px;
border-radius: var(--radius-md);
font-weight: 600;
font-size: 14px;
transition: all 0.3s ease;
display: flex;
align-items: center;
gap: 8px;
box-shadow: 0 4px 12px rgba(76, 111, 255, 0.3);
}
.add-container-btn:hover {
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(76, 111, 255, 0.4);
color: white;
}
.add-container-btn i {
font-size: 16px;
}
.filter-card {
background: white;
border-radius: var(--radius-lg);
padding: 24px;
margin-bottom: 30px;
box-shadow: var(--shadow-lg);
border: 1px solid rgba(255,255,255,0.9);
backdrop-filter: blur(10px);
}
.filter-title {
font-size: 16px;
font-weight: 600;
color: var(--dark-text);
margin-bottom: 20px;
display: flex;
align-items: center;
gap: 10px;
}
.filter-title i { color: var(--primary-color); }
.filter-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 15px;
}
.filter-group { position: relative; }
.filter-group label {
display: block;
font-size: 12px;
font-weight: 600;
color: var(--gray-text);
margin-bottom: 8px;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.filter-input, .filter-select, .filter-date {
width: 100%;
padding: 12px 16px;
border: 2px solid var(--border-color);
border-radius: var(--radius-md);
font-size: 14px;
color: var(--dark-text);
background: white;
transition: all 0.3s ease;
}
.filter-input:focus, .filter-select:focus, .filter-date:focus {
outline: none;
border-color: var(--primary-color);
box-shadow: 0 0 0 3px rgba(76, 111, 255, 0.1);
}
.filter-input::placeholder { color: #94a3b8; }
.filter-actions {
display: flex;
gap: 10px;
align-items: flex-end;
}
.apply-btn {
background: var(--primary-gradient);
color: white;
border: none;
padding: 12px 24px;
border-radius: var(--radius-md);
font-weight: 600;
font-size: 14px;
cursor: pointer;
transition: all 0.3s ease;
min-height: 46px;
width: 100%;
}
.apply-btn:hover {
transform: translateY(-1px);
box-shadow: 0 6px 20px rgba(76, 111, 255, 0.3);
}
.reset-btn {
background: white;
color: var(--gray-text);
border: 2px solid var(--border-color);
padding: 12px 24px;
border-radius: var(--radius-md);
font-weight: 600;
font-size: 14px;
cursor: pointer;
transition: all 0.3s ease;
min-height: 46px;
width: 100%;
}
.reset-btn:hover {
border-color: var(--primary-color);
color: var(--primary-color);
}
.main-card {
background: white;
border-radius: var(--radius-lg);
overflow: hidden;
box-shadow: var(--shadow-lg);
margin-bottom: 30px;
border: 1px solid rgba(255,255,255,0.9);
}
.card-header {
padding: 24px;
background: linear-gradient(135deg, #4c6fff, #8e54e9);
color: white;
}
.card-header h2 {
font-size: 20px;
font-weight: 700;
color: white;
margin: 0;
display: flex;
align-items: center;
gap: 12px;
}
.card-header h2 i { color: white; }
.stats-badge {
background: rgba(255, 255, 255, 0.2);
color: white;
font-size: 12px;
font-weight: 600;
padding: 4px 12px;
border-radius: 20px;
margin-left: 10px;
backdrop-filter: blur(10px);
}
.container-item {
padding: 16px 20px;
border-bottom: 1px solid var(--border-color);
transition: all 0.3s ease;
background: white;
}
.container-item:hover {
background: #f8fafc;
transform: translateX(4px);
}
.container-item:last-child { border-bottom: none; }
.container-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
}
.container-info {
display: flex;
align-items: center;
gap: 12px;
}
.container-avatar {
width: 40px;
height: 40px;
border-radius: 50%;
background: var(--primary-gradient);
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 16px;
font-weight: 700;
flex-shrink: 0;
box-shadow: 0 4px 12px rgba(76, 111, 255, 0.3);
}
.container-details h3 {
font-size: 16px;
font-weight: 700;
color: var(--dark-text);
margin: 0 0 4px 0;
}
.container-meta {
display: flex;
align-items: center;
gap: 12px;
font-size: 12px;
color: var(--gray-text);
}
.meta-item {
display: flex;
align-items: center;
gap: 4px;
}
.meta-item i {
font-size: 12px;
color: #94a3b8;
}
/* 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;
color: var(--dark-text);
display: flex;
align-items: center;
justify-content: space-between;
gap: 6px;
}
.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-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-option:hover { background: #eef2ff; }
.status-option .dot {
width: 8px;
height: 8px;
border-radius: 999px;
background: #9ca3af;
}
.status-option.active .dot {
background: #22c55e;
}
/* 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;
align-items: center;
gap: 12px;
flex-wrap: wrap;
}
.action-btn {
padding: 8px 16px;
border-radius: var(--radius-md);
font-size: 13px;
font-weight: 600;
display: flex;
align-items: center;
gap: 6px;
transition: all 0.3s ease;
text-decoration: none;
}
.view-btn {
background: #e0f2fe;
color: #0369a1;
border: none;
}
.view-btn:hover {
background: #0ea5e9;
color: white;
}
.delete-btn {
background: #fef2f2;
color: #dc2626;
border: 1px solid #fecaca;
}
.delete-btn:hover {
background: #dc2626;
color: white;
}
.update-form { position: relative; }
.no-results {
text-align: center;
padding: 60px 20px;
}
.no-results-icon {
font-size: 64px;
color: var(--border-color);
margin-bottom: 20px;
}
.no-results h4 {
font-size: 18px;
color: var(--gray-text);
margin-bottom: 10px;
}
.no-results p {
color: #94a3b8;
font-size: 14px;
max-width: 400px;
margin: 0 auto;
}
.success-message {
background: linear-gradient(135deg, #10b981, #059669);
color: white;
padding: 16px 24px;
border-radius: var(--radius-md);
margin-bottom: 24px;
display: flex;
align-items: center;
gap: 12px;
box-shadow: var(--shadow-md);
animation: slideIn 0.3s ease;
}
.success-message i { font-size: 20px; }
@keyframes slideIn {
from { opacity: 0; transform: translateY(-10px); }
to { opacity: 1; transform: translateY(0); }
}
.totals-section {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
gap: 12px;
margin-top: 16px;
padding: 16px;
background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);
border-radius: var(--radius-md);
border-left: 4px solid var(--primary-color);
}
.total-card {
text-align: center;
padding: 12px;
background: white;
border-radius: var(--radius-sm);
box-shadow: var(--shadow-sm);
transition: all 0.3s ease;
}
.total-card:hover {
transform: translateY(-2px);
box-shadow: var(--shadow-md);
}
.total-value {
font-size: 20px;
font-weight: 800;
color: var(--primary-color);
margin-bottom: 4px;
}
.total-label {
font-size: 11px;
font-weight: 600;
color: var(--gray-text);
text-transform: uppercase;
letter-spacing: 0.5px;
}
@media (max-width: 768px) {
.page-header {
flex-direction: column;
align-items: flex-start;
gap: 16px;
}
.add-container-btn {
width: 100%;
justify-content: center;
}
.filter-grid { grid-template-columns: 1fr; }
.container-header {
flex-direction: column;
align-items: flex-start;
gap: 16px;
}
.action-buttons {
width: 100%;
justify-content: flex-start;
}
}
@media (max-width: 576px) {
.update-form { width: 100%; }
.status-dropdown { width: 100%; }
}
</style>
<div class="containers-wrapper">
<div class="page-header">
<div class="header-content">
<h1>Container Management</h1>
<div class="header-subtitle">
Manage all containers, track status, and view entries in real-time
</div>
</div>
@can('container.create')
<a href="{{ route('containers.create') }}" class="add-container-btn">
<i class="fas fa-plus-circle"></i>
Add New Container
</a>
@endcan
</div>
@if(session('success'))
<div class="success-message">
<i class="fas fa-check-circle"></i>
<span>{{ session('success') }}</span>
</div>
@endif
<div class="filter-card">
<div class="filter-title">
<i class="fas fa-filter"></i>
Filter Containers
</div>
<form method="GET" class="filter-grid">
<div class="filter-group">
<label><i class="fas fa-search"></i> Search</label>
<input type="text" name="search" class="filter-input"
placeholder="Search by container name or number..."
value="{{ request('search') }}">
</div>
<div class="filter-group">
<label><i class="fas fa-tag"></i> Status</label>
<select name="status" class="filter-select">
<option value="">All Status</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>
<div class="filter-group">
<label><i class="fas fa-calendar"></i> Date</label>
<input type="date" name="date" class="filter-date" value="{{ request('date') }}">
</div>
<div class="filter-actions">
<button type="submit" class="apply-btn">
<i class="fas fa-search"></i> Apply Filters
</button>
<a href="{{ route('containers.index') }}" class="reset-btn">
<i class="fas fa-redo"></i> Reset
</a>
</div>
</form>
</div>
<div class="main-card">
<div class="card-header">
<h2>
<i class="fas fa-boxes"></i>
Containers List
<span class="stats-badge">{{ $containers->count() }} containers</span>
</h2>
</div>
@if($containers->isEmpty())
<div class="no-results">
<div class="no-results-icon">
<i class="fas fa-box-open"></i>
</div>
<h4>No containers found</h4>
<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 ?? '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">
{{ strtoupper(substr($container->container_name, 0, 2)) }}
</div>
<div class="container-details">
<h3>{{ $container->container_name }}</h3>
<div class="container-meta">
<div class="meta-item">
<i class="fas fa-hashtag"></i>
<span>{{ $container->container_number }}</span>
</div>
<div class="meta-item">
<i class="fas fa-calendar"></i>
<span>{{ $container->container_date?->format('M d, Y') ?: 'No date' }}</span>
</div>
<div class="meta-item">
<i class="fas fa-list"></i>
<span>{{ $container->rows->count() }} entries</span>
</div>
</div>
</div>
</div>
<div class="action-buttons">
@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 }}">
@csrf
@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
@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"
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>
<div class="totals-section">
<div class="total-card">
<div class="total-value">{{ number_format($container->summary['total_ctn'], 1) }}</div>
<div class="total-label">Total CTN</div>
</div>
<div class="total-card">
<div class="total-value">{{ number_format($container->summary['total_qty'], 0) }}</div>
<div class="total-label">Total QTY</div>
</div>
<div class="total-card">
<div class="total-value">{{ number_format($container->summary['total_cbm'], 3) }}</div>
<div class="total-label">Total CBM</div>
</div>
<div class="total-card">
<div class="total-value">{{ number_format($container->summary['total_kg'], 1) }}</div>
<div class="total-label">Total KG</div>
</div>
</div>
</div>
@endforeach
@endif
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function () {
// STATUS DROPDOWN
document.querySelectorAll('.status-dropdown').forEach(function (wrapper) {
const toggle = wrapper.querySelector('.status-dropdown-toggle');
const menu = wrapper.querySelector('.status-dropdown-menu');
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-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('Delete failed');
return;
}
if (item) {
item.style.opacity = '0';
item.style.transform = 'translateX(-10px)';
setTimeout(() => item.remove(), 200);
}
})
.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