Files
Kent-logistics-Laravel/resources/views/admin/container.blade.php

856 lines
28 KiB
PHP
Raw Normal View History

2026-02-27 10:51:26 +05:30
@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;
}
2026-03-09 10:24:44 +05:30
.filter-title i { color: var(--primary-color); }
2026-02-27 10:51:26 +05:30
.filter-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 15px;
}
2026-03-09 10:24:44 +05:30
.filter-group { position: relative; }
2026-02-27 10:51:26 +05:30
.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);
}
2026-03-09 10:24:44 +05:30
.filter-input::placeholder { color: #94a3b8; }
2026-02-27 10:51:26 +05:30
.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;
}
2026-03-09 10:24:44 +05:30
.card-header h2 i { color: white; }
2026-02-27 10:51:26 +05:30
.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);
}
2026-03-09 10:24:44 +05:30
.container-item:last-child { border-bottom: none; }
2026-02-27 10:51:26 +05:30
.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;
}
2026-03-09 10:24:44 +05:30
/* 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;
2026-02-27 10:51:26 +05:30
font-weight: 600;
2026-03-09 10:24:44 +05:30
color: var(--dark-text);
display: flex;
2026-02-27 10:51:26 +05:30
align-items: center;
2026-03-09 10:24:44 +05:30
justify-content: space-between;
2026-02-27 10:51:26 +05:30
gap: 6px;
}
2026-03-09 10:24:44 +05:30
.status-dropdown-toggle span { white-space: nowrap; }
2026-02-27 10:51:26 +05:30
2026-03-09 10:24:44 +05:30
.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;
2026-02-27 10:51:26 +05:30
}
2026-03-09 10:24:44 +05:30
.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;
2026-02-27 10:51:26 +05:30
}
2026-03-09 10:24:44 +05:30
.status-option:hover { background: #eef2ff; }
.status-option .dot {
width: 8px;
height: 8px;
border-radius: 999px;
background: #9ca3af;
2026-02-27 10:51:26 +05:30
}
2026-03-09 10:24:44 +05:30
.status-option.active .dot {
background: #22c55e;
2026-02-27 10:51:26 +05:30
}
2026-03-09 10:24:44 +05:30
/* 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; }
2026-02-27 10:51:26 +05:30
.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;
}
2026-03-09 10:24:44 +05:30
.update-form { position: relative; }
2026-02-27 10:51:26 +05:30
.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;
}
2026-03-09 10:24:44 +05:30
.success-message i { font-size: 20px; }
2026-02-27 10:51:26 +05:30
@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;
}
2026-03-09 10:24:44 +05:30
.filter-grid { grid-template-columns: 1fr; }
2026-02-27 10:51:26 +05:30
.container-header {
flex-direction: column;
align-items: flex-start;
gap: 16px;
}
.action-buttons {
width: 100%;
justify-content: flex-start;
}
}
@media (max-width: 576px) {
2026-03-09 10:24:44 +05:30
.update-form { width: 100%; }
.status-dropdown { width: 100%; }
2026-02-27 10:51:26 +05:30
}
</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>
2026-02-27 12:59:20 +05:30
@can('container.create')
2026-02-27 10:51:26 +05:30
<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>
2026-03-09 10:24:44 +05:30
<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>
2026-02-27 10:51:26 +05:30
</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
2026-03-09 10:24:44 +05:30
@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
2026-02-27 10:51:26 +05:30
@foreach($containers as $container)
@php
2026-03-09 10:24:44 +05:30
$status = $container->status ?? 'container-ready';
$statusLabel = $labels[$status] ?? ucfirst(str_replace('-', ' ', $status));
2026-02-27 10:51:26 +05:30
@endphp
<div class="container-item">
<div class="container-header">
<div class="container-info">
<div class="container-avatar">
2026-03-09 10:24:44 +05:30
{{ strtoupper(substr($container->container_name, 0, 2)) }}
2026-02-27 10:51:26 +05:30
</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) }}"
2026-03-09 10:24:44 +05:30
method="POST"
class="update-form ajax-status-form"
data-container-id="{{ $container->id }}">
2026-02-27 10:51:26 +05:30
@csrf
2026-03-09 10:24:44 +05:30
@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
2026-02-27 10:51:26 +05:30
2026-03-09 10:24:44 +05:30
@can('container.update')
<a href="{{ route('containers.show', $container->id) }}" class="action-btn view-btn">
<i class="fas fa-eye"></i> View
</a>
@endcan
2026-02-27 10:51:26 +05:30
2026-02-27 12:59:20 +05:30
@can('container.delete')
2026-03-09 10:24:44 +05:30
<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>
2026-02-27 10:51:26 +05:30
@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 () {
2026-03-09 10:24:44 +05:30
// 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'));
});
2026-02-27 10:51:26 +05:30
2026-03-09 10:24:44 +05:30
// 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');
2026-02-27 10:51:26 +05:30
fetch(url, {
method: 'POST',
headers: {
'X-Requested-With': 'XMLHttpRequest',
'Accept': 'application/json',
},
2026-03-09 10:24:44 +05:30
body: formData
2026-02-27 10:51:26 +05:30
})
.then(async res => {
let data = null;
try { data = await res.json(); } catch (e) {}
if (!res.ok || !data || !data.success) {
2026-03-09 10:24:44 +05:30
alert('Delete failed');
2026-02-27 10:51:26 +05:30
return;
}
2026-03-09 10:24:44 +05:30
if (item) {
item.style.opacity = '0';
item.style.transform = 'translateX(-10px)';
setTimeout(() => item.remove(), 200);
2026-02-27 10:51:26 +05:30
}
})
2026-03-09 10:24:44 +05:30
.catch(() => {
alert('Network error while deleting container');
2026-02-27 10:51:26 +05:30
});
});
});
});
</script>
<link rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
@endsection