Pdf Changes Done
This commit is contained in:
@@ -8,6 +8,7 @@ use App\Models\InvoiceItem;
|
|||||||
use App\Models\InvoiceInstallment;
|
use App\Models\InvoiceInstallment;
|
||||||
use App\Models\InvoiceChargeGroup;
|
use App\Models\InvoiceChargeGroup;
|
||||||
use App\Models\InvoiceChargeGroupItem;
|
use App\Models\InvoiceChargeGroupItem;
|
||||||
|
use App\Models\User;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
use Illuminate\Support\Facades\Storage;
|
use Illuminate\Support\Facades\Storage;
|
||||||
@@ -20,9 +21,9 @@ class AdminInvoiceController extends Controller
|
|||||||
// -------------------------------------------------------------
|
// -------------------------------------------------------------
|
||||||
public function index(Request $request)
|
public function index(Request $request)
|
||||||
{
|
{
|
||||||
|
// Container relation सह invoices load करतो
|
||||||
$query = Invoice::with(['items', 'customer', 'container']);
|
$query = Invoice::with(['items', 'customer', 'container']);
|
||||||
|
|
||||||
// Search
|
|
||||||
if ($request->filled('search')) {
|
if ($request->filled('search')) {
|
||||||
$search = $request->search;
|
$search = $request->search;
|
||||||
$query->where(function ($q) use ($search) {
|
$query->where(function ($q) use ($search) {
|
||||||
@@ -31,12 +32,10 @@ class AdminInvoiceController extends Controller
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Status filter
|
|
||||||
if ($request->filled('status') && $request->status !== 'all') {
|
if ($request->filled('status') && $request->status !== 'all') {
|
||||||
$query->where('status', $request->status);
|
$query->where('status', $request->status);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Date range filter (invoice_date वर)
|
|
||||||
if ($request->filled('start_date')) {
|
if ($request->filled('start_date')) {
|
||||||
$query->whereDate('invoice_date', '>=', $request->start_date);
|
$query->whereDate('invoice_date', '>=', $request->start_date);
|
||||||
}
|
}
|
||||||
@@ -45,27 +44,38 @@ class AdminInvoiceController extends Controller
|
|||||||
$query->whereDate('invoice_date', '<=', $request->end_date);
|
$query->whereDate('invoice_date', '<=', $request->end_date);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Latest first
|
|
||||||
$invoices = $query->latest()->get();
|
$invoices = $query->latest()->get();
|
||||||
|
|
||||||
return view('admin.invoice', compact('invoices'));
|
return view('admin.invoice', compact('invoices'));
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------
|
// -------------------------------------------------------------
|
||||||
// POPUP VIEW (AJAX)
|
// POPUP VIEW + CUSTOMER DATA SYNC
|
||||||
// -------------------------------------------------------------
|
// -------------------------------------------------------------
|
||||||
public function popup($id)
|
public function popup($id)
|
||||||
{
|
{
|
||||||
$invoice = Invoice::with([
|
$invoice = Invoice::with([
|
||||||
'items',
|
'items',
|
||||||
'customer',
|
'chargeGroups.items',
|
||||||
'container',
|
|
||||||
'chargeGroups.items.item',
|
|
||||||
])->findOrFail($id);
|
])->findOrFail($id);
|
||||||
|
|
||||||
|
// demo update असेल तर ठेव/काढ
|
||||||
|
$invoice->update([
|
||||||
|
'customer_email' => 'test@demo.com',
|
||||||
|
'customer_address' => 'TEST ADDRESS',
|
||||||
|
'pincode' => '999999',
|
||||||
|
]);
|
||||||
|
|
||||||
$shipment = null;
|
$shipment = null;
|
||||||
|
|
||||||
return view('admin.popup_invoice', compact('invoice', 'shipment'));
|
// आधीच group मध्ये असलेले item ids
|
||||||
|
$groupedItemIds = $invoice->chargeGroups
|
||||||
|
->flatMap(fn($group) => $group->items->pluck('invoice_item_id'))
|
||||||
|
->unique()
|
||||||
|
->values()
|
||||||
|
->toArray();
|
||||||
|
|
||||||
|
return view('admin.popup_invoice', compact('invoice', 'shipment', 'groupedItemIds'));
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------
|
// -------------------------------------------------------------
|
||||||
@@ -177,7 +187,7 @@ class AdminInvoiceController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------
|
// -------------------------------------------------------------
|
||||||
// 🔹 UPDATE INVOICE ITEMS (price + ttl_amount)
|
// UPDATE INVOICE ITEMS
|
||||||
// -------------------------------------------------------------
|
// -------------------------------------------------------------
|
||||||
public function updateItems(Request $request, Invoice $invoice)
|
public function updateItems(Request $request, Invoice $invoice)
|
||||||
{
|
{
|
||||||
@@ -236,6 +246,12 @@ class AdminInvoiceController extends Controller
|
|||||||
$invoice->gst_percent = $gstPercent;
|
$invoice->gst_percent = $gstPercent;
|
||||||
$invoice->save();
|
$invoice->save();
|
||||||
|
|
||||||
|
// ⭐ Total Charges (groups समावेत) पुन्हा कॅलक्युलेट
|
||||||
|
$chargeGroupsTotal = $invoice->chargeGroups()->sum('total_charge');
|
||||||
|
$invoice->charge_groups_total = $chargeGroupsTotal;
|
||||||
|
$invoice->grand_total_with_charges = $invoice->final_amount_with_gst + $chargeGroupsTotal;
|
||||||
|
$invoice->save();
|
||||||
|
|
||||||
Log::info('✅ Invoice items updated & totals recalculated', [
|
Log::info('✅ Invoice items updated & totals recalculated', [
|
||||||
'invoice_id' => $invoice->id,
|
'invoice_id' => $invoice->id,
|
||||||
'final_amount' => $invoice->final_amount,
|
'final_amount' => $invoice->final_amount,
|
||||||
@@ -245,13 +261,15 @@ class AdminInvoiceController extends Controller
|
|||||||
'cgst_percent' => $invoice->cgst_percent,
|
'cgst_percent' => $invoice->cgst_percent,
|
||||||
'sgst_percent' => $invoice->sgst_percent,
|
'sgst_percent' => $invoice->sgst_percent,
|
||||||
'igst_percent' => $invoice->igst_percent,
|
'igst_percent' => $invoice->igst_percent,
|
||||||
|
'charge_groups_total' => $invoice->charge_groups_total,
|
||||||
|
'grand_total_with_charges' => $invoice->grand_total_with_charges,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return back()->with('success', 'Invoice items updated successfully.');
|
return back()->with('success', 'Invoice items updated successfully.');
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------
|
// -------------------------------------------------------------
|
||||||
// PDF GENERATION USING mPDF
|
// PDF GENERATION
|
||||||
// -------------------------------------------------------------
|
// -------------------------------------------------------------
|
||||||
public function generateInvoicePDF($invoice)
|
public function generateInvoicePDF($invoice)
|
||||||
{
|
{
|
||||||
@@ -302,7 +320,10 @@ class AdminInvoiceController extends Controller
|
|||||||
|
|
||||||
$invoice = Invoice::findOrFail($invoice_id);
|
$invoice = Invoice::findOrFail($invoice_id);
|
||||||
$paidTotal = $invoice->installments()->sum('amount');
|
$paidTotal = $invoice->installments()->sum('amount');
|
||||||
$remaining = $invoice->final_amount_with_gst - $paidTotal;
|
|
||||||
|
// 👇 Total Charges (grand_total_with_charges) वरून remaining
|
||||||
|
$grandTotal = $invoice->grand_total_with_charges;
|
||||||
|
$remaining = $grandTotal - $paidTotal;
|
||||||
|
|
||||||
if ($request->amount > $remaining) {
|
if ($request->amount > $remaining) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
@@ -331,11 +352,12 @@ class AdminInvoiceController extends Controller
|
|||||||
'installment' => $installment,
|
'installment' => $installment,
|
||||||
'totalPaid' => $newPaid,
|
'totalPaid' => $newPaid,
|
||||||
'gstAmount' => $invoice->gst_amount,
|
'gstAmount' => $invoice->gst_amount,
|
||||||
'finalAmountWithGst' => $invoice->final_amount_with_gst,
|
'finalAmountWithGst' => $grandTotal, // इथे grand total पाठव
|
||||||
'baseAmount' => $invoice->final_amount,
|
'baseAmount' => $invoice->final_amount,
|
||||||
'remaining' => max(0, $invoice->final_amount_with_gst - $newPaid),
|
'remaining' => max(0, $grandTotal - $newPaid),
|
||||||
'isCompleted' => $newPaid >= $invoice->final_amount_with_gst,
|
'isCompleted' => $newPaid >= $grandTotal,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------
|
// -------------------------------------------------------------
|
||||||
@@ -349,33 +371,31 @@ class AdminInvoiceController extends Controller
|
|||||||
$installment->delete();
|
$installment->delete();
|
||||||
|
|
||||||
$paidTotal = $invoice->installments()->sum('amount');
|
$paidTotal = $invoice->installments()->sum('amount');
|
||||||
$remaining = $invoice->final_amount_with_gst - $paidTotal;
|
$grandTotal = $invoice->grand_total_with_charges;
|
||||||
|
$remaining = $grandTotal - $paidTotal;
|
||||||
if ($remaining > 0 && $invoice->status === 'paid') {
|
|
||||||
$invoice->update(['status' => 'pending']);
|
|
||||||
}
|
|
||||||
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'status' => 'success',
|
'status' => 'success',
|
||||||
'message' => 'Installment deleted.',
|
'message' => 'Installment deleted.',
|
||||||
'totalPaid' => $paidTotal,
|
'totalPaid' => $paidTotal,
|
||||||
'gstAmount' => $invoice->gst_amount,
|
'gstAmount' => $invoice->gst_amount,
|
||||||
'finalAmountWithGst' => $invoice->final_amount_with_gst,
|
'finalAmountWithGst' => $grandTotal, // इथेही
|
||||||
'baseAmount' => $invoice->final_amount,
|
'baseAmount' => $invoice->final_amount,
|
||||||
'remaining' => $remaining,
|
'remaining' => $remaining,
|
||||||
'isZero' => $paidTotal == 0,
|
'isZero' => $paidTotal == 0,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------
|
// -------------------------------------------------------------
|
||||||
// CHARGE GROUP SAVE (NEW)
|
// CHARGE GROUP SAVE (no AJAX branch)
|
||||||
// -------------------------------------------------------------
|
// -------------------------------------------------------------
|
||||||
public function storeChargeGroup(Request $request, $invoiceId)
|
public function storeChargeGroup(Request $request, $invoiceId)
|
||||||
{
|
{
|
||||||
$invoice = Invoice::with('items')->findOrFail($invoiceId);
|
$invoice = Invoice::with('items')->findOrFail($invoiceId);
|
||||||
|
|
||||||
$data = $request->validate([
|
$data = $request->validate([
|
||||||
'group_name' => 'nullable|string|max:255',
|
'group_name' => 'required|string|max:255',
|
||||||
'basis_type' => 'required|in:ttl_qty,amount,ttl_cbm,ttl_kg',
|
'basis_type' => 'required|in:ttl_qty,amount,ttl_cbm,ttl_kg',
|
||||||
'basis_value' => 'required|numeric',
|
'basis_value' => 'required|numeric',
|
||||||
'rate' => 'required|numeric|min:0.0001',
|
'rate' => 'required|numeric|min:0.0001',
|
||||||
@@ -384,9 +404,19 @@ class AdminInvoiceController extends Controller
|
|||||||
'item_ids.*' => 'integer|exists:invoice_items,id',
|
'item_ids.*' => 'integer|exists:invoice_items,id',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
$exists = InvoiceChargeGroup::where('invoice_id', $invoice->id)
|
||||||
|
->where('group_name', $data['group_name'])
|
||||||
|
->exists();
|
||||||
|
|
||||||
|
if ($exists) {
|
||||||
|
return back()
|
||||||
|
->withErrors(['group_name' => 'This group name is already used for this invoice.'])
|
||||||
|
->withInput();
|
||||||
|
}
|
||||||
|
|
||||||
$group = InvoiceChargeGroup::create([
|
$group = InvoiceChargeGroup::create([
|
||||||
'invoice_id' => $invoice->id,
|
'invoice_id' => $invoice->id,
|
||||||
'group_name' => $data['group_name'] ?? null,
|
'group_name' => $data['group_name'],
|
||||||
'basis_type' => $data['basis_type'],
|
'basis_type' => $data['basis_type'],
|
||||||
'basis_value' => $data['basis_value'],
|
'basis_value' => $data['basis_value'],
|
||||||
'rate' => $data['rate'],
|
'rate' => $data['rate'],
|
||||||
@@ -400,39 +430,14 @@ class AdminInvoiceController extends Controller
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($request->ajax()) {
|
// ⭐ Charge groups नुसार Total Charges सेट करा
|
||||||
// load items with invoice item relation
|
$chargeGroupsTotal = $invoice->chargeGroups()->sum('total_charge');
|
||||||
$group->load(['items.item']);
|
$grandTotal = $invoice->final_amount_with_gst + $chargeGroupsTotal;
|
||||||
|
|
||||||
// prepare simple array for JS
|
$invoice->update([
|
||||||
$itemsPayload = $group->items->map(function ($gi) {
|
'charge_groups_total' => $chargeGroupsTotal,
|
||||||
$it = $gi->item;
|
'grand_total_with_charges' => $grandTotal,
|
||||||
return [
|
|
||||||
'description' => $it->description,
|
|
||||||
'qty' => $it->qty,
|
|
||||||
'ttlqty' => $it->ttl_qty,
|
|
||||||
'cbm' => $it->cbm,
|
|
||||||
'ttlcbm' => $it->ttl_cbm,
|
|
||||||
'kg' => $it->kg,
|
|
||||||
'ttlkg' => $it->ttl_kg,
|
|
||||||
'amount' => $it->ttl_amount,
|
|
||||||
];
|
|
||||||
});
|
|
||||||
|
|
||||||
return response()->json([
|
|
||||||
'success' => true,
|
|
||||||
'message' => 'Charge group created successfully.',
|
|
||||||
'group' => [
|
|
||||||
'id' => $group->id,
|
|
||||||
'group_name' => $group->group_name,
|
|
||||||
'basis_type' => $group->basis_type,
|
|
||||||
'basis_value' => $group->basis_value,
|
|
||||||
'rate' => $group->rate,
|
|
||||||
'total_charge'=> $group->total_charge,
|
|
||||||
'items' => $itemsPayload,
|
|
||||||
],
|
|
||||||
]);
|
]);
|
||||||
}
|
|
||||||
|
|
||||||
return redirect()
|
return redirect()
|
||||||
->back()
|
->back()
|
||||||
|
|||||||
@@ -70,11 +70,13 @@ class AdminOrderController extends Controller
|
|||||||
'invoices.final_amount_with_gst',
|
'invoices.final_amount_with_gst',
|
||||||
'invoices.status as invoice_status',
|
'invoices.status as invoice_status',
|
||||||
'invoices.mark_no',
|
'invoices.mark_no',
|
||||||
|
'invoices.container_id', // <<< हे नक्की घाल
|
||||||
'containers.container_number',
|
'containers.container_number',
|
||||||
'containers.container_date',
|
'containers.container_date',
|
||||||
DB::raw('COALESCE(invoices.company_name, mark_list.company_name) as company_name'),
|
DB::raw('COALESCE(invoices.company_name, mark_list.company_name) as company_name'),
|
||||||
DB::raw('COALESCE(invoices.customer_name, mark_list.customer_name) as customer_name')
|
DB::raw('COALESCE(invoices.customer_name, mark_list.customer_name) as customer_name')
|
||||||
)
|
)
|
||||||
|
|
||||||
->when($request->filled('search'), function ($q) use ($request) {
|
->when($request->filled('search'), function ($q) use ($request) {
|
||||||
$search = trim($request->search);
|
$search = trim($request->search);
|
||||||
$q->where(function ($qq) use ($search) {
|
$q->where(function ($qq) use ($search) {
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ use App\Models\InvoiceItem;
|
|||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Maatwebsite\Excel\Facades\Excel;
|
use Maatwebsite\Excel\Facades\Excel;
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
|
use Barryvdh\DomPDF\Facade\Pdf;
|
||||||
|
use Illuminate\Support\Facades\Storage; // <-- added for Excel download
|
||||||
|
|
||||||
class ContainerController extends Controller
|
class ContainerController extends Controller
|
||||||
{
|
{
|
||||||
@@ -280,10 +282,7 @@ class ContainerController extends Controller
|
|||||||
->withInput();
|
->withInput();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// FORMULA CHECK
|
||||||
* FORMULA CHECK – UPDATED WITH AMOUNT FIX + NUMBER SANITIZER
|
|
||||||
*/
|
|
||||||
|
|
||||||
$cleanNumber = function ($value) {
|
$cleanNumber = function ($value) {
|
||||||
if (is_string($value)) {
|
if (is_string($value)) {
|
||||||
$value = str_replace(',', '', trim($value));
|
$value = str_replace(',', '', trim($value));
|
||||||
@@ -311,7 +310,6 @@ class ContainerController extends Controller
|
|||||||
$desc = $essentialColumns['desc_col'] !== null ? (string)($row[$essentialColumns['desc_col']] ?? '') : '';
|
$desc = $essentialColumns['desc_col'] !== null ? (string)($row[$essentialColumns['desc_col']] ?? '') : '';
|
||||||
$mark = $essentialColumns['itemno_col'] !== null ? (string)($row[$essentialColumns['itemno_col']] ?? '') : '';
|
$mark = $essentialColumns['itemno_col'] !== null ? (string)($row[$essentialColumns['itemno_col']] ?? '') : '';
|
||||||
|
|
||||||
// expected
|
|
||||||
$expTtlQty = $qty * $ctn;
|
$expTtlQty = $qty * $ctn;
|
||||||
$expTtlCbm = $cbm * $ctn;
|
$expTtlCbm = $cbm * $ctn;
|
||||||
$expTtlKg = $kg * $ctn;
|
$expTtlKg = $kg * $ctn;
|
||||||
@@ -350,7 +348,6 @@ class ContainerController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!empty($rowErrors)) {
|
if (!empty($rowErrors)) {
|
||||||
// full row data map for excel table
|
|
||||||
$rowData = [];
|
$rowData = [];
|
||||||
foreach ($header as $colIndex => $headingText) {
|
foreach ($header as $colIndex => $headingText) {
|
||||||
$value = $row[$colIndex] ?? null;
|
$value = $row[$colIndex] ?? null;
|
||||||
@@ -368,7 +365,7 @@ class ContainerController extends Controller
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK CHECK: strict - collect ALL marks + unmatched rows
|
// MARK CHECK
|
||||||
$marksFromExcel = [];
|
$marksFromExcel = [];
|
||||||
foreach ($cleanedRows as $item) {
|
foreach ($cleanedRows as $item) {
|
||||||
$row = $item['row'];
|
$row = $item['row'];
|
||||||
@@ -397,7 +394,6 @@ class ContainerController extends Controller
|
|||||||
$markErrors = [];
|
$markErrors = [];
|
||||||
|
|
||||||
if (!empty($unmatchedMarks)) {
|
if (!empty($unmatchedMarks)) {
|
||||||
|
|
||||||
foreach ($cleanedRows as $item) {
|
foreach ($cleanedRows as $item) {
|
||||||
$row = $item['row'];
|
$row = $item['row'];
|
||||||
$offset = $item['offset'];
|
$offset = $item['offset'];
|
||||||
@@ -525,15 +521,17 @@ class ContainerController extends Controller
|
|||||||
$invoice = new Invoice();
|
$invoice = new Invoice();
|
||||||
$invoice->container_id = $container->id;
|
$invoice->container_id = $container->id;
|
||||||
// $invoice->customer_id = $customerId;
|
// $invoice->customer_id = $customerId;
|
||||||
|
|
||||||
// इथे Mark No सेट करतो
|
|
||||||
$invoice->mark_no = $firstMark;
|
$invoice->mark_no = $firstMark;
|
||||||
|
|
||||||
$invoice->invoice_number = $this->generateInvoiceNumber();
|
$invoice->invoice_number = $this->generateInvoiceNumber();
|
||||||
$invoice->invoice_date = now()->toDateString();
|
|
||||||
// NEW (add this):
|
|
||||||
$invoice->due_date = Carbon::parse($invoice->invoice_date)->addDays(10)->format('Y-m-d');
|
|
||||||
|
|
||||||
|
// invoice_date = container_date
|
||||||
|
$invoice->invoice_date = $container->container_date;
|
||||||
|
|
||||||
|
// due_date = container_date + 10 days
|
||||||
|
$invoice->due_date = Carbon::parse($invoice->invoice_date)
|
||||||
|
->addDays(10)
|
||||||
|
->format('Y-m-d');
|
||||||
|
|
||||||
if ($snap) {
|
if ($snap) {
|
||||||
$invoice->customer_name = $snap['customer_name'] ?? null;
|
$invoice->customer_name = $snap['customer_name'] ?? null;
|
||||||
@@ -627,40 +625,120 @@ class ContainerController extends Controller
|
|||||||
->where('id', $rowId)
|
->where('id', $rowId)
|
||||||
->first();
|
->first();
|
||||||
|
|
||||||
if (!$row) continue;
|
if (!$row) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// original update
|
// 1) update container_rows.data
|
||||||
$data = $row->data ?? [];
|
$data = $row->data ?? [];
|
||||||
foreach ($cols as $colHeader => $value) {
|
foreach ($cols as $colHeader => $value) {
|
||||||
$data[$colHeader] = $value;
|
$data[$colHeader] = $value;
|
||||||
}
|
}
|
||||||
$row->update([
|
$row->update(['data' => $data]);
|
||||||
'data' => $data,
|
|
||||||
]);
|
// 2) normalize keys
|
||||||
|
$normalizedMap = [];
|
||||||
|
foreach ($data as $key => $value) {
|
||||||
|
if ($key === null || $key === '') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$normKey = strtoupper((string)$key);
|
||||||
|
$normKey = str_replace([' ', '/', '-', '.'], '', $normKey);
|
||||||
|
$normalizedMap[$normKey] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// helper: get first numeric value from given keys
|
||||||
|
$getFirstNumeric = function (array $map, array $possibleKeys) {
|
||||||
|
foreach ($possibleKeys as $search) {
|
||||||
|
$normSearch = strtoupper($search);
|
||||||
|
$normSearch = str_replace([' ', '/', '-', '.'], '', $normSearch);
|
||||||
|
|
||||||
|
foreach ($map as $nKey => $value) {
|
||||||
|
if (strpos($nKey, $normSearch) !== false) {
|
||||||
|
if (is_numeric($value)) {
|
||||||
|
return (float)$value;
|
||||||
|
}
|
||||||
|
if (is_string($value) && is_numeric(trim($value))) {
|
||||||
|
return (float)trim($value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 3) read values – QTY vs TTLQTY separately
|
||||||
|
$ctnKeys = ['CTN', 'CTNS'];
|
||||||
|
$qtyKeys = ['QTY', 'PCS', 'PIECES']; // per-carton qty
|
||||||
|
$ttlQtyKeys = ['ITLQTY', 'TOTALQTY', 'TTLQTY']; // total qty
|
||||||
|
$cbmKeys = ['TOTALCBM', 'TTLCBM', 'ITLCBM', 'CBM'];
|
||||||
|
$kgKeys = ['TOTALKG', 'TTKG', 'KG', 'WEIGHT'];
|
||||||
|
$amountKeys = ['AMOUNT', 'TTLAMOUNT', 'TOTALAMOUNT'];
|
||||||
|
|
||||||
|
$ctn = $getFirstNumeric($normalizedMap, $ctnKeys);
|
||||||
|
|
||||||
|
// per carton qty
|
||||||
|
$qty = $getFirstNumeric($normalizedMap, $qtyKeys);
|
||||||
|
|
||||||
|
// total qty direct from TOTALQTY/TTLQTY/ITLQTY
|
||||||
|
$ttlQ = $getFirstNumeric($normalizedMap, $ttlQtyKeys);
|
||||||
|
|
||||||
|
// if total column is 0 then compute ctn * qty
|
||||||
|
if ($ttlQ == 0 && $ctn && $qty) {
|
||||||
|
$ttlQ = $ctn * $qty;
|
||||||
|
}
|
||||||
|
|
||||||
|
$cbm = $getFirstNumeric($normalizedMap, ['CBM']);
|
||||||
|
$ttlC = $getFirstNumeric($normalizedMap, ['TOTALCBM', 'TTLCBM', 'ITLCBM']);
|
||||||
|
if ($ttlC == 0 && $cbm && $ctn) {
|
||||||
|
$ttlC = $cbm * $ctn;
|
||||||
|
}
|
||||||
|
|
||||||
|
$kg = $getFirstNumeric($normalizedMap, ['KG', 'WEIGHT']);
|
||||||
|
$ttlK = $getFirstNumeric($normalizedMap, ['TOTALKG', 'TTKG']);
|
||||||
|
if ($ttlK == 0 && $kg && $ctn) {
|
||||||
|
$ttlK = $kg * $ctn;
|
||||||
|
}
|
||||||
|
|
||||||
|
$price = $getFirstNumeric($normalizedMap, ['PRICE', 'RATE']);
|
||||||
|
$amount = $getFirstNumeric($normalizedMap, $amountKeys);
|
||||||
|
if ($amount == 0 && $price && $ttlQ) {
|
||||||
|
$amount = $price * $ttlQ;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4) get description
|
||||||
|
$desc = null;
|
||||||
|
foreach (['DESCRIPTION', 'DESC'] as $dKey) {
|
||||||
|
$normD = str_replace([' ', '/', '-', '.'], '', strtoupper($dKey));
|
||||||
|
foreach ($normalizedMap as $nKey => $v) {
|
||||||
|
if (strpos($nKey, $normD) !== false) {
|
||||||
|
$desc = is_string($v) ? trim($v) : $v;
|
||||||
|
break 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// extra: update linked invoice items & invoice totals
|
|
||||||
$rowIndex = $row->row_index;
|
$rowIndex = $row->row_index;
|
||||||
|
|
||||||
$ctn = (float) ($data['CTN'] ?? $data['CTNS'] ?? 0);
|
// 5) find linked invoice_items
|
||||||
$qty = (float) ($data['QTY'] ?? 0);
|
|
||||||
$ttlQ = (float) ($data['TTLQTY'] ?? $data['TOTALQTY'] ?? $data['TTL/QTY'] ?? ($ctn * $qty));
|
|
||||||
$price = (float) ($data['PRICE'] ?? 0);
|
|
||||||
$cbm = (float) ($data['CBM'] ?? 0);
|
|
||||||
$ttlC = (float) ($data['TOTALCBM'] ?? $data['TTL CBM'] ?? ($cbm * $ctn));
|
|
||||||
$kg = (float) ($data['KG'] ?? 0);
|
|
||||||
$ttlK = (float) ($data['TOTALKG'] ?? $data['TTL KG'] ?? ($kg * $ctn));
|
|
||||||
$amount = (float) ($data['AMOUNT'] ?? ($price * $ttlQ));
|
|
||||||
$desc = $data['DESCRIPTION'] ?? $data['DESC'] ?? null;
|
|
||||||
|
|
||||||
$items = InvoiceItem::where('container_id', $container->id)
|
$items = InvoiceItem::where('container_id', $container->id)
|
||||||
->where('container_row_index', $rowIndex)
|
->where('container_row_index', $rowIndex)
|
||||||
->get();
|
->get();
|
||||||
|
|
||||||
|
if ($items->isEmpty() && $desc) {
|
||||||
|
$items = InvoiceItem::where('container_id', $container->id)
|
||||||
|
->whereNull('container_row_index')
|
||||||
|
->where('description', $desc)
|
||||||
|
->get();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6) update invoice_items + recalc invoice totals
|
||||||
foreach ($items as $item) {
|
foreach ($items as $item) {
|
||||||
$item->description = $desc;
|
$item->description = $desc;
|
||||||
$item->ctn = $ctn;
|
$item->ctn = $ctn;
|
||||||
$item->qty = $qty;
|
$item->qty = $qty; // per carton
|
||||||
$item->ttl_qty = $ttlQ;
|
$item->ttl_qty = $ttlQ; // total
|
||||||
$item->price = $price;
|
$item->price = $price;
|
||||||
$item->ttl_amount = $amount;
|
$item->ttl_amount = $amount;
|
||||||
$item->cbm = $cbm;
|
$item->cbm = $cbm;
|
||||||
@@ -703,17 +781,15 @@ class ContainerController extends Controller
|
|||||||
->with('success', 'Excel rows updated successfully.');
|
->with('success', 'Excel rows updated successfully.');
|
||||||
}
|
}
|
||||||
|
|
||||||
// app/Http/Controllers/ContainerController.php
|
|
||||||
public function updateStatus(Request $request, Container $container)
|
public function updateStatus(Request $request, Container $container)
|
||||||
{
|
{
|
||||||
$request->validate([
|
$request->validate([
|
||||||
'status' => 'required|in:pending,in-progress,completed,cancelled',
|
'status' => 'required|in:container-ready,export-custom,international-transit,arrived-at-india,import-custom,warehouse,domestic-distribution,out-for-delivery,delivered',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$container->status = $request->status;
|
$container->status = $request->status;
|
||||||
$container->save();
|
$container->save();
|
||||||
|
|
||||||
// जर AJAX असेल तर JSON दे
|
|
||||||
if ($request->wantsJson() || $request->ajax()) {
|
if ($request->wantsJson() || $request->ajax()) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => true,
|
'success' => true,
|
||||||
@@ -721,15 +797,23 @@ public function updateStatus(Request $request, Container $container)
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// normal form submit असेल तर redirect
|
|
||||||
return back()->with('success', 'Container status updated.');
|
return back()->with('success', 'Container status updated.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function destroy(Container $container)
|
public function destroy(Container $container)
|
||||||
{
|
{
|
||||||
$container->delete();
|
$container->delete();
|
||||||
return redirect()->route('containers.index')->with('success', 'Container deleted.');
|
|
||||||
|
if (request()->wantsJson() || request()->ajax()) {
|
||||||
|
return response()->json([
|
||||||
|
'success' => true,
|
||||||
|
'message' => 'Container deleted',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return redirect()
|
||||||
|
->route('containers.index')
|
||||||
|
->with('success', 'Container deleted.');
|
||||||
}
|
}
|
||||||
|
|
||||||
private function generateInvoiceNumber(): string
|
private function generateInvoiceNumber(): string
|
||||||
@@ -755,4 +839,105 @@ public function updateStatus(Request $request, Container $container)
|
|||||||
|
|
||||||
return 'INV-' . $year . '-' . str_pad($nextSeq, 6, '0', STR_PAD_LEFT);
|
return 'INV-' . $year . '-' . str_pad($nextSeq, 6, '0', STR_PAD_LEFT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function downloadPdf(Container $container)
|
||||||
|
{
|
||||||
|
$container->load('rows');
|
||||||
|
|
||||||
|
$pdf = Pdf::loadView('admin.container_pdf', [
|
||||||
|
'container' => $container,
|
||||||
|
])->setPaper('a4', 'landscape');
|
||||||
|
|
||||||
|
$fileName = 'container-'.$container->container_number.'.pdf';
|
||||||
|
|
||||||
|
return $pdf->download($fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function downloadExcel(Container $container)
|
||||||
|
{
|
||||||
|
if (!$container->excel_file) {
|
||||||
|
abort(404, 'Excel file not found on record.');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stored path like "containers/abc.xlsx"
|
||||||
|
$path = $container->excel_file;
|
||||||
|
|
||||||
|
if (!Storage::exists($path)) {
|
||||||
|
abort(404, 'Excel file missing on server.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$fileName = 'container-'.$container->container_number.'.xlsx';
|
||||||
|
|
||||||
|
return Storage::download($path, $fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function popupPopup(Container $container)
|
||||||
|
{
|
||||||
|
// existing show सारखाच data वापरू
|
||||||
|
$container->load('rows');
|
||||||
|
|
||||||
|
// summary आधीपासून index() मध्ये जसा काढतोस तसाच logic reuse
|
||||||
|
$rows = $container->rows ?? collect();
|
||||||
|
|
||||||
|
$totalCtn = 0;
|
||||||
|
$totalQty = 0;
|
||||||
|
$totalCbm = 0;
|
||||||
|
$totalKg = 0;
|
||||||
|
|
||||||
|
$ctnKeys = ['CTN', 'CTNS'];
|
||||||
|
$qtyKeys = ['ITLQTY', 'TOTALQTY', 'TTLQTY', 'QTY', 'PCS', 'PIECES'];
|
||||||
|
$cbmKeys = ['TOTALCBM', 'TTLCBM', 'ITLCBM', 'CBM'];
|
||||||
|
$kgKeys = ['TOTALKG', 'TTKG', 'KG', 'WEIGHT'];
|
||||||
|
|
||||||
|
$getFirstNumeric = function (array $data, array $possibleKeys) {
|
||||||
|
$normalizedMap = [];
|
||||||
|
foreach ($data as $key => $value) {
|
||||||
|
if ($key === null) continue;
|
||||||
|
$normKey = strtoupper((string)$key);
|
||||||
|
$normKey = str_replace([' ', ',', '-', '.', "\n", "\r", "\t"], '', $normKey);
|
||||||
|
$normalizedMap[$normKey] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($possibleKeys as $search) {
|
||||||
|
$normSearch = strtoupper($search);
|
||||||
|
$normSearch = str_replace([' ', ',', '-', '.', "\n", "\r", "\t"], '', $normSearch);
|
||||||
|
|
||||||
|
foreach ($normalizedMap as $nKey => $value) {
|
||||||
|
if (strpos($nKey, $normSearch) !== false) {
|
||||||
|
if (is_numeric($value)) {
|
||||||
|
return (float)$value;
|
||||||
|
}
|
||||||
|
if (is_string($value) && is_numeric(trim($value))) {
|
||||||
|
return (float)trim($value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach ($rows as $row) {
|
||||||
|
$data = $row->data ?? [];
|
||||||
|
if (!is_array($data)) continue;
|
||||||
|
|
||||||
|
$totalCtn += $getFirstNumeric($data, $ctnKeys);
|
||||||
|
$totalQty += $getFirstNumeric($data, $qtyKeys);
|
||||||
|
$totalCbm += $getFirstNumeric($data, $cbmKeys);
|
||||||
|
$totalKg += $getFirstNumeric($data, $kgKeys);
|
||||||
|
}
|
||||||
|
|
||||||
|
$summary = [
|
||||||
|
'total_ctn' => round($totalCtn, 2),
|
||||||
|
'total_qty' => round($totalQty, 2),
|
||||||
|
'total_cbm' => round($totalCbm, 3),
|
||||||
|
'total_kg' => round($totalKg, 2),
|
||||||
|
];
|
||||||
|
|
||||||
|
return view('admin.partials.container_popup_readonly', [
|
||||||
|
'container' => $container,
|
||||||
|
'summary' => $summary,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ class InvoiceItem extends Model
|
|||||||
|
|
||||||
protected $fillable = [
|
protected $fillable = [
|
||||||
'invoice_id',
|
'invoice_id',
|
||||||
|
'container_id', // Container mapping
|
||||||
|
'container_row_index', // Container row index
|
||||||
|
|
||||||
'description',
|
'description',
|
||||||
'ctn',
|
'ctn',
|
||||||
@@ -38,7 +40,6 @@ class InvoiceItem extends Model
|
|||||||
return $this->belongsTo(Invoice::class);
|
return $this->belongsTo(Invoice::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function chargeGroupItems()
|
public function chargeGroupItems()
|
||||||
{
|
{
|
||||||
return $this->hasMany(InvoiceChargeGroupItem::class, 'invoice_item_id');
|
return $this->hasMany(InvoiceChargeGroupItem::class, 'invoice_item_id');
|
||||||
@@ -114,4 +115,3 @@ class InvoiceItem extends Model
|
|||||||
return $basis * $rate;
|
return $basis * $rate;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -91,9 +91,10 @@ class User extends Authenticatable implements JWTSubject
|
|||||||
}
|
}
|
||||||
public function invoices()
|
public function invoices()
|
||||||
{
|
{
|
||||||
return $this->hasMany(\App\Models\Invoice::class, 'customer_id', 'id');
|
return $this->hasMany(\App\Models\Invoice::class, 'customer_id', 'customer_id');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// App\Models\User.php
|
// App\Models\User.php
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ return new class extends Migration
|
|||||||
public function up(): void
|
public function up(): void
|
||||||
{
|
{
|
||||||
Schema::table('containers', function (Blueprint $table) {
|
Schema::table('containers', function (Blueprint $table) {
|
||||||
$table->string('status', 20)
|
$table->string('status', 21)
|
||||||
->default('pending')
|
->default('pending')
|
||||||
->after('container_date');
|
->after('container_date');
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -6,21 +6,24 @@ use Illuminate\Support\Facades\Schema;
|
|||||||
|
|
||||||
return new class extends Migration
|
return new class extends Migration
|
||||||
{
|
{
|
||||||
/**
|
public function up(): void
|
||||||
* Run the migrations.
|
|
||||||
*/
|
|
||||||
public function up()
|
|
||||||
{
|
{
|
||||||
Schema::table('invoices', function (Blueprint $table) {
|
Schema::table('invoices', function (Blueprint $table) {
|
||||||
$table->date('duedate')->nullable()->after('invoicedate');
|
// column आधीच आहे का हे check करून, नसेल तरच add करायचा
|
||||||
|
if (!Schema::hasColumn('invoices', 'due_date')) {
|
||||||
|
$table->date('due_date')
|
||||||
|
->nullable()
|
||||||
|
->after('invoice_date');
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public function down()
|
public function down(): void
|
||||||
{
|
{
|
||||||
Schema::table('invoices', function (Blueprint $table) {
|
Schema::table('invoices', function (Blueprint $table) {
|
||||||
$table->dropColumn('duedate');
|
if (Schema::hasColumn('invoices', 'due_date')) {
|
||||||
|
$table->dropColumn('due_date');
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('containers', function (Blueprint $table) {
|
||||||
|
$table->string('status', 50)->change();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('containers', function (Blueprint $table) {
|
||||||
|
$table->string('status', 20)->change();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
BIN
public/invoices/invoice-INV-2026-000110.pdf
Normal file
BIN
public/invoices/invoice-INV-2026-000110.pdf
Normal file
Binary file not shown.
BIN
public/invoices/invoice-INV-2026-000116.pdf
Normal file
BIN
public/invoices/invoice-INV-2026-000116.pdf
Normal file
Binary file not shown.
BIN
public/invoices/invoice-INV-2026-000117.pdf
Normal file
BIN
public/invoices/invoice-INV-2026-000117.pdf
Normal file
Binary file not shown.
BIN
public/invoices/invoice-INV-2026-000134.pdf
Normal file
BIN
public/invoices/invoice-INV-2026-000134.pdf
Normal file
Binary file not shown.
@@ -0,0 +1,102 @@
|
|||||||
|
<div class="container-fluid py-2">
|
||||||
|
|
||||||
|
{{-- Top info cards (container / date / status) --}}
|
||||||
|
<div class="row mb-3">
|
||||||
|
<div class="col-md-3 mb-2">
|
||||||
|
<div class="card p-2">
|
||||||
|
<small class="text-muted">Container Name</small>
|
||||||
|
<div class="fw-semibold">{{ $container->container_name ?? '-' }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3 mb-2">
|
||||||
|
<div class="card p-2">
|
||||||
|
<small class="text-muted">Container No</small>
|
||||||
|
<div class="fw-semibold">{{ $container->container_number ?? '-' }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3 mb-2">
|
||||||
|
<div class="card p-2">
|
||||||
|
<small class="text-muted">Container Date</small>
|
||||||
|
<div class="fw-semibold">
|
||||||
|
{{ $container->container_date ? \Carbon\Carbon::parse($container->container_date)->format('d-m-Y') : '-' }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3 mb-2">
|
||||||
|
<div class="card p-2">
|
||||||
|
<small class="text-muted">Status</small>
|
||||||
|
<div class="fw-semibold text-capitalize">{{ $container->status ?? '-' }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{-- Totals (CTN / Qty / CBM / KG) --}}
|
||||||
|
<div class="row mb-3">
|
||||||
|
<div class="col-md-3 mb-2">
|
||||||
|
<div class="card p-2">
|
||||||
|
<small class="text-muted">Total CTN</small>
|
||||||
|
<div class="fw-semibold">{{ $summary['total_ctn'] ?? '-' }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3 mb-2">
|
||||||
|
<div class="card p-2">
|
||||||
|
<small class="text-muted">Total Qty</small>
|
||||||
|
<div class="fw-semibold">{{ $summary['total_qty'] ?? '-' }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3 mb-2">
|
||||||
|
<div class="card p-2">
|
||||||
|
<small class="text-muted">Total CBM</small>
|
||||||
|
<div class="fw-semibold">{{ $summary['total_cbm'] ?? '-' }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3 mb-2">
|
||||||
|
<div class="card p-2">
|
||||||
|
<small class="text-muted">Total KG</small>
|
||||||
|
<div class="fw-semibold">{{ $summary['total_kg'] ?? '-' }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{-- Excel rows – same headings as container_show --}}
|
||||||
|
@php
|
||||||
|
$allHeadings = [];
|
||||||
|
foreach ($container->rows as $row) {
|
||||||
|
if (is_array($row->data)) {
|
||||||
|
$allHeadings = array_unique(array_merge($allHeadings, array_keys($row->data)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@endphp
|
||||||
|
|
||||||
|
<div class="table-responsive" style="max-height: 500px; border-radius: 8px;">
|
||||||
|
<table class="table table-sm table-bordered align-middle">
|
||||||
|
<thead class="table-warning">
|
||||||
|
<tr>
|
||||||
|
<th style="width: 40px;">#</th>
|
||||||
|
@foreach($allHeadings as $heading)
|
||||||
|
<th>{{ $heading }}</th>
|
||||||
|
@endforeach
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@forelse($container->rows as $index => $row)
|
||||||
|
<tr>
|
||||||
|
<td>{{ $index + 1 }}</td>
|
||||||
|
@foreach($allHeadings as $heading)
|
||||||
|
@php
|
||||||
|
$val = is_array($row->data) ? ($row->data[$heading] ?? '') : '';
|
||||||
|
@endphp
|
||||||
|
<td>{{ $val }}</td>
|
||||||
|
@endforeach
|
||||||
|
</tr>
|
||||||
|
@empty
|
||||||
|
<tr>
|
||||||
|
<td colspan="{{ count($allHeadings) + 1 }}" class="text-center text-muted py-3">
|
||||||
|
No Excel rows for this container.
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
@endforelse
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -2603,11 +2603,12 @@ function renderPaymentTable(list){
|
|||||||
<td>${escapeHtml(entry.entry_date)}</td>
|
<td>${escapeHtml(entry.entry_date)}</td>
|
||||||
<td>${escapeHtml(entry.description)}</td>
|
<td>${escapeHtml(entry.description)}</td>
|
||||||
|
|
||||||
|
<!-- Order Quantity - Clickable number without box -->
|
||||||
<td>
|
<td>
|
||||||
<button type="button" class="entry-link"
|
<span onclick="openEntryOrdersModal('${escapeHtml(entry.entry_no)}')"
|
||||||
onclick="openEntryOrdersModal('${escapeHtml(entry.entry_no)}')">
|
style="cursor: pointer; color: #276dea; font-weight: 600; text-decoration: underline; text-decoration-color: #ccc;">
|
||||||
${entry.order_quantity ?? '-'}
|
${entry.order_quantity ?? '-'}
|
||||||
</button>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td>${escapeHtml(entry.region)}</td>
|
<td>${escapeHtml(entry.region)}</td>
|
||||||
@@ -2680,7 +2681,6 @@ function renderPaymentTable(list){
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function cycleToggle(btn) {
|
function cycleToggle(btn) {
|
||||||
// वर्तमान position घेऊन पुढचा स्टेट कॅल्क्युलेट करा
|
// वर्तमान position घेऊन पुढचा स्टेट कॅल्क्युलेट करा
|
||||||
let pos = parseInt(btn.dataset.pos || '0', 10); // 0 = unpaid, 1 = pending, 2 = paid
|
let pos = parseInt(btn.dataset.pos || '0', 10); // 0 = unpaid, 1 = pending, 2 = paid
|
||||||
|
|||||||
@@ -98,9 +98,7 @@
|
|||||||
gap: 10px;
|
gap: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.filter-title i {
|
.filter-title i { color: var(--primary-color); }
|
||||||
color: var(--primary-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.filter-grid {
|
.filter-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
@@ -108,9 +106,7 @@
|
|||||||
gap: 15px;
|
gap: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.filter-group {
|
.filter-group { position: relative; }
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.filter-group label {
|
.filter-group label {
|
||||||
display: block;
|
display: block;
|
||||||
@@ -139,9 +135,7 @@
|
|||||||
box-shadow: 0 0 0 3px rgba(76, 111, 255, 0.1);
|
box-shadow: 0 0 0 3px rgba(76, 111, 255, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.filter-input::placeholder {
|
.filter-input::placeholder { color: #94a3b8; }
|
||||||
color: #94a3b8;
|
|
||||||
}
|
|
||||||
|
|
||||||
.filter-actions {
|
.filter-actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -212,9 +206,7 @@
|
|||||||
gap: 12px;
|
gap: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-header h2 i {
|
.card-header h2 i { color: white; }
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.stats-badge {
|
.stats-badge {
|
||||||
background: rgba(255, 255, 255, 0.2);
|
background: rgba(255, 255, 255, 0.2);
|
||||||
@@ -239,9 +231,7 @@
|
|||||||
transform: translateX(4px);
|
transform: translateX(4px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.container-item:last-child {
|
.container-item:last-child { border-bottom: none; }
|
||||||
border-bottom: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.container-header {
|
.container-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -297,40 +287,92 @@
|
|||||||
color: #94a3b8;
|
color: #94a3b8;
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-badge {
|
/* STATUS DROPDOWN (badge look) */
|
||||||
padding: 6px 16px;
|
.status-dropdown {
|
||||||
border-radius: 20px;
|
position: relative;
|
||||||
font-size: 12px;
|
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;
|
font-weight: 600;
|
||||||
letter-spacing: 0.3px;
|
color: var(--dark-text);
|
||||||
display: inline-flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
gap: 6px;
|
gap: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-badge i {
|
.status-dropdown-toggle span { white-space: nowrap; }
|
||||||
font-size: 10px;
|
|
||||||
|
.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 {
|
.status-dropdown-menu.open { display: block; }
|
||||||
background: #fef3c7;
|
|
||||||
color: #d97706;
|
.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 {
|
.status-option:hover { background: #eef2ff; }
|
||||||
background: #dbeafe;
|
|
||||||
color: #1d4ed8;
|
.status-option .dot {
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
border-radius: 999px;
|
||||||
|
background: #9ca3af;
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-completed {
|
.status-option.active .dot {
|
||||||
background: #d1fae5;
|
background: #22c55e;
|
||||||
color: #065f46;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-cancelled {
|
/* COLOR MAPPING per status – dropdown tint + main toggle text color */
|
||||||
background: #fee2e2;
|
.status-option.status-container-ready { background: #eff6ff; color: #1d4ed8; }
|
||||||
color: #991b1b;
|
.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 {
|
.action-buttons {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -373,39 +415,7 @@
|
|||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
.update-form {
|
.update-form { position: relative; }
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
.no-results {
|
.no-results {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
@@ -444,16 +454,13 @@
|
|||||||
animation: slideIn 0.3s ease;
|
animation: slideIn 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.success-message i {
|
.success-message i { font-size: 20px; }
|
||||||
font-size: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes slideIn {
|
@keyframes slideIn {
|
||||||
from { opacity: 0; transform: translateY(-10px); }
|
from { opacity: 0; transform: translateY(-10px); }
|
||||||
to { opacity: 1; transform: translateY(0); }
|
to { opacity: 1; transform: translateY(0); }
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 🔥 Totals section */
|
|
||||||
.totals-section {
|
.totals-section {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
|
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
|
||||||
@@ -504,9 +511,7 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
.filter-grid {
|
.filter-grid { grid-template-columns: 1fr; }
|
||||||
grid-template-columns: 1fr;
|
|
||||||
}
|
|
||||||
.container-header {
|
.container-header {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
@@ -519,13 +524,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 576px) {
|
@media (max-width: 576px) {
|
||||||
.update-form {
|
.update-form { width: 100%; }
|
||||||
flex-direction: column;
|
.status-dropdown { width: 100%; }
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
.status-select, .update-btn {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
@@ -569,10 +569,15 @@
|
|||||||
<label><i class="fas fa-tag"></i> Status</label>
|
<label><i class="fas fa-tag"></i> Status</label>
|
||||||
<select name="status" class="filter-select">
|
<select name="status" class="filter-select">
|
||||||
<option value="">All Status</option>
|
<option value="">All Status</option>
|
||||||
<option value="pending" {{ request('status') == 'pending' ? 'selected' : '' }}>Pending</option>
|
<option value="container-ready" {{ request('status') == 'container-ready' ? 'selected' : '' }}>Container Ready</option>
|
||||||
<option value="in-progress" {{ request('status') == 'in-progress' ? 'selected' : '' }}>In Progress</option>
|
<option value="export-custom" {{ request('status') == 'export-custom' ? 'selected' : '' }}>Export Custom</option>
|
||||||
<option value="completed" {{ request('status') == 'completed' ? 'selected' : '' }}>Completed</option>
|
<option value="international-transit" {{ request('status') == 'international-transit' ? 'selected' : '' }}>International Transit</option>
|
||||||
<option value="cancelled" {{ request('status') == 'cancelled' ? 'selected' : '' }}>Cancelled</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>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -610,22 +615,31 @@
|
|||||||
<p>Get started by creating your first container</p>
|
<p>Get started by creating your first container</p>
|
||||||
</div>
|
</div>
|
||||||
@else
|
@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)
|
@foreach($containers as $container)
|
||||||
@php
|
@php
|
||||||
$status = $container->status;
|
$status = $container->status ?? 'container-ready';
|
||||||
$statusClass = match ($status) {
|
$statusLabel = $labels[$status] ?? ucfirst(str_replace('-', ' ', $status));
|
||||||
'completed' => 'status-completed',
|
|
||||||
'in-progress' => 'status-in-progress',
|
|
||||||
'cancelled' => 'status-cancelled',
|
|
||||||
default => 'status-pending',
|
|
||||||
};
|
|
||||||
@endphp
|
@endphp
|
||||||
|
|
||||||
<div class="container-item">
|
<div class="container-item">
|
||||||
<div class="container-header">
|
<div class="container-header">
|
||||||
<div class="container-info">
|
<div class="container-info">
|
||||||
<div class="container-avatar">
|
<div class="container-avatar">
|
||||||
{{ substr($container->container_name, 0, 2) }}
|
{{ strtoupper(substr($container->container_name, 0, 2)) }}
|
||||||
</div>
|
</div>
|
||||||
<div class="container-details">
|
<div class="container-details">
|
||||||
<h3>{{ $container->container_name }}</h3>
|
<h3>{{ $container->container_name }}</h3>
|
||||||
@@ -647,13 +661,35 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="action-buttons">
|
<div class="action-buttons">
|
||||||
<span class="status-badge {{ $statusClass }}">
|
@can('containers.update_status')
|
||||||
<i class="fas fa-circle"></i>
|
<form action="{{ route('containers.update-status', $container->id) }}"
|
||||||
<span class="status-text">
|
method="POST"
|
||||||
{{ ucfirst(str_replace('-', ' ', $status)) }}
|
class="update-form ajax-status-form"
|
||||||
</span>
|
data-container-id="{{ $container->id }}">
|
||||||
</span>
|
@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')
|
@can('container.update')
|
||||||
<a href="{{ route('containers.show', $container->id) }}" class="action-btn view-btn">
|
<a href="{{ route('containers.show', $container->id) }}" class="action-btn view-btn">
|
||||||
@@ -661,26 +697,10 @@
|
|||||||
</a>
|
</a>
|
||||||
@endcan
|
@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 }}">
|
|
||||||
@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>
|
|
||||||
</form>
|
|
||||||
@endcan
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@can('container.delete')
|
@can('container.delete')
|
||||||
<form action="{{ route('containers.destroy', $container->id) }}" method="POST"
|
<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?');">
|
class="delete-form"
|
||||||
|
data-container-id="{{ $container->id }}">
|
||||||
@csrf
|
@csrf
|
||||||
@method('DELETE')
|
@method('DELETE')
|
||||||
<button type="submit" class="action-btn delete-btn">
|
<button type="submit" class="action-btn delete-btn">
|
||||||
@@ -691,7 +711,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 🔥 Totals instead of first row preview -->
|
|
||||||
<div class="totals-section">
|
<div class="totals-section">
|
||||||
<div class="total-card">
|
<div class="total-card">
|
||||||
<div class="total-value">{{ number_format($container->summary['total_ctn'], 1) }}</div>
|
<div class="total-value">{{ number_format($container->summary['total_ctn'], 1) }}</div>
|
||||||
@@ -716,87 +735,121 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener('DOMContentLoaded', function () {
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
document.querySelectorAll('.ajax-status-form .status-select').forEach(function (selectEl) {
|
// STATUS DROPDOWN
|
||||||
selectEl.addEventListener('change', function (event) {
|
document.querySelectorAll('.status-dropdown').forEach(function (wrapper) {
|
||||||
const form = event.target.closest('form');
|
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 url = form.getAttribute('action');
|
||||||
const token = form.querySelector('input[name="_token"]').value;
|
const token = form.querySelector('input[name="_token"]').value;
|
||||||
const status = event.target.value;
|
|
||||||
|
|
||||||
console.log('Sending status update:', url, status);
|
const formData = new FormData();
|
||||||
|
formData.append('_token', token);
|
||||||
|
formData.append('status', status);
|
||||||
|
|
||||||
fetch(url, {
|
fetch(url, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'X-CSRF-TOKEN': token,
|
|
||||||
'X-Requested-With': 'XMLHttpRequest',
|
'X-Requested-With': 'XMLHttpRequest',
|
||||||
'Content-Type': 'application/json',
|
|
||||||
'Accept': 'application/json',
|
'Accept': 'application/json',
|
||||||
},
|
},
|
||||||
body: JSON.stringify({ status: status })
|
body: formData
|
||||||
})
|
})
|
||||||
.then(async res => {
|
.then(async res => {
|
||||||
console.log('Response status:', res.status);
|
|
||||||
let data = null;
|
let data = null;
|
||||||
try { data = await res.json(); } catch (e) {}
|
try { data = await res.json(); } catch (e) {}
|
||||||
console.log('Response body:', data);
|
|
||||||
|
|
||||||
if (!res.ok || !data || !data.success) {
|
if (!res.ok || !data || !data.success) {
|
||||||
alert('Status update failed');
|
alert('Status update failed');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
alert('Network error while updating status');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// 👉 UI update (badge text + color)
|
document.addEventListener('click', function () {
|
||||||
const item = form.closest('.container-item');
|
document.querySelectorAll('.status-dropdown-menu.open')
|
||||||
const badge = item.querySelector('.status-badge');
|
.forEach(m => m.classList.remove('open'));
|
||||||
|
});
|
||||||
|
|
||||||
// text
|
// DELETE VIA AJAX
|
||||||
// text
|
document.querySelectorAll('.delete-form').forEach(function (form) {
|
||||||
const pretty = data.status.replace('-', ' ');
|
form.addEventListener('submit', function (e) {
|
||||||
const textEl = badge.querySelector('.status-text');
|
e.preventDefault();
|
||||||
if (textEl) {
|
|
||||||
textEl.textContent =
|
if (!confirm('Are you sure you want to delete this container and all its entries?')) {
|
||||||
pretty.charAt(0).toUpperCase() + pretty.slice(1);
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// classes
|
const url = form.getAttribute('action');
|
||||||
badge.classList.remove(
|
const token = form.querySelector('input[name="_token"]').value;
|
||||||
'status-pending',
|
const item = form.closest('.container-item');
|
||||||
'status-in-progress',
|
|
||||||
'status-completed',
|
|
||||||
'status-cancelled'
|
|
||||||
);
|
|
||||||
|
|
||||||
if (data.status === 'pending') {
|
const formData = new FormData();
|
||||||
badge.classList.add('status-pending');
|
formData.append('_token', token);
|
||||||
} else if (data.status === 'in-progress') {
|
formData.append('_method', 'DELETE');
|
||||||
badge.classList.add('status-in-progress');
|
|
||||||
} else if (data.status === 'completed') {
|
fetch(url, {
|
||||||
badge.classList.add('status-completed');
|
method: 'POST',
|
||||||
} else if (data.status === 'cancelled') {
|
headers: {
|
||||||
badge.classList.add('status-cancelled');
|
'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(err => {
|
.catch(() => {
|
||||||
console.error('Error:', err);
|
alert('Network error while deleting container');
|
||||||
alert('Network error while updating status');
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
<link rel="stylesheet"
|
<link rel="stylesheet"
|
||||||
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
|
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@endsection
|
@endsection
|
||||||
|
|||||||
@@ -137,6 +137,10 @@
|
|||||||
Some rows in your Excel file have formula or mark issues.
|
Some rows in your Excel file have formula or mark issues.
|
||||||
See the table below, and detailed messages after the table.
|
See the table below, and detailed messages after the table.
|
||||||
</div>
|
</div>
|
||||||
|
<ul class="small mb-0 ps-3">
|
||||||
|
<li>Red highlighted rows indicate formula mismatches in the uploaded Excel data.</li>
|
||||||
|
<li>Yellow highlighted rows indicate marks from the Excel file that do not match any record in the system.</li>
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -183,11 +187,9 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{{-- Formula error rows --}}
|
{{-- Formula error rows (red – formula mismatch, critical) --}}
|
||||||
@foreach($formulaErrors as $fe)
|
@foreach($formulaErrors as $fe)
|
||||||
@php
|
@php $rowData = $fe['data'] ?? []; @endphp
|
||||||
$rowData = $fe['data'] ?? [];
|
|
||||||
@endphp
|
|
||||||
@if(!empty($rowData))
|
@if(!empty($rowData))
|
||||||
<tr class="table-danger">
|
<tr class="table-danger">
|
||||||
<td>{{ $fe['excel_row'] }}</td>
|
<td>{{ $fe['excel_row'] }}</td>
|
||||||
@@ -199,11 +201,9 @@
|
|||||||
@endif
|
@endif
|
||||||
@endforeach
|
@endforeach
|
||||||
|
|
||||||
{{-- Mark error rows --}}
|
{{-- Mark error rows (yellow – mark not found, warning) --}}
|
||||||
@foreach($markErrors as $me)
|
@foreach($markErrors as $me)
|
||||||
@php
|
@php $rowData = $me['data'] ?? []; @endphp
|
||||||
$rowData = $me['data'] ?? [];
|
|
||||||
@endphp
|
|
||||||
@if(!empty($rowData))
|
@if(!empty($rowData))
|
||||||
<tr class="table-warning">
|
<tr class="table-warning">
|
||||||
<td>{{ $me['excel_row'] }}</td>
|
<td>{{ $me['excel_row'] }}</td>
|
||||||
@@ -292,7 +292,9 @@
|
|||||||
|
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<label class="cm-form-label">Container Date</label>
|
<label class="cm-form-label">Container Date</label>
|
||||||
<input type="date" name="container_date"
|
<input type="date"
|
||||||
|
name="container_date" {{-- name fix --}}
|
||||||
|
id="containerdate" {{-- JS साठी जुना id ठेवला --}}
|
||||||
class="form-control cm-form-control"
|
class="form-control cm-form-control"
|
||||||
value="{{ old('container_date') }}">
|
value="{{ old('container_date') }}">
|
||||||
</div>
|
</div>
|
||||||
@@ -322,4 +324,26 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
|
const dateInput = document.getElementById('containerdate');
|
||||||
|
if (!dateInput) return;
|
||||||
|
|
||||||
|
const today = new Date();
|
||||||
|
const year = today.getFullYear();
|
||||||
|
const month = String(today.getMonth() + 1).padStart(2, '0');
|
||||||
|
const day = String(today.getDate()).padStart(2, '0');
|
||||||
|
const todayStr = `${year}-${month}-${day}`;
|
||||||
|
|
||||||
|
// Todays date
|
||||||
|
dateInput.min = todayStr;
|
||||||
|
|
||||||
|
// old date remove
|
||||||
|
if (dateInput.value && dateInput.value < todayStr) {
|
||||||
|
dateInput.value = todayStr;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
@endsection
|
@endsection
|
||||||
|
|||||||
202
resources/views/admin/container_pdf.blade.php
Normal file
202
resources/views/admin/container_pdf.blade.php
Normal file
@@ -0,0 +1,202 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>Container {{ $container->container_number }} Summary</title>
|
||||||
|
<style>
|
||||||
|
* { box-sizing: border-box; }
|
||||||
|
body {
|
||||||
|
font-family: DejaVu Sans, sans-serif;
|
||||||
|
font-size: 10px;
|
||||||
|
margin: 10px;
|
||||||
|
background: #e5e7ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* COMMON CARD GRID – 4 equal columns, 2 rows */
|
||||||
|
.card-grid {
|
||||||
|
display: table;
|
||||||
|
width: 100%;
|
||||||
|
border-spacing: 8px 6px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
.card-row {
|
||||||
|
display: table-row;
|
||||||
|
}
|
||||||
|
.card {
|
||||||
|
display: table-cell;
|
||||||
|
width: 25%;
|
||||||
|
padding: 7px 10px;
|
||||||
|
border-radius: 14px;
|
||||||
|
box-shadow: 0 4px 12px rgba(15,35,52,0.18);
|
||||||
|
color: #0f172a;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* INFO CARDS (FIRST ROW) */
|
||||||
|
.info-id { background: #e0f2ff; border-left: 4px solid #2563eb; }
|
||||||
|
.info-no { background: #dcfce7; border-left: 4px solid #22c55e; }
|
||||||
|
.info-date { background: #fee2e2; border-left: 4px solid #ef4444; }
|
||||||
|
.info-name { background: #fef9c3; border-left: 4px solid #f59e0b; }
|
||||||
|
|
||||||
|
/* TOTAL CARDS (SECOND ROW) */
|
||||||
|
.total-ctn { background: #dbeafe; border-left: 4px solid #1d4ed8; }
|
||||||
|
.total-qty { background: #bbf7d0; border-left: 4px solid #16a34a; }
|
||||||
|
.total-cbm { background: #fef3c7; border-left: 4px solid #d97706; }
|
||||||
|
.total-kg { background: #fecaca; border-left: 4px solid #dc2626; }
|
||||||
|
|
||||||
|
.label-text {
|
||||||
|
font-size: 9px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.4px;
|
||||||
|
color: #4b5563;
|
||||||
|
}
|
||||||
|
.value-text-small {
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 800;
|
||||||
|
margin-top: 2px;
|
||||||
|
word-wrap: break-word;
|
||||||
|
}
|
||||||
|
.value-text-big {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 800;
|
||||||
|
margin-top: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TABLE – solid yellow header like screenshot */
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
margin-top: 6px;
|
||||||
|
table-layout: fixed;
|
||||||
|
background: #ffffff;
|
||||||
|
border-radius: 12px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
th, td {
|
||||||
|
border: 1px solid #e5e7eb;
|
||||||
|
padding: 4px 3px;
|
||||||
|
text-align: center;
|
||||||
|
word-wrap: break-word;
|
||||||
|
}
|
||||||
|
th {
|
||||||
|
background: #fbd85d; /* इथे solid yellow */
|
||||||
|
font-size: 9px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #0c0909;
|
||||||
|
}
|
||||||
|
td {
|
||||||
|
font-size: 9px;
|
||||||
|
color: #111827;
|
||||||
|
}
|
||||||
|
tr:nth-child(even) td {
|
||||||
|
background: #f9fafb;
|
||||||
|
}
|
||||||
|
|
||||||
|
thead { display: table-header-group; }
|
||||||
|
tr { page-break-inside: avoid; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
@php
|
||||||
|
$totalCtn = 0;
|
||||||
|
$totalQty = 0;
|
||||||
|
$totalCbm = 0.0;
|
||||||
|
$totalKg = 0.0;
|
||||||
|
|
||||||
|
foreach ($container->rows as $row) {
|
||||||
|
if (!is_array($row->data)) continue;
|
||||||
|
foreach ($row->data as $h => $v) {
|
||||||
|
$norm = strtoupper(str_replace([' ', '/', '-', '.'],'', $h));
|
||||||
|
$val = is_numeric(str_replace([','], '', $v)) ? floatval(str_replace([','], '', $v)) : 0;
|
||||||
|
|
||||||
|
if (str_contains($norm, 'TOTALCTN') || $norm === 'CTN' || str_contains($norm,'TOTALCNTR') || str_contains($norm,'TOTALCARTON')) {
|
||||||
|
$totalCtn += $val;
|
||||||
|
}
|
||||||
|
if (str_contains($norm,'TOTALQTY') || str_contains($norm,'ITLQTY') || str_contains($norm,'TTLQTY')) {
|
||||||
|
$totalQty += $val;
|
||||||
|
}
|
||||||
|
if (str_contains($norm,'TOTALCBM') || str_contains($norm,'TTLCBM') || str_contains($norm,'ITLCBM')) {
|
||||||
|
$totalCbm += $val;
|
||||||
|
}
|
||||||
|
if (str_contains($norm,'TOTALKG') || str_contains($norm,'TTKG')) {
|
||||||
|
$totalKg += $val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$allHeadings = [];
|
||||||
|
foreach ($container->rows as $row) {
|
||||||
|
if (is_array($row->data)) {
|
||||||
|
$allHeadings = array_unique(array_merge($allHeadings, array_keys($row->data)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@endphp
|
||||||
|
|
||||||
|
{{-- TWO ROW GRID – FIRST: INFO / SECOND: TOTALS --}}
|
||||||
|
<div class="card-grid">
|
||||||
|
<div class="card-row">
|
||||||
|
<div class="card info-id">
|
||||||
|
<div class="label-text">Container ID</div>
|
||||||
|
<div class="value-text-small">{{ $container->id }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="card info-no">
|
||||||
|
<div class="label-text">Container Number</div>
|
||||||
|
<div class="value-text-small">{{ $container->container_number }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="card info-date">
|
||||||
|
<div class="label-text">Container Date</div>
|
||||||
|
<div class="value-text-small">
|
||||||
|
{{ $container->container_date ? $container->container_date->format('d-m-Y') : '-' }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card info-name">
|
||||||
|
<div class="label-text">Container Name</div>
|
||||||
|
<div class="value-text-small">
|
||||||
|
{{ $container->container_name ?? '-' }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card-row">
|
||||||
|
<div class="card total-ctn">
|
||||||
|
<div class="label-text">Total CTN</div>
|
||||||
|
<div class="value-text-big">{{ number_format($totalCtn, 0) }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="card total-qty">
|
||||||
|
<div class="label-text">Total QTY</div>
|
||||||
|
<div class="value-text-big">{{ number_format($totalQty, 0) }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="card total-cbm">
|
||||||
|
<div class="label-text">Total CBM</div>
|
||||||
|
<div class="value-text-big">{{ number_format($totalCbm, 3) }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="card total-kg">
|
||||||
|
<div class="label-text">Total KG</div>
|
||||||
|
<div class="value-text-big">{{ number_format($totalKg, 2) }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{-- FULL TABLE --}}
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th style="width:18px;">#</th>
|
||||||
|
@foreach($allHeadings as $heading)
|
||||||
|
<th>{{ $heading }}</th>
|
||||||
|
@endforeach
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@foreach($container->rows as $index => $row)
|
||||||
|
<tr>
|
||||||
|
<td>{{ $index + 1 }}</td>
|
||||||
|
@foreach($allHeadings as $heading)
|
||||||
|
<td>{{ $row->data[$heading] ?? '' }}</td>
|
||||||
|
@endforeach
|
||||||
|
</tr>
|
||||||
|
@endforeach
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -6,7 +6,13 @@
|
|||||||
<style>
|
<style>
|
||||||
@import url('https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;600;700&display=swap');
|
@import url('https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;600;700&display=swap');
|
||||||
|
|
||||||
/* ── TOP HEADER CARD ── */
|
html, body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
width: 100%;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
.cm-header-card {
|
.cm-header-card {
|
||||||
background: linear-gradient(100deg, #4c6fff 0%, #8e54e9 100%);
|
background: linear-gradient(100deg, #4c6fff 0%, #8e54e9 100%);
|
||||||
border-radius: 14px;
|
border-radius: 14px;
|
||||||
@@ -34,13 +40,13 @@
|
|||||||
margin-top: 2px;
|
margin-top: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ── MAIN CARD ── */
|
|
||||||
.cm-main-card {
|
.cm-main-card {
|
||||||
border-radius: 14px;
|
border-radius: 14px;
|
||||||
border: none;
|
border: none;
|
||||||
box-shadow: 0 4px 20px rgba(15,35,52,0.10);
|
box-shadow: 0 4px 20px rgba(15,35,52,0.10);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
|
position: relative;
|
||||||
}
|
}
|
||||||
.cm-main-card .card-header {
|
.cm-main-card .card-header {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
@@ -57,30 +63,138 @@
|
|||||||
color: #1a2340;
|
color: #1a2340;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ── INFO STRIP ── */
|
.cm-info-cards-row {
|
||||||
.cm-info-strip {
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, minmax(0, 1fr)); /* 3 cards one row */
|
||||||
|
gap: 14px;
|
||||||
|
padding: 14px 18px 8px 18px;
|
||||||
|
}
|
||||||
|
.cm-info-card {
|
||||||
|
border-radius: 16px;
|
||||||
|
padding: 12px 14px;
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 28px;
|
align-items: center;
|
||||||
padding: 12px 20px 0 20px;
|
gap: 10px;
|
||||||
flex-wrap: wrap;
|
box-shadow: 0 4px 16px rgba(15,35,52,0.12);
|
||||||
|
border: 1px solid rgba(148,163,184,0.25);
|
||||||
|
background: #fff;
|
||||||
|
min-height: 70px;
|
||||||
}
|
}
|
||||||
.cm-info-label {
|
.cm-info-card-icon {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: #0f172a;
|
||||||
|
font-size: 16px;
|
||||||
|
box-shadow: 0 3px 10px rgba(15,23,42,0.10);
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.cm-info-card-body {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 3px;
|
||||||
|
}
|
||||||
|
.cm-info-card-label {
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
color: #8a93a6;
|
|
||||||
font-weight: 600;
|
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
letter-spacing: 0.5px;
|
letter-spacing: 0.4px;
|
||||||
margin-bottom: 2px;
|
color: #64748b;
|
||||||
}
|
|
||||||
.cm-info-value {
|
|
||||||
font-size: 13px;
|
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: #1a2340;
|
}
|
||||||
|
.cm-info-card-value {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #0f172a;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.cm-card-container {
|
||||||
|
background: linear-gradient(135deg, #e0f2ff, #eef4ff);
|
||||||
|
}
|
||||||
|
.cm-card-container .cm-info-card-icon {
|
||||||
|
background: linear-gradient(135deg, #2563eb, #4f46e5);
|
||||||
|
color: #e5edff;
|
||||||
|
}
|
||||||
|
.cm-card-date {
|
||||||
|
background: linear-gradient(135deg, #ecfdf3, #e0fbea);
|
||||||
|
}
|
||||||
|
.cm-card-date .cm-info-card-icon {
|
||||||
|
background: linear-gradient(135deg, #16a34a, #22c55e);
|
||||||
|
color: #ecfdf3;
|
||||||
|
}
|
||||||
|
.cm-card-excel {
|
||||||
|
background: linear-gradient(135deg, #fff7ed, #fffbeb);
|
||||||
|
}
|
||||||
|
.cm-card-excel .cm-info-card-icon {
|
||||||
|
background: linear-gradient(135deg, #f97316, #fb923c);
|
||||||
|
color: #fff7ed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TOTAL BOXES */
|
||||||
|
.cm-total-cards-row {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||||
|
gap: 14px;
|
||||||
|
padding: 4px 18px 14px 18px;
|
||||||
|
}
|
||||||
|
.cm-total-card {
|
||||||
|
border-radius: 18px;
|
||||||
|
padding: 12px 18px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
box-shadow: 0 8px 30px rgba(15,23,42,0.08);
|
||||||
|
border: 1px solid rgba(148,163,184,0.25);
|
||||||
|
}
|
||||||
|
.cm-total-icon {
|
||||||
|
width: 34px;
|
||||||
|
height: 34px;
|
||||||
|
border-radius: 999px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 17px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
color: #0f172a;
|
||||||
|
background: #fff;
|
||||||
|
box-shadow: 0 4px 14px rgba(15,23,42,0.15);
|
||||||
|
}
|
||||||
|
.cm-total-text {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 3px;
|
||||||
|
}
|
||||||
|
.cm-total-label {
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #4b5563;
|
||||||
|
}
|
||||||
|
.cm-total-value {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 800;
|
||||||
|
color: #111827;
|
||||||
|
}
|
||||||
|
.cm-total-card-ctn {
|
||||||
|
background: linear-gradient(135deg, #e0f2ff, #eef2ff);
|
||||||
|
border-left: 4px solid #3b82f6;
|
||||||
|
}
|
||||||
|
.cm-total-card-qty {
|
||||||
|
background: linear-gradient(135deg, #dcfce7, #ecfdf5);
|
||||||
|
border-left: 4px solid #22c55e;
|
||||||
|
}
|
||||||
|
.cm-total-card-cbm {
|
||||||
|
background: linear-gradient(135deg, #fef9c3, #fffbeb);
|
||||||
|
border-left: 4px solid #f59e0b;
|
||||||
|
}
|
||||||
|
.cm-total-card-kg {
|
||||||
|
background: linear-gradient(135deg, #fee2e2, #fef2f2);
|
||||||
|
border-left: 4px solid #ef4444;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ── FILTER BAR ── */
|
|
||||||
.cm-filter-bar {
|
.cm-filter-bar {
|
||||||
padding: 12px 20px 0 20px;
|
padding: 4px 20px 0 20px;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -106,59 +220,50 @@
|
|||||||
box-shadow: 0 0 0 3px rgba(76,111,255,0.1);
|
box-shadow: 0 0 0 3px rgba(76,111,255,0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ── TABLE SCROLL OUTER ── */
|
|
||||||
.cm-table-scroll-outer {
|
.cm-table-scroll-outer {
|
||||||
margin: 10px 14px 0 14px;
|
margin: 10px 14px 0 14px;
|
||||||
border-radius: 14px 14px 0 0;
|
border-radius: 14px;
|
||||||
/* इथे overflow: hidden होते, ते visible केले */
|
border: 1.5px solid #c9a359;
|
||||||
overflow-x: auto; /* फक्त horizontal scroll हवा असेल तर */
|
box-shadow: 0 4px 14px rgba(76,111,255,0.18);
|
||||||
overflow-y: visible;
|
overflow-x: auto;
|
||||||
border: 1.5px solid #b8920e;
|
overflow-y: hidden;
|
||||||
border-bottom: none;
|
position: relative;
|
||||||
box-shadow: 0 -2px 10px rgba(184,146,14,0.10);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ── TABLE WRAPPER ── */
|
|
||||||
.cm-table-wrapper {
|
.cm-table-wrapper {
|
||||||
position: relative;
|
position: relative;
|
||||||
/* इथे max-height, overflow auto काढले, जे inner scroll देत होते */
|
min-width: 1200px;
|
||||||
max-height: none;
|
|
||||||
overflow: visible;
|
|
||||||
border-top: none;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ── TABLE STYLES ── */
|
|
||||||
.cm-table {
|
.cm-table {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
min-width: 1100px;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border-collapse: separate;
|
border-collapse: separate;
|
||||||
border-spacing: 0;
|
border-spacing: 0;
|
||||||
font-family: 'DM Sans', sans-serif;
|
font-family: 'DM Sans', sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Sticky header – light header color */
|
|
||||||
.cm-table thead tr th {
|
.cm-table thead tr th {
|
||||||
position: sticky;
|
position: sticky;
|
||||||
top: 0;
|
top: 0;
|
||||||
z-index: 3;
|
z-index: 3;
|
||||||
background: #fde4b3;
|
background: linear-gradient(100deg, #fde4b3 0%, #fbd48a 100%);
|
||||||
color: #0a0a09;
|
color: #0c0909;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
padding: 11px 14px;
|
padding: 11px 14px;
|
||||||
border-bottom: 2px solid #fde4b3;
|
border-bottom: 2px solid rgba(255,255,255,0.15);
|
||||||
border-right: 1px solid #fde4b3;
|
border-right: 1px solid rgba(255,255,255,0.18);
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
letter-spacing: 0.2px;
|
letter-spacing: 0.2px;
|
||||||
text-shadow: 0 1px 2px rgba(0,0,0,0.18);
|
text-shadow: 0 1px 2px rgba(0,0,0,0.35);
|
||||||
|
}
|
||||||
|
.cm-table thead tr th:first-child {
|
||||||
|
border-top-left-radius: 10px;
|
||||||
}
|
}
|
||||||
.cm-table thead tr th:last-child {
|
.cm-table thead tr th:last-child {
|
||||||
|
border-top-right-radius: 10px;
|
||||||
border-right: none;
|
border-right: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* # column – narrower */
|
|
||||||
.cm-table thead tr th:first-child,
|
.cm-table thead tr th:first-child,
|
||||||
.cm-table tbody tr td:first-child {
|
.cm-table tbody tr td:first-child {
|
||||||
width: 46px;
|
width: 46px;
|
||||||
@@ -166,8 +271,6 @@
|
|||||||
max-width: 46px;
|
max-width: 46px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Body rows */
|
|
||||||
.cm-table tbody tr td {
|
.cm-table tbody tr td {
|
||||||
padding: 8px 14px;
|
padding: 8px 14px;
|
||||||
border-bottom: 1px solid #f0f3fb;
|
border-bottom: 1px solid #f0f3fb;
|
||||||
@@ -178,28 +281,23 @@
|
|||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
transition: background 0.15s;
|
transition: background 0.15s;
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
.cm-table tbody tr td:last-child {
|
.cm-table tbody tr td:last-child {
|
||||||
border-right: none;
|
border-right: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Zebra */
|
|
||||||
.cm-table tbody tr:nth-child(even) td {
|
.cm-table tbody tr:nth-child(even) td {
|
||||||
background: #f8f9ff;
|
background: #f8f9ff;
|
||||||
}
|
}
|
||||||
/* Hover */
|
|
||||||
.cm-table tbody tr:hover td {
|
.cm-table tbody tr:hover td {
|
||||||
background: #edf3ff !important;
|
background: #edf3ff !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Row number td */
|
|
||||||
.cm-row-num {
|
.cm-row-num {
|
||||||
color: #8a93a6;
|
color: #8a93a6;
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Editable cell input */
|
|
||||||
.cm-cell-input {
|
.cm-cell-input {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
min-width: 90px;
|
min-width: 90px;
|
||||||
@@ -223,103 +321,278 @@
|
|||||||
background: #fff;
|
background: #fff;
|
||||||
box-shadow: 0 0 0 3px rgba(76,111,255,0.12);
|
box-shadow: 0 0 0 3px rgba(76,111,255,0.12);
|
||||||
}
|
}
|
||||||
|
.cm-cell-readonly {
|
||||||
|
background: #f3f4ff;
|
||||||
|
cursor: not-allowed;
|
||||||
|
border-color: #e2e3ff;
|
||||||
|
}
|
||||||
|
|
||||||
/* Save button */
|
.cm-save-btn-floating {
|
||||||
.cm-save-btn {
|
position: fixed;
|
||||||
font-size: 12.5px;
|
right: 26px;
|
||||||
|
bottom: 22px;
|
||||||
|
z-index: 50;
|
||||||
|
font-size: 13px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
border-radius: 20px;
|
border-radius: 22px;
|
||||||
padding: 7px 18px;
|
padding: 8px 20px;
|
||||||
background: linear-gradient(90deg, #4c6fff, #8e54e9);
|
background: linear-gradient(90deg, #4c6fff, #8e54e9);
|
||||||
border: none;
|
border: none;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
box-shadow: 0 3px 10px rgba(76,111,255,0.22);
|
box-shadow: 0 4px 16px rgba(76,111,255,0.35);
|
||||||
transition: opacity 0.2s, transform 0.15s;
|
transition: opacity 0.2s, transform 0.15s;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
.cm-save-btn:hover {
|
.cm-save-btn-floating:hover {
|
||||||
opacity: 0.92;
|
opacity: 0.94;
|
||||||
transform: translateY(-1px);
|
transform: translateY(-1px);
|
||||||
}
|
}
|
||||||
|
.cm-save-btn-floating.cm-disabled {
|
||||||
|
opacity: 0.6;
|
||||||
|
cursor: default;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-toast {
|
||||||
|
position: fixed;
|
||||||
|
right: 26px;
|
||||||
|
bottom: 70px;
|
||||||
|
background: #0f172a;
|
||||||
|
color: #f9fafb;
|
||||||
|
font-size: 12.5px;
|
||||||
|
padding: 8px 14px;
|
||||||
|
border-radius: 10px;
|
||||||
|
box-shadow: 0 8px 24px rgba(15,23,42,0.25);
|
||||||
|
opacity: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
transition: opacity 0.25s, transform 0.2s;
|
||||||
|
transform: translateY(8px);
|
||||||
|
z-index: 60;
|
||||||
|
}
|
||||||
|
.cm-toast.cm-show {
|
||||||
|
opacity: 1;
|
||||||
|
pointer-events: auto;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
|
||||||
/* Empty state */
|
|
||||||
.cm-empty {
|
.cm-empty {
|
||||||
padding: 30px 20px;
|
padding: 30px 20px;
|
||||||
color: #8a93a6;
|
color: #8a93a6;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Scrollbar styling – आता फक्त horizontal ला लागू होईल */
|
.cm-table-scroll-outer::-webkit-scrollbar {
|
||||||
.cm-table-scroll-outer::-webkit-scrollbar { height: 6px; }
|
height: 6px;
|
||||||
.cm-table-scroll-outer::-webkit-scrollbar-track { background: #f0f3fb; border-radius: 4px; }
|
}
|
||||||
.cm-table-scroll-outer::-webkit-scrollbar-thumb { background: #c5cce8; border-radius: 4px; }
|
.cm-table-scroll-outer::-webkit-scrollbar-track {
|
||||||
.cm-table-scroll-outer::-webkit-scrollbar-thumb:hover { background: #8a99d0; }
|
background: #f0f3fb;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
.cm-table-scroll-outer::-webkit-scrollbar-thumb {
|
||||||
|
background: #c5cce8;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
.cm-table-scroll-outer::-webkit-scrollbar-thumb:hover {
|
||||||
|
background: #8a99d0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 991px) {
|
||||||
|
.cm-total-cards-row {
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
}
|
||||||
|
}
|
||||||
@media (max-width: 767px) {
|
@media (max-width: 767px) {
|
||||||
.cm-header-card .card-body { flex-direction: column; align-items: flex-start; }
|
.cm-header-card .card-body {
|
||||||
.cm-info-strip { gap: 16px; }
|
flex-direction: column;
|
||||||
.cm-table-scroll-outer { overflow-x: auto; }
|
align-items: flex-start;
|
||||||
.cm-table { min-width: 900px; }
|
}
|
||||||
|
.cm-info-cards-row {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
.cm-table-scroll-outer {
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
.cm-table-wrapper {
|
||||||
|
min-width: 900px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media (max-width: 575px) {
|
||||||
|
.cm-total-cards-row {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<div class="container-fluid cm-wrapper">
|
<div class="container-fluid cm-wrapper">
|
||||||
|
|
||||||
{{-- TOP GRADIENT HEADER --}}
|
|
||||||
<div class="card cm-header-card">
|
<div class="card cm-header-card">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div>
|
<div>
|
||||||
<h4 class="cm-header-title">
|
<h4 class="cm-header-title">Container {{ $container->container_number }}</h4>
|
||||||
Container: {{ $container->container_number }}
|
|
||||||
</h4>
|
|
||||||
<div class="cm-header-sub">
|
<div class="cm-header-sub">
|
||||||
Edit loading list directly — scroll horizontally & vertically like Excel.
|
Edit loading list directly – like Excel. TT columns auto‑calculate from CTN, QTY, CBM, KG, PRICE.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<a href="{{ route('containers.index') }}" class="btn btn-light btn-sm">
|
<div class="d-flex gap-2">
|
||||||
← Back to list
|
<a href="{{ route('containers.index') }}" class="btn btn-light btn-sm">Back to list</a>
|
||||||
|
|
||||||
|
<a href="{{ route('containers.download.pdf', $container->id) }}"
|
||||||
|
class="btn btn-sm btn-outline-primary">
|
||||||
|
Download PDF
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a href="{{ route('containers.download.excel', $container->id) }}"
|
||||||
|
class="btn btn-sm btn-outline-success">
|
||||||
|
Download Excel
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{{-- MAIN CARD --}}
|
|
||||||
<div class="card cm-main-card">
|
<div class="card cm-main-card">
|
||||||
|
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<h5>Container Information</h5>
|
<h5>Container Information</h5>
|
||||||
@if(!$container->rows->isEmpty())
|
|
||||||
<button type="submit"
|
|
||||||
form="cm-edit-rows-form"
|
|
||||||
class="cm-save-btn">
|
|
||||||
💾 Save Changes
|
|
||||||
</button>
|
|
||||||
@endif
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{-- INFO STRIP --}}
|
{{-- 3 INFO CARDS IN SINGLE ROW --}}
|
||||||
<div class="cm-info-strip">
|
<div class="cm-info-cards-row">
|
||||||
<div class="cm-info-item">
|
<div class="cm-info-card cm-card-container">
|
||||||
<div class="cm-info-label">Container</div>
|
<div class="cm-info-card-icon">
|
||||||
<div class="cm-info-value">{{ $container->container_name }}</div>
|
<i class="bi bi-box-seam"></i>
|
||||||
</div>
|
</div>
|
||||||
<div class="cm-info-item">
|
<div class="cm-info-card-body">
|
||||||
<div class="cm-info-label">Date</div>
|
<div class="cm-info-card-label">Container</div>
|
||||||
<div class="cm-info-value">{{ $container->container_date?->format('d-m-Y') }}</div>
|
<div class="cm-info-card-value">
|
||||||
|
{{ $container->container_name ?? $container->container_number }}
|
||||||
</div>
|
</div>
|
||||||
<div class="cm-info-item">
|
</div>
|
||||||
<div class="cm-info-label">Excel File</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="cm-info-card cm-card-date">
|
||||||
|
<div class="cm-info-card-icon">
|
||||||
|
<i class="bi bi-calendar-event"></i>
|
||||||
|
</div>
|
||||||
|
<div class="cm-info-card-body">
|
||||||
|
<div class="cm-info-card-label">Date</div>
|
||||||
|
<div class="cm-info-card-value">
|
||||||
|
{{ $container->container_date ? $container->container_date->format('d-m-Y') : '' }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="cm-info-card cm-card-excel">
|
||||||
|
<div class="cm-info-card-icon">
|
||||||
|
<i class="bi bi-file-earmark-excel"></i>
|
||||||
|
</div>
|
||||||
|
<div class="cm-info-card-body">
|
||||||
|
<div class="cm-info-card-label">Excel File</div>
|
||||||
|
<div class="cm-info-card-value">
|
||||||
@if($container->excel_file)
|
@if($container->excel_file)
|
||||||
<div class="cm-info-value">
|
<a href="{{ url($container->excel_file) }}" target="_blank" style="color:#0f172a;text-decoration:none;">
|
||||||
<a href="{{ \Illuminate\Support\Facades\Storage::url($container->excel_file) }}"
|
Download / View
|
||||||
target="_blank" style="color:#4c6fff;text-decoration:none;font-weight:600;">
|
|
||||||
📄 Download / View
|
|
||||||
</a>
|
</a>
|
||||||
</div>
|
|
||||||
@else
|
@else
|
||||||
<div class="cm-info-value" style="color:#b0b8cc;">Not uploaded</div>
|
<span style="color:#b0b8cc;">Not uploaded</span>
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@php
|
||||||
|
$totalCtn = 0;
|
||||||
|
$totalQty = 0;
|
||||||
|
$totalCbm = 0.0;
|
||||||
|
$totalKg = 0.0;
|
||||||
|
|
||||||
|
if(!$container->rows->isEmpty()){
|
||||||
|
foreach ($container->rows as $row) {
|
||||||
|
if (!is_array($row->data)) continue;
|
||||||
|
foreach ($row->data as $h => $v) {
|
||||||
|
$norm = strtoupper(str_replace([' ', '/', '-', '.'],'', $h));
|
||||||
|
$val = is_numeric(str_replace([','], '', $v)) ? floatval(str_replace([','], '', $v)) : 0;
|
||||||
|
|
||||||
|
if (str_contains($norm, 'TOTALCTN') || $norm === 'CTN' || str_contains($norm,'TOTALCNTR') || str_contains($norm,'TOTALCARTON')) {
|
||||||
|
$totalCtn += $val;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
str_contains($norm,'TOTALQTY') ||
|
||||||
|
str_contains($norm,'ITLQTY') ||
|
||||||
|
str_contains($norm,'TTLQTY')
|
||||||
|
) {
|
||||||
|
$totalQty += $val;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
str_contains($norm,'TOTALCBM') ||
|
||||||
|
str_contains($norm,'TTLCBM') ||
|
||||||
|
str_contains($norm,'ITLCBM')
|
||||||
|
) {
|
||||||
|
$totalCbm += $val;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
str_contains($norm,'TOTALKG') ||
|
||||||
|
str_contains($norm,'TTKG')
|
||||||
|
) {
|
||||||
|
$totalKg += $val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@endphp
|
||||||
|
|
||||||
|
@if(!$container->rows->isEmpty())
|
||||||
|
<div class="cm-total-cards-row">
|
||||||
|
<div class="cm-total-card cm-total-card-ctn">
|
||||||
|
<div class="cm-total-icon">
|
||||||
|
<i class="bi bi-box"></i>
|
||||||
|
</div>
|
||||||
|
<div class="cm-total-text">
|
||||||
|
<div class="cm-total-label">Total CTN</div>
|
||||||
|
<div class="cm-total-value">
|
||||||
|
{{ number_format($totalCtn, 0) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="cm-total-card cm-total-card-qty">
|
||||||
|
<div class="cm-total-icon">
|
||||||
|
<i class="bi bi-check-circle"></i>
|
||||||
|
</div>
|
||||||
|
<div class="cm-total-text">
|
||||||
|
<div class="cm-total-label">Total QTY</div>
|
||||||
|
<div class="cm-total-value">
|
||||||
|
{{ number_format($totalQty, 0) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="cm-total-card cm-total-card-cbm">
|
||||||
|
<div class="cm-total-icon">
|
||||||
|
<i class="bi bi-border-width"></i>
|
||||||
|
</div>
|
||||||
|
<div class="cm-total-text">
|
||||||
|
<div class="cm-total-label">Total CBM</div>
|
||||||
|
<div class="cm-total-value">
|
||||||
|
{{ number_format($totalCbm, 3) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="cm-total-card cm-total-card-kg">
|
||||||
|
<div class="cm-total-icon">
|
||||||
|
<i class="bi bi-exclamation-triangle"></i>
|
||||||
|
</div>
|
||||||
|
<div class="cm-total-text">
|
||||||
|
<div class="cm-total-label">Total KG</div>
|
||||||
|
<div class="cm-total-value">
|
||||||
|
{{ number_format($totalKg, 2) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
@if($container->rows->isEmpty())
|
@if($container->rows->isEmpty())
|
||||||
<div class="cm-empty">No entries found for this container.</div>
|
<div class="cm-empty">No entries found for this container.</div>
|
||||||
@@ -333,24 +606,16 @@
|
|||||||
}
|
}
|
||||||
@endphp
|
@endphp
|
||||||
|
|
||||||
{{-- FILTER BAR --}}
|
|
||||||
<div class="cm-filter-bar">
|
<div class="cm-filter-bar">
|
||||||
<span class="cm-row-count">
|
<span class="cm-row-count">
|
||||||
Total rows: {{ $container->rows->count() }} • Edit cells then click "Save Changes"
|
Total rows: {{ $container->rows->count() }} Edit cells then click "Save Changes".
|
||||||
</span>
|
</span>
|
||||||
<input type="text"
|
<input type="text" id="cmRowSearch" class="cm-filter-input"
|
||||||
id="cmRowSearch"
|
placeholder="Quick search..." onkeyup="cmFilterRows()">
|
||||||
class="cm-filter-input"
|
|
||||||
placeholder="🔍 Quick search..."
|
|
||||||
onkeyup="cmFilterRows()">
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{-- EDITABLE TABLE FORM --}}
|
<form id="cm-edit-rows-form" action="{{ route('containers.rows.update', $container->id) }}" method="POST">
|
||||||
<form id="cm-edit-rows-form"
|
|
||||||
action="{{ route('containers.rows.update', $container->id) }}"
|
|
||||||
method="POST">
|
|
||||||
@csrf
|
@csrf
|
||||||
|
|
||||||
<div class="cm-table-scroll-outer">
|
<div class="cm-table-scroll-outer">
|
||||||
<div class="cm-table-wrapper">
|
<div class="cm-table-wrapper">
|
||||||
<table class="cm-table" id="cmExcelTable">
|
<table class="cm-table" id="cmExcelTable">
|
||||||
@@ -367,12 +632,66 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<td class="cm-row-num">{{ $index + 1 }}</td>
|
<td class="cm-row-num">{{ $index + 1 }}</td>
|
||||||
@foreach($allHeadings as $heading)
|
@foreach($allHeadings as $heading)
|
||||||
@php $value = $row->data[$heading] ?? ''; @endphp
|
@php
|
||||||
|
$value = $row->data[$heading] ?? '';
|
||||||
|
|
||||||
|
$norm = strtoupper(str_replace([' ', '/', '-', '.'],'', $heading));
|
||||||
|
|
||||||
|
$isCtn = str_contains($norm, 'CTN');
|
||||||
|
|
||||||
|
$isTotalQty = (
|
||||||
|
str_contains($norm, 'TOTALQTY') ||
|
||||||
|
str_contains($norm, 'ITLQTY') ||
|
||||||
|
str_contains($norm, 'TTLQTY')
|
||||||
|
);
|
||||||
|
$isQty = !$isTotalQty && (
|
||||||
|
str_contains($norm, 'QTY') ||
|
||||||
|
str_contains($norm, 'PCS') ||
|
||||||
|
str_contains($norm, 'PIECES')
|
||||||
|
);
|
||||||
|
|
||||||
|
$isTotalCbm = (
|
||||||
|
str_contains($norm, 'TOTALCBM') ||
|
||||||
|
str_contains($norm, 'TTLCBM') ||
|
||||||
|
str_contains($norm, 'ITLCBM')
|
||||||
|
);
|
||||||
|
$isCbm = !$isTotalCbm && str_contains($norm, 'CBM');
|
||||||
|
|
||||||
|
$isTotalKg = (
|
||||||
|
str_contains($norm, 'TOTALKG') ||
|
||||||
|
str_contains($norm, 'TTKG')
|
||||||
|
);
|
||||||
|
$isKg = !$isTotalKg && (str_contains($norm, 'KG') || str_contains($norm, 'WEIGHT'));
|
||||||
|
|
||||||
|
$isPrice = (str_contains($norm, 'PRICE') || str_contains($norm, 'RATE'));
|
||||||
|
|
||||||
|
$isAmount = (
|
||||||
|
str_contains($norm, 'AMOUNT') ||
|
||||||
|
str_contains($norm, 'TTLAMOUNT') ||
|
||||||
|
str_contains($norm, 'TOTALAMOUNT')
|
||||||
|
);
|
||||||
|
|
||||||
|
$isTotalColumn = $isTotalQty || $isTotalCbm || $isTotalKg || $isAmount;
|
||||||
|
@endphp
|
||||||
<td>
|
<td>
|
||||||
<input type="text"
|
<input
|
||||||
class="cm-cell-input"
|
type="text"
|
||||||
|
class="cm-cell-input {{ $isTotalColumn ? 'cm-cell-readonly' : '' }}"
|
||||||
name="rows[{{ $row->id }}][{{ $heading }}]"
|
name="rows[{{ $row->id }}][{{ $heading }}]"
|
||||||
value="{{ $value }}">
|
value="{{ $value }}"
|
||||||
|
data-row-id="{{ $row->id }}"
|
||||||
|
data-col-key="{{ $heading }}"
|
||||||
|
data-ctn="{{ $isCtn ? '1' : '0' }}"
|
||||||
|
data-qty="{{ $isQty ? '1' : '0' }}"
|
||||||
|
data-ttlqty="{{ $isTotalQty ? '1' : '0' }}"
|
||||||
|
data-cbm="{{ $isCbm ? '1' : '0' }}"
|
||||||
|
data-ttlcbm="{{ $isTotalCbm ? '1' : '0' }}"
|
||||||
|
data-kg="{{ $isKg ? '1' : '0' }}"
|
||||||
|
data-ttlkg="{{ $isTotalKg ? '1' : '0' }}"
|
||||||
|
data-price="{{ $isPrice ? '1' : '0' }}"
|
||||||
|
data-amount="{{ $isAmount ? '1' : '0' }}"
|
||||||
|
@if($isTotalColumn) readonly @endif
|
||||||
|
>
|
||||||
</td>
|
</td>
|
||||||
@endforeach
|
@endforeach
|
||||||
</tr>
|
</tr>
|
||||||
@@ -384,9 +703,17 @@
|
|||||||
</form>
|
</form>
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@if(!$container->rows->isEmpty())
|
||||||
|
<button id="cmSaveBtnFloating" class="cm-save-btn-floating">
|
||||||
|
Save Changes
|
||||||
|
</button>
|
||||||
|
<div id="cmToast" class="cm-toast">
|
||||||
|
Changes saved successfully.
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
function cmFilterRows() {
|
function cmFilterRows() {
|
||||||
const input = document.getElementById('cmRowSearch');
|
const input = document.getElementById('cmRowSearch');
|
||||||
@@ -394,7 +721,6 @@
|
|||||||
const filter = input.value.toLowerCase();
|
const filter = input.value.toLowerCase();
|
||||||
const table = document.getElementById('cmExcelTable');
|
const table = document.getElementById('cmExcelTable');
|
||||||
if (!table) return;
|
if (!table) return;
|
||||||
|
|
||||||
const rows = table.getElementsByTagName('tr');
|
const rows = table.getElementsByTagName('tr');
|
||||||
for (let i = 1; i < rows.length; i++) {
|
for (let i = 1; i < rows.length; i++) {
|
||||||
const cells = rows[i].getElementsByTagName('td');
|
const cells = rows[i].getElementsByTagName('td');
|
||||||
@@ -409,5 +735,157 @@
|
|||||||
rows[i].style.display = match ? '' : 'none';
|
rows[i].style.display = match ? '' : 'none';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
|
const form = document.getElementById('cm-edit-rows-form');
|
||||||
|
const btn = document.getElementById('cmSaveBtnFloating');
|
||||||
|
const toast = document.getElementById('cmToast');
|
||||||
|
const table = document.getElementById('cmExcelTable');
|
||||||
|
|
||||||
|
function showToast(message, isError = false) {
|
||||||
|
if (!toast) return;
|
||||||
|
toast.textContent = message;
|
||||||
|
toast.style.background = isError ? '#b91c1c' : '#0f172a';
|
||||||
|
toast.classList.add('cm-show');
|
||||||
|
setTimeout(() => toast.classList.remove('cm-show'), 2500);
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseNumber(str) {
|
||||||
|
if (!str) return 0;
|
||||||
|
const cleaned = String(str).replace(/,/g, '').trim();
|
||||||
|
const val = parseFloat(cleaned);
|
||||||
|
return isNaN(val) ? 0 : val;
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatNumber(val, decimals) {
|
||||||
|
if (isNaN(val)) val = 0;
|
||||||
|
return val.toFixed(decimals);
|
||||||
|
}
|
||||||
|
|
||||||
|
function recalcRow(row) {
|
||||||
|
const inputs = row.querySelectorAll('.cm-cell-input');
|
||||||
|
|
||||||
|
let ctn = 0, qty = 0, ttlQty = 0;
|
||||||
|
let cbm = 0, ttlCbm = 0;
|
||||||
|
let kg = 0, ttlKg = 0;
|
||||||
|
let price = 0, amount = 0;
|
||||||
|
|
||||||
|
let ctnInput = null,
|
||||||
|
qtyInput = null,
|
||||||
|
ttlQtyInput = null,
|
||||||
|
cbmInput = null,
|
||||||
|
ttlCbmInput = null,
|
||||||
|
kgInput = null,
|
||||||
|
ttlKgInput = null,
|
||||||
|
priceInput = null,
|
||||||
|
amountInput = null;
|
||||||
|
|
||||||
|
inputs.forEach(inp => {
|
||||||
|
const val = parseNumber(inp.value);
|
||||||
|
|
||||||
|
if (inp.dataset.ctn === '1') {
|
||||||
|
ctn = val;
|
||||||
|
ctnInput = inp;
|
||||||
|
} else if (inp.dataset.qty === '1') {
|
||||||
|
qty = val;
|
||||||
|
qtyInput = inp;
|
||||||
|
} else if (inp.dataset.ttlqty === '1') {
|
||||||
|
ttlQty = val;
|
||||||
|
ttlQtyInput = inp;
|
||||||
|
} else if (inp.dataset.cbm === '1') {
|
||||||
|
cbm = val;
|
||||||
|
cbmInput = inp;
|
||||||
|
} else if (inp.dataset.ttlcbm === '1') {
|
||||||
|
ttlCbm = val;
|
||||||
|
ttlCbmInput = inp;
|
||||||
|
} else if (inp.dataset.kg === '1') {
|
||||||
|
kg = val;
|
||||||
|
kgInput = inp;
|
||||||
|
} else if (inp.dataset.ttlkg === '1') {
|
||||||
|
ttlKg = val;
|
||||||
|
ttlKgInput = inp;
|
||||||
|
} else if (inp.dataset.price === '1') {
|
||||||
|
price = val;
|
||||||
|
priceInput = inp;
|
||||||
|
} else if (inp.dataset.amount === '1') {
|
||||||
|
amount = val;
|
||||||
|
amountInput = inp;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (ttlQtyInput && ctnInput && qtyInput) {
|
||||||
|
const newTtlQty = ctn * qty;
|
||||||
|
ttlQtyInput.value = formatNumber(newTtlQty, 0);
|
||||||
|
ttlQty = newTtlQty;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ttlCbmInput && cbmInput && ctnInput) {
|
||||||
|
const newTtlCbm = cbm * ctn;
|
||||||
|
ttlCbmInput.value = formatNumber(newTtlCbm, 3);
|
||||||
|
ttlCbm = newTtlCbm;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ttlKgInput && kgInput && ctnInput) {
|
||||||
|
const newTtlKg = kg * ctn;
|
||||||
|
ttlKgInput.value = formatNumber(newTtlKg, 2);
|
||||||
|
ttlKg = newTtlKg;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (amountInput && priceInput && ttlQtyInput) {
|
||||||
|
const newAmount = price * ttlQty;
|
||||||
|
amountInput.value = formatNumber(newAmount, 2);
|
||||||
|
amount = newAmount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (table) {
|
||||||
|
table.addEventListener('input', function (e) {
|
||||||
|
const target = e.target;
|
||||||
|
if (!target.classList.contains('cm-cell-input')) return;
|
||||||
|
|
||||||
|
if (target.classList.contains('cm-cell-readonly') || target.hasAttribute('readonly')) {
|
||||||
|
target.blur();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const row = target.closest('tr');
|
||||||
|
if (row) {
|
||||||
|
recalcRow(row);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (form && btn) {
|
||||||
|
btn.addEventListener('click', function () {
|
||||||
|
btn.classList.add('cm-disabled');
|
||||||
|
const formData = new FormData(form);
|
||||||
|
|
||||||
|
fetch(form.action, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'X-Requested-With': 'XMLHttpRequest',
|
||||||
|
'X-CSRF-TOKEN': document.querySelector('input[name="_token"]').value
|
||||||
|
},
|
||||||
|
body: formData
|
||||||
|
})
|
||||||
|
.then(async res => {
|
||||||
|
if (!res.ok) {
|
||||||
|
const text = await res.text();
|
||||||
|
throw new Error(text || 'Failed to save');
|
||||||
|
}
|
||||||
|
return res.json().catch(() => ({}));
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
showToast('Changes saved successfully.');
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
showToast('Error while saving changes.', true);
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
btn.classList.remove('cm-disabled');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
@endsection
|
@endsection
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
body {
|
body {
|
||||||
font-family: 'Inter', sans-serif;
|
font-family: 'Inter', sans-serif;
|
||||||
overflow-x: hidden; /* Prevent horizontal scroll on body */
|
overflow-x: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.glass-card {
|
.glass-card {
|
||||||
@@ -22,7 +22,6 @@
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* New Stats Container */
|
|
||||||
.stats-container {
|
.stats-container {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
|
grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
|
||||||
@@ -43,9 +42,7 @@
|
|||||||
min-height: 70px;
|
min-height: 70px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.stat-card:hover {
|
.stat-card:hover { transform: translateY(-2px); }
|
||||||
transform: translateY(-2px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.stat-card.warning {
|
.stat-card.warning {
|
||||||
border-left-color: #f59e0b;
|
border-left-color: #f59e0b;
|
||||||
@@ -57,16 +54,6 @@
|
|||||||
background: linear-gradient(135deg, #ecfdf5 0%, #d1fae5 100%);
|
background: linear-gradient(135deg, #ecfdf5 0%, #d1fae5 100%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.stat-card.danger {
|
|
||||||
border-left-color: #ef4444;
|
|
||||||
background: linear-gradient(135deg, #fef2f2 0%, #fee2e2 100%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.stat-card.info {
|
|
||||||
border-left-color: #3b82f6;
|
|
||||||
background: linear-gradient(135deg, #eff6ff 0%, #dbeafe 100%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.stat-card.secondary {
|
.stat-card.secondary {
|
||||||
border-left-color: #8b5cf6;
|
border-left-color: #8b5cf6;
|
||||||
background: linear-gradient(135deg, #faf5ff 0%, #f3e8ff 100%);
|
background: linear-gradient(135deg, #faf5ff 0%, #f3e8ff 100%);
|
||||||
@@ -91,46 +78,19 @@
|
|||||||
.stat-card.warning .stat-icon {
|
.stat-card.warning .stat-icon {
|
||||||
background: rgba(245, 158, 11, 0.1);
|
background: rgba(245, 158, 11, 0.1);
|
||||||
}
|
}
|
||||||
|
.stat-card.warning .stat-icon i { color: #f59e0b; }
|
||||||
.stat-card.warning .stat-icon i {
|
|
||||||
color: #f59e0b;
|
|
||||||
}
|
|
||||||
|
|
||||||
.stat-card.success .stat-icon {
|
.stat-card.success .stat-icon {
|
||||||
background: rgba(16, 185, 129, 0.1);
|
background: rgba(16, 185, 129, 0.1);
|
||||||
}
|
}
|
||||||
|
.stat-card.success .stat-icon i { color: #10b981; }
|
||||||
.stat-card.success .stat-icon i {
|
|
||||||
color: #10b981;
|
|
||||||
}
|
|
||||||
|
|
||||||
.stat-card.danger .stat-icon {
|
|
||||||
background: rgba(239, 68, 68, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.stat-card.danger .stat-icon i {
|
|
||||||
color: #ef4444;
|
|
||||||
}
|
|
||||||
|
|
||||||
.stat-card.info .stat-icon {
|
|
||||||
background: rgba(59, 130, 246, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.stat-card.info .stat-icon i {
|
|
||||||
color: #3b82f6;
|
|
||||||
}
|
|
||||||
|
|
||||||
.stat-card.secondary .stat-icon {
|
.stat-card.secondary .stat-icon {
|
||||||
background: rgba(139, 92, 246, 0.1);
|
background: rgba(139, 92, 246, 0.1);
|
||||||
}
|
}
|
||||||
|
.stat-card.secondary .stat-icon i { color: #8b5cf6; }
|
||||||
|
|
||||||
.stat-card.secondary .stat-icon i {
|
.stat-content { flex: 1; }
|
||||||
color: #8b5cf6;
|
|
||||||
}
|
|
||||||
|
|
||||||
.stat-content {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.stat-value {
|
.stat-value {
|
||||||
font-size: 22px;
|
font-size: 22px;
|
||||||
@@ -147,14 +107,13 @@
|
|||||||
line-height: 1.3;
|
line-height: 1.3;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Updated Search Container - Wider with icon on left */
|
|
||||||
.search-container {
|
.search-container {
|
||||||
background: rgba(255, 255, 255, 0.9);
|
background: rgba(255, 255, 255, 0.9);
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
padding: 6px 12px;
|
padding: 6px 12px;
|
||||||
border: 1px solid rgba(0, 0, 0, 0.1);
|
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
||||||
width: 350px; /* Increased width */
|
width: 350px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
@@ -210,7 +169,6 @@
|
|||||||
font-family: 'Inter', sans-serif;
|
font-family: 'Inter', sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Updated Table Styles - Fixed horizontal scroll */
|
|
||||||
.table-glass {
|
.table-glass {
|
||||||
background: rgba(255, 255, 255, 0.9);
|
background: rgba(255, 255, 255, 0.9);
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
@@ -219,7 +177,6 @@
|
|||||||
font-family: 'Inter', sans-serif;
|
font-family: 'Inter', sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Single gradient for entire header - Blue to Purple */
|
|
||||||
.table thead {
|
.table thead {
|
||||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
|
||||||
}
|
}
|
||||||
@@ -232,20 +189,9 @@
|
|||||||
border: none;
|
border: none;
|
||||||
font-family: 'Inter', sans-serif;
|
font-family: 'Inter', sans-serif;
|
||||||
position: relative;
|
position: relative;
|
||||||
background: linear-gradient(135deg, #667eea 0%);;
|
background: linear-gradient(135deg, #667eea 0%);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Remove individual curved borders */
|
|
||||||
.table-header:first-child {
|
|
||||||
border-top-left-radius: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.table-header:last-child {
|
|
||||||
border-top-right-radius: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Apply rounded corners to the entire header container */
|
|
||||||
.table-container thead tr:first-child th:first-child {
|
.table-container thead tr:first-child th:first-child {
|
||||||
border-top-left-radius: 10px;
|
border-top-left-radius: 10px;
|
||||||
}
|
}
|
||||||
@@ -254,7 +200,6 @@
|
|||||||
border-top-right-radius: 10px;
|
border-top-right-radius: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Updated Table Column Alignment */
|
|
||||||
.table > :not(caption) > * > * {
|
.table > :not(caption) > * > * {
|
||||||
padding: 14px 12px;
|
padding: 14px 12px;
|
||||||
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
|
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
|
||||||
@@ -263,44 +208,35 @@
|
|||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Center align specific columns */
|
.table > :not(caption) > * > *:nth-child(2),
|
||||||
.table > :not(caption) > * > *:nth-child(2), /* Customer ID */
|
.table > :not(caption) > * > *:nth-child(3),
|
||||||
.table > :not(caption) > * > *:nth-child(3), /* Orders */
|
.table > :not(caption) > * > *:nth-child(4),
|
||||||
.table > :not(caption) > * > *:nth-child(4), /* Total */
|
.table > :not(caption) > * > *:nth-child(5) {
|
||||||
.table > :not(caption) > * > *:nth-child(5) { /* Create Date */
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Customer Info column should remain left-aligned */
|
.table > :not(caption) > * > *:first-child { text-align: left; }
|
||||||
.table > :not(caption) > * > *:first-child {
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Status and Actions columns should remain as is */
|
.table > :not(caption) > * > *:nth-child(6),
|
||||||
.table > :not(caption) > * > *:nth-child(6), /* Status */
|
.table > :not(caption) > * > *:nth-child(7),
|
||||||
.table > :not(caption) > * > *:nth-child(7) { /* Actions */
|
.table > :not(caption) > * > *:nth-child(8),
|
||||||
|
.table > :not(caption) > * > *:nth-child(9) {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Updated header alignment to match */
|
|
||||||
.table-header:nth-child(2),
|
.table-header:nth-child(2),
|
||||||
.table-header:nth-child(3),
|
.table-header:nth-child(3),
|
||||||
.table-header:nth-child(4),
|
.table-header:nth-child(4),
|
||||||
.table-header:nth-child(5) {
|
.table-header:nth-child(5),
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Customer Info header stays left */
|
|
||||||
.table-header:first-child {
|
|
||||||
text-align: Center;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Status and Actions headers stay centered */
|
|
||||||
.table-header:nth-child(6),
|
.table-header:nth-child(6),
|
||||||
.table-header:nth-child(7) {
|
.table-header:nth-child(7),
|
||||||
|
.table-header:nth-child(8),
|
||||||
|
.table-header:nth-child(9) {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.table-header:first-child { text-align: Center; }
|
||||||
|
|
||||||
.customer-avatar {
|
.customer-avatar {
|
||||||
width: 40px;
|
width: 40px;
|
||||||
height: 40px;
|
height: 40px;
|
||||||
@@ -374,7 +310,7 @@
|
|||||||
|
|
||||||
.customer-info-column {
|
.customer-info-column {
|
||||||
min-width: 220px;
|
min-width: 220px;
|
||||||
max-width: 220px; /* Added max-width to prevent overflow */
|
max-width: 220px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.table tbody tr {
|
.table tbody tr {
|
||||||
@@ -391,31 +327,25 @@
|
|||||||
color: #6c757d;
|
color: #6c757d;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Remove customer-stats since we're adding columns */
|
|
||||||
|
|
||||||
/* Enhanced table styling - Fixed horizontal scroll */
|
|
||||||
.table-container {
|
.table-container {
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
width: 100%; /* Ensure container takes full width */
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Fix table responsiveness */
|
|
||||||
.table-responsive {
|
.table-responsive {
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
-webkit-overflow-scrolling: touch;
|
-webkit-overflow-scrolling: touch;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Ensure table doesn't exceed container */
|
|
||||||
.table {
|
.table {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
table-layout: auto; /* Changed to auto for better column distribution */
|
table-layout: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Fix for search and filter section */
|
|
||||||
.search-filter-container {
|
.search-filter-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -439,11 +369,10 @@
|
|||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* New columns styling */
|
|
||||||
.orders-column, .total-column, .customer-id-column, .create-date-column {
|
.orders-column, .total-column, .customer-id-column, .create-date-column {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
min-width: 80px; /* Added minimum widths for consistency */
|
min-width: 80px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.orders-count {
|
.orders-count {
|
||||||
@@ -458,7 +387,6 @@
|
|||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---------- Pagination Styles ---------- */
|
|
||||||
.pagination-container {
|
.pagination-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
@@ -485,14 +413,10 @@
|
|||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pagination-btn {
|
.pagination-img-btn {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border: 1px solid #e3eaf6;
|
border: 1px solid #e3eaf6;
|
||||||
color: #1a2951;
|
|
||||||
padding: 8px 12px;
|
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
font-size: 13px;
|
|
||||||
font-weight: 600;
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -500,20 +424,19 @@
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
min-width: 40px;
|
min-width: 40px;
|
||||||
height: 32px;
|
height: 32px;
|
||||||
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pagination-btn:hover:not(:disabled) {
|
.pagination-img-btn:hover:not(:disabled) {
|
||||||
background: #1a2951;
|
background: #1a2951;
|
||||||
color: white;
|
|
||||||
border-color: #1a2951;
|
border-color: #1a2951;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pagination-btn:disabled {
|
.pagination-img-btn:disabled {
|
||||||
background: #f8fafc;
|
background: #f8fafc;
|
||||||
color: #cbd5e0;
|
|
||||||
border-color: #e2e8f0;
|
border-color: #e2e8f0;
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
opacity: 0.6;
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pagination-page-btn {
|
.pagination-page-btn {
|
||||||
@@ -551,61 +474,11 @@
|
|||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pagination-ellipsis {
|
|
||||||
color: #9ba5bb;
|
|
||||||
font-size: 13px;
|
|
||||||
padding: 0 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Image-based pagination buttons */
|
|
||||||
.pagination-img-btn {
|
|
||||||
background: #fff;
|
|
||||||
border: 1px solid #e3eaf6;
|
|
||||||
border-radius: 6px;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
min-width: 40px;
|
|
||||||
height: 32px;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pagination-img-btn:hover:not(:disabled) {
|
|
||||||
background: #1a2951;
|
|
||||||
border-color: #1a2951;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pagination-img-btn:disabled {
|
|
||||||
background: #f8fafc;
|
|
||||||
border-color: #e2e8f0;
|
|
||||||
cursor: not-allowed;
|
|
||||||
opacity: 0.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pagination-img-btn img {
|
|
||||||
width: 16px;
|
|
||||||
height: 16px;
|
|
||||||
filter: brightness(0) saturate(100%) invert(26%) sepia(89%) saturate(748%) hue-rotate(201deg) brightness(93%) contrast(89%);
|
|
||||||
transition: filter 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pagination-img-btn:hover:not(:disabled) img {
|
|
||||||
filter: brightness(0) saturate(100%) invert(100%) sepia(100%) saturate(0%) hue-rotate(288deg) brightness(106%) contrast(101%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.pagination-img-btn:disabled img {
|
|
||||||
filter: brightness(0) saturate(100%) invert(84%) sepia(8%) saturate(165%) hue-rotate(179deg) brightness(89%) contrast(86%);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Mobile responsive fixes */
|
|
||||||
@media (max-width: 1200px) {
|
@media (max-width: 1200px) {
|
||||||
.table > :not(caption) > * > * {
|
.table > :not(caption) > * > * {
|
||||||
padding: 12px 8px;
|
padding: 12px 8px;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.customer-info-column {
|
.customer-info-column {
|
||||||
min-width: 180px;
|
min-width: 180px;
|
||||||
max-width: 180px;
|
max-width: 180px;
|
||||||
@@ -613,65 +486,42 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 992px) {
|
@media (max-width: 992px) {
|
||||||
.search-container {
|
.search-container { width: 280px; }
|
||||||
width: 280px;
|
.stats-container { grid-template-columns: repeat(2, 1fr); }
|
||||||
}
|
|
||||||
|
|
||||||
.stats-container {
|
|
||||||
grid-template-columns: repeat(2, 1fr);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.stats-container {
|
.stats-container { grid-template-columns: repeat(2, 1fr); }
|
||||||
grid-template-columns: repeat(2, 1fr);
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-filter-container {
|
.search-filter-container {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
}
|
}
|
||||||
|
.search-section { justify-content: center; }
|
||||||
.search-section {
|
.search-container { width: 100%; }
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-container {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.filter-section {
|
.filter-section {
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pagination-container {
|
.pagination-container {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
}
|
}
|
||||||
|
.pagination-controls { justify-content: center; }
|
||||||
.pagination-controls {
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.table > :not(caption) > * > * {
|
.table > :not(caption) > * > * {
|
||||||
padding: 10px 6px;
|
padding: 10px 6px;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.customer-info-column {
|
.customer-info-column {
|
||||||
min-width: 150px;
|
min-width: 150px;
|
||||||
max-width: 150px;
|
max-width: 150px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.customer-avatar {
|
.customer-avatar {
|
||||||
width: 32px;
|
width: 32px;
|
||||||
height: 32px;
|
height: 32px;
|
||||||
font-size: 0.8rem;
|
font-size: 0.8rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.action-btn {
|
.action-btn {
|
||||||
width: 26px;
|
width: 26px;
|
||||||
height: 26px;
|
height: 26px;
|
||||||
@@ -680,19 +530,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 576px) {
|
@media (max-width: 576px) {
|
||||||
.stats-container {
|
.stats-container { grid-template-columns: 1fr; }
|
||||||
grid-template-columns: 1fr;
|
.table-responsive { font-size: 12px; }
|
||||||
}
|
|
||||||
|
|
||||||
.table-responsive {
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.customer-info-column {
|
.customer-info-column {
|
||||||
min-width: 120px;
|
min-width: 120px;
|
||||||
max-width: 120px;
|
max-width: 120px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.premium-badge,
|
.premium-badge,
|
||||||
.regular-badge,
|
.regular-badge,
|
||||||
.status-badge {
|
.status-badge {
|
||||||
@@ -703,14 +546,11 @@
|
|||||||
</style>
|
</style>
|
||||||
|
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<!-- Header - Removed gradient -->
|
|
||||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||||
<h4 style="color: #2c3e50; font-weight: 700; font-family: 'Inter', sans-serif;">Customer List</h4>
|
<h4 style="color: #2c3e50; font-weight: 700; font-family: 'Inter', sans-serif;">Customer List</h4>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Stats Cards with NEW DESIGN -->
|
|
||||||
<div class="stats-container">
|
<div class="stats-container">
|
||||||
<!-- Total Customers -->
|
|
||||||
<div class="stat-card">
|
<div class="stat-card">
|
||||||
<div class="stat-icon">
|
<div class="stat-icon">
|
||||||
<i class="bi bi-people-fill"></i>
|
<i class="bi bi-people-fill"></i>
|
||||||
@@ -721,7 +561,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- New This Month -->
|
|
||||||
<div class="stat-card warning">
|
<div class="stat-card warning">
|
||||||
<div class="stat-icon">
|
<div class="stat-icon">
|
||||||
<i class="bi bi-person-plus"></i>
|
<i class="bi bi-person-plus"></i>
|
||||||
@@ -739,7 +578,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Active Customers -->
|
|
||||||
<div class="stat-card success">
|
<div class="stat-card success">
|
||||||
<div class="stat-icon">
|
<div class="stat-icon">
|
||||||
<i class="bi bi-activity"></i>
|
<i class="bi bi-activity"></i>
|
||||||
@@ -755,7 +593,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Premium Customers -->
|
|
||||||
<div class="stat-card secondary">
|
<div class="stat-card secondary">
|
||||||
<div class="stat-icon">
|
<div class="stat-icon">
|
||||||
<i class="bi bi-award-fill"></i>
|
<i class="bi bi-award-fill"></i>
|
||||||
@@ -772,10 +609,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Search and Filter Section -->
|
|
||||||
<div class="glass-card p-3 mb-3">
|
<div class="glass-card p-3 mb-3">
|
||||||
<div class="search-filter-container">
|
<div class="search-filter-container">
|
||||||
<!-- Search Section - Wider with icon on left -->
|
|
||||||
<div class="search-section">
|
<div class="search-section">
|
||||||
<form method="GET" action="{{ route('admin.customers.index') }}" class="d-flex align-items-center">
|
<form method="GET" action="{{ route('admin.customers.index') }}" class="d-flex align-items-center">
|
||||||
<div class="search-container">
|
<div class="search-container">
|
||||||
@@ -792,7 +627,6 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Filter Section -->
|
|
||||||
<div class="filter-section">
|
<div class="filter-section">
|
||||||
<a href="{{ route('admin.customers.index', ['status'=>'active', 'search'=>$search ?? '']) }}"
|
<a href="{{ route('admin.customers.index', ['status'=>'active', 'search'=>$search ?? '']) }}"
|
||||||
class="filter-btn {{ ($status ?? '') == 'active' ? 'active' : '' }}">
|
class="filter-btn {{ ($status ?? '') == 'active' ? 'active' : '' }}">
|
||||||
@@ -818,7 +652,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Customer List Table -->
|
|
||||||
<div class="table-container">
|
<div class="table-container">
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="table table-hover align-middle mb-0">
|
<table class="table table-hover align-middle mb-0">
|
||||||
@@ -828,8 +661,8 @@
|
|||||||
<th class="table-header">Customer ID</th>
|
<th class="table-header">Customer ID</th>
|
||||||
<th class="table-header">Orders</th>
|
<th class="table-header">Orders</th>
|
||||||
<th class="table-header">Order Total</th>
|
<th class="table-header">Order Total</th>
|
||||||
<th class="table-header">Total Payable</th> {{-- NEW --}}
|
<th class="table-header">Total Payable</th>
|
||||||
<th class="table-header">Remaining</th> {{-- NEW --}}
|
<th class="table-header">Remaining</th>
|
||||||
<th class="table-header">Create Date</th>
|
<th class="table-header">Create Date</th>
|
||||||
<th class="table-header">Status</th>
|
<th class="table-header">Status</th>
|
||||||
<th class="table-header" width="100">Actions</th>
|
<th class="table-header" width="100">Actions</th>
|
||||||
@@ -839,19 +672,22 @@
|
|||||||
<tbody id="customersTableBody">
|
<tbody id="customersTableBody">
|
||||||
@forelse($customers as $c)
|
@forelse($customers as $c)
|
||||||
@php
|
@php
|
||||||
// Invoice total (with GST)
|
// Orders = invoice count
|
||||||
$totalPayable = $c->invoices->sum('final_amount_with_gst');
|
$ordersCount = $c->invoices->count();
|
||||||
|
|
||||||
|
// Order Total = items total from all invoices (final_amount)
|
||||||
|
$orderTotal = $c->invoices->sum('final_amount');
|
||||||
|
|
||||||
|
// Total payable = grand total with GST + groups
|
||||||
|
$totalPayable = $c->invoices->sum('grand_total_with_charges');
|
||||||
|
|
||||||
// Total paid via installments
|
// Total paid via installments
|
||||||
$totalPaid = $c->invoices
|
$totalPaid = $c->invoiceInstallments->sum('amount');
|
||||||
->flatMap(fn($inv) => $inv->installments)
|
|
||||||
->sum('amount');
|
|
||||||
|
|
||||||
// Remaining amount
|
// Remaining amount
|
||||||
$remainingAmount = max($totalPayable - $totalPaid, 0);
|
$remainingAmount = max($totalPayable - $totalPaid, 0);
|
||||||
@endphp
|
@endphp
|
||||||
<tr>
|
<tr>
|
||||||
<!-- Customer Info Column -->
|
|
||||||
<td class="customer-info-column">
|
<td class="customer-info-column">
|
||||||
<div class="d-flex align-items-start">
|
<div class="d-flex align-items-start">
|
||||||
<div class="customer-avatar me-3">
|
<div class="customer-avatar me-3">
|
||||||
@@ -872,20 +708,17 @@
|
|||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<!-- Customer ID -->
|
|
||||||
<td class="customer-id-column">
|
<td class="customer-id-column">
|
||||||
<span class="fw-bold text-primary">{{ $c->customer_id }}</span>
|
<span class="fw-bold text-primary">{{ $c->customer_id }}</span>
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<!-- Orders Column -->
|
|
||||||
<td class="orders-column">
|
<td class="orders-column">
|
||||||
<span class="orders-count">{{ $c->orders->count() }}</span>
|
<span class="orders-count">{{ $ordersCount }}</span>
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<!-- Total Column -->
|
|
||||||
<td class="total-column">
|
<td class="total-column">
|
||||||
<span class="total-amount">
|
<span class="total-amount">
|
||||||
₹{{ number_format($c->orders->sum('ttl_amount'), 2) }}
|
₹{{ number_format($orderTotal, 2) }}
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
@@ -907,16 +740,10 @@
|
|||||||
@endif
|
@endif
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!-- Create Date -->
|
|
||||||
<td class="create-date-column">
|
<td class="create-date-column">
|
||||||
<span class="text-muted">{{ $c->created_at ? $c->created_at->format('d-m-Y') : '-' }}</span>
|
<span class="text-muted">{{ $c->created_at ? $c->created_at->format('d-m-Y') : '-' }}</span>
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<!-- Status -->
|
|
||||||
<td>
|
<td>
|
||||||
@if($c->status === 'active')
|
@if($c->status === 'active')
|
||||||
<span class="status-badge active-status">Active</span>
|
<span class="status-badge active-status">Active</span>
|
||||||
@@ -925,7 +752,6 @@
|
|||||||
@endif
|
@endif
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<!-- Actions -->
|
|
||||||
<td>
|
<td>
|
||||||
<div class="d-flex justify-content-center">
|
<div class="d-flex justify-content-center">
|
||||||
<a href="{{ route('admin.customers.view', $c->id) }}"
|
<a href="{{ route('admin.customers.view', $c->id) }}"
|
||||||
@@ -945,7 +771,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
@empty
|
@empty
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="7" class="text-center py-4">
|
<td colspan="9" class="text-center py-4">
|
||||||
<i class="bi bi-people display-4 text-muted d-block mb-2"></i>
|
<i class="bi bi-people display-4 text-muted d-block mb-2"></i>
|
||||||
<span class="text-muted">No customers found.</span>
|
<span class="text-muted">No customers found.</span>
|
||||||
</td>
|
</td>
|
||||||
@@ -956,14 +782,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Pagination Controls -->
|
|
||||||
<div class="pagination-container">
|
<div class="pagination-container">
|
||||||
<div class="pagination-info" id="pageInfo">
|
<div class="pagination-info" id="pageInfo">
|
||||||
Showing {{ $customers->firstItem() ?? 0 }} to {{ $customers->lastItem() ?? 0 }} of {{ $customers->total() }} entries
|
Showing {{ $customers->firstItem() ?? 0 }} to {{ $customers->lastItem() ?? 0 }} of {{ $customers->total() }} entries
|
||||||
</div>
|
</div>
|
||||||
<div class="pagination-controls">
|
<div class="pagination-controls">
|
||||||
<button class="pagination-img-btn" id="prevPageBtn" title="Previous page" {{ $customers->onFirstPage() ? 'disabled' : '' }}>
|
<button class="pagination-img-btn" id="prevPageBtn" title="Previous page" {{ $customers->onFirstPage() ? 'disabled' : '' }}>
|
||||||
<!-- Left arrow SVG -->
|
|
||||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path d="M10 12L6 8L10 4" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
<path d="M10 12L6 8L10 4" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
</svg>
|
</svg>
|
||||||
@@ -977,7 +801,6 @@
|
|||||||
@endfor
|
@endfor
|
||||||
</div>
|
</div>
|
||||||
<button class="pagination-img-btn" id="nextPageBtn" title="Next page" {{ $customers->hasMorePages() ? '' : 'disabled' }}>
|
<button class="pagination-img-btn" id="nextPageBtn" title="Next page" {{ $customers->hasMorePages() ? '' : 'disabled' }}>
|
||||||
<!-- Right arrow SVG -->
|
|
||||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path d="M6 4L10 8L6 12" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
<path d="M6 4L10 8L6 12" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
</svg>
|
</svg>
|
||||||
@@ -988,7 +811,6 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
// Add hover effects to table rows
|
|
||||||
const tableRows = document.querySelectorAll('.table tbody tr');
|
const tableRows = document.querySelectorAll('.table tbody tr');
|
||||||
tableRows.forEach(row => {
|
tableRows.forEach(row => {
|
||||||
row.addEventListener('mouseenter', function() {
|
row.addEventListener('mouseenter', function() {
|
||||||
@@ -1000,7 +822,6 @@
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Pagination button handlers
|
|
||||||
document.getElementById('prevPageBtn').addEventListener('click', function() {
|
document.getElementById('prevPageBtn').addEventListener('click', function() {
|
||||||
@if(!$customers->onFirstPage())
|
@if(!$customers->onFirstPage())
|
||||||
window.location.href = '{{ $customers->previousPageUrl() }}';
|
window.location.href = '{{ $customers->previousPageUrl() }}';
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
@section('page-title', 'Customer Details')
|
@section('page-title', 'Customer Details')
|
||||||
|
|
||||||
@section('content')
|
@section('content')
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
:root {
|
:root {
|
||||||
--primary-gradient: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
--primary-gradient: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
@@ -33,10 +32,12 @@
|
|||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
background: linear-gradient(45deg,
|
background: linear-gradient(
|
||||||
|
45deg,
|
||||||
rgba(102, 126, 234, 0.03) 0%,
|
rgba(102, 126, 234, 0.03) 0%,
|
||||||
rgba(118, 75, 162, 0.03) 50%,
|
rgba(118, 75, 162, 0.03) 50%,
|
||||||
rgba(16, 185, 129, 0.03) 100%);
|
rgba(16, 185, 129, 0.03) 100%
|
||||||
|
);
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,10 +82,12 @@
|
|||||||
left: -50%;
|
left: -50%;
|
||||||
width: 200%;
|
width: 200%;
|
||||||
height: 200%;
|
height: 200%;
|
||||||
background: linear-gradient(45deg,
|
background: linear-gradient(
|
||||||
|
45deg,
|
||||||
transparent 0%,
|
transparent 0%,
|
||||||
rgba(255, 255, 255, 0.1) 50%,
|
rgba(255, 255, 255, 0.1) 50%,
|
||||||
transparent 100%);
|
transparent 100%
|
||||||
|
);
|
||||||
animation: headerShimmer 8s infinite linear;
|
animation: headerShimmer 8s infinite linear;
|
||||||
transform: rotate(45deg);
|
transform: rotate(45deg);
|
||||||
}
|
}
|
||||||
@@ -333,7 +336,7 @@
|
|||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Buttons - FIXED POSITIONING */
|
/* Buttons */
|
||||||
.btn-back {
|
.btn-back {
|
||||||
background: rgba(255, 255, 255, 0.2);
|
background: rgba(255, 255, 255, 0.2);
|
||||||
border: 2px solid rgba(255, 255, 255, 0.3);
|
border: 2px solid rgba(255, 255, 255, 0.3);
|
||||||
@@ -358,10 +361,12 @@
|
|||||||
left: -100%;
|
left: -100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background: linear-gradient(90deg,
|
background: linear-gradient(
|
||||||
|
90deg,
|
||||||
transparent,
|
transparent,
|
||||||
rgba(255, 255, 255, 0.2),
|
rgba(255, 255, 255, 0.2),
|
||||||
transparent);
|
transparent
|
||||||
|
);
|
||||||
transition: left 0.6s ease;
|
transition: left 0.6s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -397,10 +402,12 @@
|
|||||||
left: -100%;
|
left: -100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background: linear-gradient(90deg,
|
background: linear-gradient(
|
||||||
|
90deg,
|
||||||
transparent,
|
transparent,
|
||||||
rgba(255, 255, 255, 0.2),
|
rgba(255, 255, 255, 0.2),
|
||||||
transparent);
|
transparent
|
||||||
|
);
|
||||||
transition: left 0.6s ease;
|
transition: left 0.6s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -497,7 +504,7 @@
|
|||||||
.animation-delay-3 { animation-delay: 0.3s; }
|
.animation-delay-3 { animation-delay: 0.3s; }
|
||||||
.animation-delay-4 { animation-delay: 0.4s; }
|
.animation-delay-4 { animation-delay: 0.4s; }
|
||||||
|
|
||||||
/* Header Button Container - FIXED */
|
/* Header Button Container */
|
||||||
.header-actions {
|
.header-actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -541,7 +548,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 576px) {
|
@media (max-width: 576px) {
|
||||||
.btn-back, .btn-outline-secondary {
|
.btn-back,
|
||||||
|
.btn-outline-secondary {
|
||||||
padding: 10px 20px;
|
padding: 10px 20px;
|
||||||
font-size: 0.9rem;
|
font-size: 0.9rem;
|
||||||
}
|
}
|
||||||
@@ -560,8 +568,7 @@
|
|||||||
</style>
|
</style>
|
||||||
|
|
||||||
<div class="container py-4">
|
<div class="container py-4">
|
||||||
|
{{-- HEADER --}}
|
||||||
{{-- HEADER - FIXED BUTTON POSITION --}}
|
|
||||||
<div class="page-header animate-fade-in">
|
<div class="page-header animate-fade-in">
|
||||||
<div class="row align-items-center">
|
<div class="row align-items-center">
|
||||||
<div class="col-md-8">
|
<div class="col-md-8">
|
||||||
@@ -703,42 +710,27 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- {{-- Total Payable --}}
|
{{-- Total Payable --}}
|
||||||
<div class="stats-card amount">
|
<div class="col-md-4 animate-fade-in animation-delay-3">
|
||||||
|
<div class="stats-card marks">
|
||||||
<div class="stats-icon">
|
<div class="stats-icon">
|
||||||
<i class="bi bi-wallet2"></i>
|
<i class="bi bi-wallet2"></i>
|
||||||
</div>
|
</div>
|
||||||
<div class="stats-value">₹{{ number_format($totalPayable, 2) }}</div>
|
<div class="stats-value">₹{{ number_format($totalPayable, 2) }}</div>
|
||||||
<div class="stats-label">Total Payable</div>
|
<div class="stats-label">Total Payable</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{{-- Total Remaining --}}
|
{{-- Remaining Amount --}}
|
||||||
|
<div class="col-md-4 animate-fade-in animation-delay-3">
|
||||||
<div class="stats-card marks">
|
<div class="stats-card marks">
|
||||||
<div class="stats-icon">
|
<div class="stats-icon">
|
||||||
<i class="bi bi-exclamation-circle"></i>
|
<i class="bi bi-exclamation-circle"></i>
|
||||||
</div>
|
</div>
|
||||||
<div class="stats-value">₹{{ number_format($totalRemaining, 2) }}</div>
|
<div class="stats-value">₹{{ number_format($totalRemaining, 2) }}</div>
|
||||||
<div class="stats-label">Remaining Amount</div>
|
<div class="stats-label">Remaining Amount</div>
|
||||||
</div> -->
|
|
||||||
<div class="col-md-4 animate-fade-in animation-delay-3">
|
|
||||||
<div class="stats-card marks">
|
|
||||||
<div class="stats-icon">
|
|
||||||
<i class="bi bi-hash"></i>
|
|
||||||
</div>
|
|
||||||
<div class="stats-value">₹{{ number_format($totalPayable, 2) }}</div>
|
|
||||||
<div class="stats-label">Total Payable</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-4 animate-fade-in animation-delay-3">
|
|
||||||
<div class="stats-card marks">
|
|
||||||
<div class="stats-icon">
|
|
||||||
<i class="bi bi-hash"></i>
|
|
||||||
</div>
|
|
||||||
<div class="stats-value">₹{{ number_format($totalRemaining, 2) }}</div>
|
|
||||||
<div class="stats-label">Remaining Amount</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
{{-- Mark Count --}}
|
{{-- Mark Count --}}
|
||||||
<div class="col-md-4 animate-fade-in animation-delay-3">
|
<div class="col-md-4 animate-fade-in animation-delay-3">
|
||||||
@@ -783,7 +775,6 @@
|
|||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@@ -793,7 +784,11 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
|
|
||||||
interactiveElements.forEach(element => {
|
interactiveElements.forEach(element => {
|
||||||
element.addEventListener('mouseenter', function () {
|
element.addEventListener('mouseenter', function () {
|
||||||
this.style.transform = this.classList.contains('mark-item') ? 'translateX(5px)' : 'translateY(-5px)';
|
if (this.classList.contains('mark-item')) {
|
||||||
|
this.style.transform = 'translateX(5px)';
|
||||||
|
} else {
|
||||||
|
this.style.transform = 'translateY(-5px)';
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
element.addEventListener('mouseleave', function () {
|
element.addEventListener('mouseleave', function () {
|
||||||
@@ -817,5 +812,4 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@endsection
|
@endsection
|
||||||
@@ -1298,7 +1298,7 @@ body {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- ORDER MANAGEMENT -->
|
<!-- ORDER MANAGEMENT -->
|
||||||
<div class="order-mgmt-box">
|
<!-- <div class="order-mgmt-box">
|
||||||
<div class="order-mgmt-bar">
|
<div class="order-mgmt-bar">
|
||||||
<span class="order-mgmt-title"><i class="bi bi-table"></i> Order Management</span>
|
<span class="order-mgmt-title"><i class="bi bi-table"></i> Order Management</span>
|
||||||
@can('order.create')
|
@can('order.create')
|
||||||
@@ -1307,9 +1307,9 @@ body {
|
|||||||
</button>
|
</button>
|
||||||
@endcan
|
@endcan
|
||||||
</div>
|
</div>
|
||||||
<div class="order-mgmt-main">
|
<div class="order-mgmt-main"> -->
|
||||||
<!-- RECENT ORDERS TABLE -->
|
<!-- RECENT ORDERS TABLE -->
|
||||||
<div class="card shadow-sm">
|
<!-- <div class="card shadow-sm">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<strong>Recent Orders</strong>
|
<strong>Recent Orders</strong>
|
||||||
<span style="font-size:13px;color:rgba(255,255,255,0.8);margin-left:10px;">
|
<span style="font-size:13px;color:rgba(255,255,255,0.8);margin-left:10px;">
|
||||||
@@ -1421,20 +1421,20 @@ body {
|
|||||||
@endforelse
|
@endforelse
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div> -->
|
||||||
|
|
||||||
<!-- Pagination Controls -->
|
<!-- Pagination Controls -->
|
||||||
<div class="pagination-container">
|
<!-- <div class="pagination-container">
|
||||||
<div class="pagination-info" id="pageInfo">Showing 1 to 10 of {{ $orders->count() }} entries</div>
|
<div class="pagination-info" id="pageInfo">Showing 1 to 10 of {{ $orders->count() }} entries</div>
|
||||||
<div class="pagination-controls">
|
<div class="pagination-controls">
|
||||||
<button class="pagination-img-btn" id="prevPageBtn" title="Previous page">
|
<button class="pagination-img-btn" id="prevPageBtn" title="Previous page">
|
||||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path d="M10 12L6 8L10 4" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
<path d="M10 12L6 8L10 4" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button> -->
|
||||||
<div class="pagination-pages" id="paginationPages">
|
<!-- <div class="pagination-pages" id="paginationPages"> -->
|
||||||
<!-- Page numbers will be inserted here -->
|
<!-- Page numbers will be inserted here -->
|
||||||
</div>
|
<!-- </div>
|
||||||
<button class="pagination-img-btn" id="nextPageBtn" title="Next page">
|
<button class="pagination-img-btn" id="nextPageBtn" title="Next page">
|
||||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path d="M6 4L10 8L6 12" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
<path d="M6 4L10 8L6 12" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
@@ -1447,7 +1447,7 @@ body {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div> -->
|
||||||
|
|
||||||
<!-- CREATE ORDER MODAL -->
|
<!-- CREATE ORDER MODAL -->
|
||||||
<div class="create-order-modal" id="createOrderModal">
|
<div class="create-order-modal" id="createOrderModal">
|
||||||
|
|||||||
@@ -8,10 +8,10 @@
|
|||||||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
|
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
|
||||||
|
|
||||||
/*Remove horizontal scroll bar*/
|
/*Remove horizontal scroll bar*/
|
||||||
html, body {
|
/* html, body {
|
||||||
overflow-x: hidden !important;
|
overflow-x: hidden !important;
|
||||||
font-family: 'Inter', sans-serif !important;
|
font-family: 'Inter', sans-serif !important;
|
||||||
}
|
} */
|
||||||
|
|
||||||
/* Invoice Management Styles */
|
/* Invoice Management Styles */
|
||||||
.invoice-management-box {
|
.invoice-management-box {
|
||||||
@@ -50,43 +50,50 @@
|
|||||||
color: #336ad3;
|
color: #336ad3;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Tools Row Styling */
|
/* ===== UPDATED FILTER BAR WITH DATE RANGE ===== */
|
||||||
.invoice-tools-row {
|
.invoice-tools-row {
|
||||||
background: linear-gradient(135deg, #f8fafc 0%, #edf2f7 100%);
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
padding: 25px 30px;
|
padding: 16px 24px;
|
||||||
border-bottom: 1px solid #e2e8f0;
|
border-bottom: 1px solid rgba(255,255,255,0.1);
|
||||||
display: flex;
|
box-shadow: 0 10px 25px -5px rgba(99, 102, 241, 0.3);
|
||||||
justify-content: space-between;
|
position: relative;
|
||||||
align-items: center;
|
border-radius: 17px 17px 0 0;
|
||||||
gap: 20px;
|
top:5px;
|
||||||
flex-wrap: wrap;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.filter-bar-container {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 16px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Search Box Styling */
|
||||||
.search-box {
|
.search-box {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
background: white;
|
background: white;
|
||||||
border-radius: 12px;
|
border-radius: 50px;
|
||||||
padding: 10px 18px;
|
padding: 8px 20px;
|
||||||
box-shadow: 0 4px 15px rgba(0,0,0,0.08);
|
box-shadow: 0 4px 15px rgba(0,0,0,0.08), inset 0 1px 2px rgba(255,255,255,0.5);
|
||||||
border: 1.5px solid #e2e8f0;
|
border: 1px solid rgba(255,255,255,0.3);
|
||||||
min-width: 380px;
|
flex: 2;
|
||||||
flex: 1;
|
min-width: 250px;
|
||||||
max-width: 500px;
|
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
font-family: 'Inter', sans-serif;
|
font-family: 'Inter', sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-box:focus-within {
|
.search-box:focus-within {
|
||||||
border-color: #3b82f6;
|
box-shadow: 0 8px 20px rgba(79, 70, 229, 0.25);
|
||||||
box-shadow: 0 4px 20px rgba(59, 130, 246, 0.15);
|
|
||||||
transform: translateY(-2px);
|
transform: translateY(-2px);
|
||||||
|
border-color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-box i {
|
.search-box i {
|
||||||
color: #64748b;
|
color: #667eea;
|
||||||
margin-right: 12px;
|
margin-right: 12px;
|
||||||
font-size: 1.2rem;
|
font-size: 1.1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-box input {
|
.search-box input {
|
||||||
@@ -95,93 +102,189 @@
|
|||||||
background: transparent;
|
background: transparent;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
color: #334155;
|
color: #1f2937;
|
||||||
font-family: 'Inter', sans-serif;
|
font-family: 'Inter', sans-serif;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-box input::placeholder {
|
.search-box input::placeholder {
|
||||||
color: #94a3b8;
|
color: #9ca3af;
|
||||||
font-family: 'Inter', sans-serif;
|
font-family: 'Inter', sans-serif;
|
||||||
|
font-weight: 300;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* .search-btn {
|
/* Filter Selects Styling */
|
||||||
background: linear-gradient(90deg, #226ad6, #46b4fd 123%);
|
.filter-select-wrapper {
|
||||||
border: none;
|
flex: 1;
|
||||||
border-radius: 8px;
|
min-width: 140px;
|
||||||
padding: 5px 10px;
|
|
||||||
color: white;
|
|
||||||
font-weight: 600;
|
|
||||||
font-size: 14px;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 8px;
|
|
||||||
margin-left: 3px;
|
|
||||||
font-family: 'Inter', sans-serif;
|
|
||||||
white-space: nowrap;
|
|
||||||
} */
|
|
||||||
|
|
||||||
.search-btn:hover {
|
|
||||||
background: linear-gradient(90deg, #3264f8, #3acfff 140%);
|
|
||||||
box-shadow: 0 4px 15px rgba(59, 130, 246, 0.4);
|
|
||||||
transform: translateY(-1px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.filter-group {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 15px;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.filter-select {
|
.filter-select {
|
||||||
|
width: 100%;
|
||||||
background: white;
|
background: white;
|
||||||
border: 1.5px solid #e2e8f0;
|
border: 1px solid rgba(255,255,255,0.5);
|
||||||
border-radius: 10px;
|
border-radius: 50px;
|
||||||
padding: 10px 15px;
|
padding: 10px 20px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: #334155;
|
color: #1f2937;
|
||||||
outline: none;
|
outline: none;
|
||||||
min-width: 160px;
|
box-shadow: 0 4px 10px rgba(0,0,0,0.05);
|
||||||
box-shadow: 0 2px 8px rgba(0,0,0,0.06);
|
|
||||||
font-family: 'Inter', sans-serif;
|
font-family: 'Inter', sans-serif;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
|
cursor: pointer;
|
||||||
|
appearance: none;
|
||||||
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23667eea' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: right 15px center;
|
||||||
|
background-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-select:hover {
|
||||||
|
background-color: white;
|
||||||
|
border-color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
.filter-select:focus {
|
.filter-select:focus {
|
||||||
border-color: #3b82f6;
|
border-color: white;
|
||||||
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
|
box-shadow: 0 0 0 3px rgba(255,255,255,0.2);
|
||||||
|
background-color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
.create-invoice-btn {
|
/* ===== NEW: Status dropdown option colors ===== */
|
||||||
background: linear-gradient(90deg, #226ad6, #46b4fd 123%);
|
.filter-select option[value="paid"] {
|
||||||
padding: 12px 28px;
|
background-color: #d1fae5;
|
||||||
font-weight: 600;
|
color: #065f46;
|
||||||
border: none;
|
font-weight: 500;
|
||||||
border-radius: 10px;
|
}
|
||||||
color: #fff;
|
|
||||||
font-size: 15px;
|
.filter-select option[value="pending"] {
|
||||||
box-shadow: 0 2px 13px #dde7fa42;
|
background-color: #fef3c7;
|
||||||
transition: all 0.3s ease;
|
color: #d97706;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-select option[value="overdue"] {
|
||||||
|
background-color: #e9d5ff;
|
||||||
|
color: #6b21a8;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-select option[value="all"] {
|
||||||
|
background-color: white;
|
||||||
|
color: #1f2937;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* For Firefox compatibility */
|
||||||
|
.filter-select option {
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
/* ===== END status dropdown option colors ===== */
|
||||||
|
|
||||||
|
/* Date Range Styling */
|
||||||
|
.date-range-wrapper {
|
||||||
|
flex: 1.5;
|
||||||
|
min-width: 280px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
font-family: 'Inter', sans-serif;
|
/* background: rgba(255, 255, 255, 0.2); */
|
||||||
text-decoration: none;
|
padding: 5px 12px;
|
||||||
white-space: nowrap;
|
border-radius: 50px;
|
||||||
|
backdrop-filter: blur(5px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.create-invoice-btn:hover {
|
.date-input-container {
|
||||||
background: linear-gradient(90deg, #3264f8, #3acfff 140%);
|
display: flex;
|
||||||
box-shadow: 0 4px 25px #5ab8f880;
|
align-items: center;
|
||||||
color: #fff;
|
background: white;
|
||||||
text-decoration: none;
|
border-radius: 50px;
|
||||||
transform: translateY(-2px);
|
padding: 5px 12px;
|
||||||
|
flex: 1;
|
||||||
|
border: 1px solid rgba(255,255,255,0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.date-input-container i {
|
||||||
|
color: #667eea;
|
||||||
|
margin-right: 6px;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.date-input {
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
background: transparent;
|
||||||
|
width: 100%;
|
||||||
|
font-size: 13px;
|
||||||
|
color: #1f2937;
|
||||||
|
font-family: 'Inter', sans-serif;
|
||||||
|
padding: 5px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.date-input::placeholder {
|
||||||
|
color: #9ca3af;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.date-separator {
|
||||||
|
color: white;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 14px;
|
||||||
|
text-shadow: 0 1px 2px rgba(0,0,0,0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive Design */
|
||||||
|
@media (max-width: 1100px) {
|
||||||
|
.filter-bar-container {
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-box {
|
||||||
|
min-width: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.date-range-wrapper {
|
||||||
|
min-width: 260px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.invoice-tools-row {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-bar-container {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-box {
|
||||||
|
min-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-select-wrapper {
|
||||||
|
min-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.date-range-wrapper {
|
||||||
|
min-width: 100%;
|
||||||
|
flex-direction: column;
|
||||||
|
background: transparent;
|
||||||
|
backdrop-filter: none;
|
||||||
|
padding: 0;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.date-input-container {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.date-separator {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* ===== END UPDATED FILTER BAR ===== */
|
||||||
|
|
||||||
.invoice-management-main {
|
.invoice-management-main {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border-radius: 0 0 17px 17px;
|
border-radius: 0 0 17px 17px;
|
||||||
@@ -569,37 +672,6 @@
|
|||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Date Range Picker Styles */
|
|
||||||
.date-range-container {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.date-input {
|
|
||||||
background: white;
|
|
||||||
border: 1.5px solid #e2e8f0;
|
|
||||||
border-radius: 8px;
|
|
||||||
padding: 10px 12px;
|
|
||||||
font-size: 14px;
|
|
||||||
color: #334155;
|
|
||||||
outline: none;
|
|
||||||
min-width: 140px;
|
|
||||||
box-shadow: 0 2px 5px rgba(0,0,0,0.04);
|
|
||||||
font-family: 'Inter', sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
.date-input:focus {
|
|
||||||
border-color: #3b82f6;
|
|
||||||
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.date-separator {
|
|
||||||
color: #000000ff;
|
|
||||||
font-weight: 500;
|
|
||||||
font-family: 'Inter', sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Stats Summary - Centered */
|
/* Stats Summary - Centered */
|
||||||
.stats-summary {
|
.stats-summary {
|
||||||
display: grid;
|
display: grid;
|
||||||
@@ -950,17 +1022,6 @@
|
|||||||
|
|
||||||
/* Responsive Design */
|
/* Responsive Design */
|
||||||
@media (max-width: 1200px) {
|
@media (max-width: 1200px) {
|
||||||
.invoice-tools-row {
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: stretch;
|
|
||||||
gap: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-box {
|
|
||||||
max-width: 100%;
|
|
||||||
min-width: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.filter-group {
|
.filter-group {
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -978,10 +1039,6 @@
|
|||||||
padding: 15px 20px;
|
padding: 15px 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.invoice-tools-row {
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.table th, .table td {
|
.table th, .table td {
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
padding: 15px 10px;
|
padding: 15px 10px;
|
||||||
@@ -1003,21 +1060,6 @@
|
|||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.filter-group {
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: stretch;
|
|
||||||
}
|
|
||||||
|
|
||||||
.filter-select {
|
|
||||||
min-width: auto;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.create-invoice-btn {
|
|
||||||
width: 100%;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.invoice-icon {
|
.invoice-icon {
|
||||||
width: 32px;
|
width: 32px;
|
||||||
height: 32px;
|
height: 32px;
|
||||||
@@ -1033,15 +1075,6 @@
|
|||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.date-range-container {
|
|
||||||
flex-direction: column;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.date-input {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.stats-summary {
|
.stats-summary {
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
margin: 20px;
|
margin: 20px;
|
||||||
@@ -1079,11 +1112,6 @@
|
|||||||
min-width: auto;
|
min-width: auto;
|
||||||
padding: 10px 15px;
|
padding: 10px 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-btn {
|
|
||||||
padding: 8px 15px;
|
|
||||||
font-size: 13px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
@@ -1096,56 +1124,58 @@
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- TOOLS ROW - Search, Filter, Create Button -->
|
<!-- UPDATED FILTER BAR WITH DATE RANGE -->
|
||||||
<div class="invoice-tools-row">
|
<div class="invoice-tools-row">
|
||||||
<!-- Search Box with Button -->
|
<form method="GET" action="{{ route('admin.invoices.index') }}" style="width: 100%;">
|
||||||
<form method="GET" action="{{ route('admin.invoices.index') }}">
|
<div class="filter-bar-container">
|
||||||
<div class="invoice-tools-row">
|
<!-- Search Box -->
|
||||||
<div style="display:flex; align-items:center; flex:1; max-width:500px; min-width:380px;">
|
|
||||||
<div class="search-box">
|
<div class="search-box">
|
||||||
<i class="bi bi-search"></i>
|
<i class="bi bi-search"></i>
|
||||||
<input type="text"
|
<input type="text"
|
||||||
id="invoiceSearch"
|
id="searchInput"
|
||||||
name="search"
|
name="search"
|
||||||
value="{{ request('search') }}"
|
value="{{ request('search') }}"
|
||||||
placeholder="Search by invoice number, customer name...">
|
placeholder="Search Invoices...">
|
||||||
</div>
|
|
||||||
<button type="submit" id="searchButton" class="search-btn">
|
|
||||||
Search
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="filter-group">
|
<!-- Status Filter -->
|
||||||
|
<div class="filter-select-wrapper">
|
||||||
<select class="filter-select" id="statusFilter" name="status">
|
<select class="filter-select" id="statusFilter" name="status">
|
||||||
<option value="all">All Status</option>
|
<option value="all" {{ request('status')=='all' ? 'selected' : '' }}>All Status</option>
|
||||||
<option value="paid" {{ request('status')=='paid' ? 'selected' : '' }}>Paid</option>
|
<option value="paid" {{ request('status')=='paid' ? 'selected' : '' }}>Paid</option>
|
||||||
<option value="pending" {{ request('status')=='pending' ? 'selected' : '' }}>Pending</option>
|
<option value="pending" {{ request('status')=='pending' ? 'selected' : '' }}>Pending</option>
|
||||||
<option value="overdue" {{ request('status')=='overdue' ? 'selected' : '' }}>Overdue</option>
|
<option value="overdue" {{ request('status')=='overdue' ? 'selected' : '' }}>Overdue</option>
|
||||||
</select>
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="date-range-container">
|
<!-- Date Range Filter (FROM - TO) with dd-mm-yyyy format -->
|
||||||
<input type="date"
|
<div class="date-range-wrapper">
|
||||||
|
<div class="date-input-container">
|
||||||
|
<i class="bi bi-calendar3"></i>
|
||||||
|
<input type="text"
|
||||||
class="date-input"
|
class="date-input"
|
||||||
id="startDate"
|
id="startDate"
|
||||||
name="start_date"
|
name="start_date"
|
||||||
value="{{ request('start_date') }}">
|
value="{{ request('start_date') ? date('d-m-Y', strtotime(request('start_date'))) : '' }}"
|
||||||
|
placeholder="dd-mm-yyyy"
|
||||||
|
onfocus="(this.type='date')"
|
||||||
|
onblur="if(!this.value) this.type='text'">
|
||||||
|
</div>
|
||||||
<span class="date-separator">to</span>
|
<span class="date-separator">to</span>
|
||||||
<input type="date"
|
<div class="date-input-container">
|
||||||
|
<i class="bi bi-calendar3"></i>
|
||||||
|
<input type="text"
|
||||||
class="date-input"
|
class="date-input"
|
||||||
id="endDate"
|
id="endDate"
|
||||||
name="end_date"
|
name="end_date"
|
||||||
value="{{ request('end_date') }}">
|
value="{{ request('end_date') ? date('d-m-Y', strtotime(request('end_date'))) : '' }}"
|
||||||
|
placeholder="dd-mm-yyyy"
|
||||||
|
onfocus="(this.type='date')"
|
||||||
|
onblur="if(!this.value) this.type='text'">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!-- Create Invoice Button -->
|
|
||||||
<!-- <a href="{{ route('admin.invoices.create') }}" class="create-invoice-btn">
|
|
||||||
<i class="bi bi-plus-circle"></i> Create Invoice
|
|
||||||
</a> -->
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="invoice-management-main no-extra-space">
|
<div class="invoice-management-main no-extra-space">
|
||||||
@@ -1186,6 +1216,7 @@
|
|||||||
<th class="column-header">#</th>
|
<th class="column-header">#</th>
|
||||||
<th class="column-header">Invoice Number</th>
|
<th class="column-header">Invoice Number</th>
|
||||||
<th class="column-header">Customer</th>
|
<th class="column-header">Customer</th>
|
||||||
|
<th class="column-header">Container</th> {{-- NEW --}}
|
||||||
<th class="column-header">Final Amount</th>
|
<th class="column-header">Final Amount</th>
|
||||||
<th class="column-header">GST %</th>
|
<th class="column-header">GST %</th>
|
||||||
<th class="column-header">Total w/GST</th>
|
<th class="column-header">Total w/GST</th>
|
||||||
@@ -1217,11 +1248,33 @@
|
|||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td class="customer-cell">{{ $invoice->customer_name }}</td>
|
<td class="customer-cell">
|
||||||
|
{{ $invoice->customer_name }}
|
||||||
|
</td>
|
||||||
|
|
||||||
<td class="amount-cell">₹{{ number_format($invoice->final_amount, 2) }}</td>
|
{{-- NEW: Container column --}}
|
||||||
<td class="gst-cell">{{ $invoice->gst_percent }}%</td>
|
<td class="customer-cell">
|
||||||
<td class="amount-cell">₹{{ number_format($invoice->final_amount_with_gst, 2) }}</td>
|
@if($invoice->container)
|
||||||
|
{{ $invoice->container->container_number }}
|
||||||
|
{{-- जर फक्त ID हवी असेल तर:
|
||||||
|
#{{ $invoice->container->id }}
|
||||||
|
--}}
|
||||||
|
@else
|
||||||
|
—
|
||||||
|
@endif
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td class="amount-cell">
|
||||||
|
₹{{ number_format($invoice->final_amount, 2) }}
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td class="gst-cell">
|
||||||
|
{{ $invoice->gst_percent }}%
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td class="amount-cell">
|
||||||
|
₹{{ number_format($invoice->final_amount_with_gst, 2) }}
|
||||||
|
</td>
|
||||||
|
|
||||||
<td>
|
<td>
|
||||||
<span class="badge badge-{{ $invoice->status }}">
|
<span class="badge badge-{{ $invoice->status }}">
|
||||||
@@ -1236,8 +1289,13 @@
|
|||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td class="date-cell">{{ $invoice->invoice_date }}</td>
|
<td class="date-cell">
|
||||||
<td class="date-cell">{{ $invoice->due_date }}</td>
|
{{ $invoice->invoice_date }}
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td class="date-cell">
|
||||||
|
{{ $invoice->due_date }}
|
||||||
|
</td>
|
||||||
|
|
||||||
<td>
|
<td>
|
||||||
<a href="{{ route('admin.invoices.edit', $invoice->id) }}"
|
<a href="{{ route('admin.invoices.edit', $invoice->id) }}"
|
||||||
@@ -1248,10 +1306,12 @@
|
|||||||
</tr>
|
</tr>
|
||||||
@empty
|
@empty
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="10" class="text-muted">No invoices found</td>
|
{{-- 1 new column वाढवलाय म्हणून colspan 11 --}}
|
||||||
|
<td colspan="11" class="text-muted">No invoices found</td>
|
||||||
</tr>
|
</tr>
|
||||||
@endforelse
|
@endforelse
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -1366,7 +1426,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
// Pagination state
|
// Pagination state
|
||||||
@@ -1413,18 +1472,28 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Search and filter functionality for both desktop and mobile
|
// Function to parse dd-mm-yyyy to Date object
|
||||||
const searchInput = document.getElementById('invoiceSearch');
|
function parseDate(dateStr) {
|
||||||
|
if (!dateStr) return null;
|
||||||
|
const parts = dateStr.split('-');
|
||||||
|
if (parts.length === 3) {
|
||||||
|
// dd-mm-yyyy format
|
||||||
|
return new Date(parts[2], parts[1] - 1, parts[0]);
|
||||||
|
}
|
||||||
|
return new Date(dateStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search and filter functionality
|
||||||
|
const searchInput = document.getElementById('searchInput');
|
||||||
const statusFilter = document.getElementById('statusFilter');
|
const statusFilter = document.getElementById('statusFilter');
|
||||||
const startDateInput = document.getElementById('startDate');
|
const startDate = document.getElementById('startDate');
|
||||||
const endDateInput = document.getElementById('endDate');
|
const endDate = document.getElementById('endDate');
|
||||||
const searchButton = document.getElementById('searchButton');
|
|
||||||
|
|
||||||
function filterInvoices() {
|
function filterInvoices() {
|
||||||
const searchTerm = searchInput.value.toLowerCase();
|
const searchTerm = searchInput.value.toLowerCase();
|
||||||
const statusValue = statusFilter.value;
|
const statusValue = statusFilter.value;
|
||||||
const startDate = startDateInput.value;
|
const startDateValue = startDate.value;
|
||||||
const endDate = endDateInput.value;
|
const endDateValue = endDate.value;
|
||||||
|
|
||||||
filteredInvoices = allInvoices.filter(invoice => {
|
filteredInvoices = allInvoices.filter(invoice => {
|
||||||
let include = true;
|
let include = true;
|
||||||
@@ -1438,18 +1507,23 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Status filter
|
// Status filter
|
||||||
if (statusValue && invoice.status !== statusValue) {
|
if (statusValue && statusValue !== 'all' && invoice.status !== statusValue) {
|
||||||
include = false;
|
include = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Date filter
|
// Date range filter
|
||||||
if (startDate || endDate) {
|
if (startDateValue || endDateValue) {
|
||||||
const invoiceDate = new Date(invoice.invoice_date);
|
const invoiceDate = parseDate(invoice.invoice_date);
|
||||||
const start = startDate ? new Date(startDate) : null;
|
|
||||||
const end = endDate ? new Date(endDate) : null;
|
|
||||||
|
|
||||||
if (start && invoiceDate < start) include = false;
|
if (startDateValue) {
|
||||||
if (end && invoiceDate > end) include = false;
|
const start = parseDate(startDateValue);
|
||||||
|
if (invoiceDate < start) include = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (endDateValue) {
|
||||||
|
const end = parseDate(endDateValue);
|
||||||
|
if (invoiceDate > end) include = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return include;
|
return include;
|
||||||
@@ -1462,14 +1536,14 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
|
|
||||||
// Add event listeners for filtering
|
// Add event listeners for filtering
|
||||||
searchInput.addEventListener('input', filterInvoices);
|
searchInput.addEventListener('input', filterInvoices);
|
||||||
searchButton.addEventListener('click', filterInvoices);
|
|
||||||
statusFilter.addEventListener('change', filterInvoices);
|
statusFilter.addEventListener('change', filterInvoices);
|
||||||
startDateInput.addEventListener('change', filterInvoices);
|
startDate.addEventListener('change', filterInvoices);
|
||||||
endDateInput.addEventListener('change', filterInvoices);
|
endDate.addEventListener('change', filterInvoices);
|
||||||
|
|
||||||
// Also trigger search on Enter key in search input
|
// Also trigger search on Enter key in search input
|
||||||
searchInput.addEventListener('keypress', function(e) {
|
searchInput.addEventListener('keypress', function(e) {
|
||||||
if (e.key === 'Enter') {
|
if (e.key === 'Enter') {
|
||||||
|
e.preventDefault();
|
||||||
filterInvoices();
|
filterInvoices();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -1559,7 +1633,8 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
const mobileContainer = document.getElementById('mobileInvoicesContainer');
|
const mobileContainer = document.getElementById('mobileInvoicesContainer');
|
||||||
|
|
||||||
if (filteredInvoices.length === 0) {
|
if (filteredInvoices.length === 0) {
|
||||||
tbody.innerHTML = '<tr><td colspan="10" class="text-muted">No invoices found</td></tr>';
|
// 1 column वाढवल्यामुळे colspan 11
|
||||||
|
tbody.innerHTML = '<tr><td colspan="11" class="text-muted">No invoices found</td></tr>';
|
||||||
mobileContainer.innerHTML = '<div class="text-muted text-center py-4">No invoices found</div>';
|
mobileContainer.innerHTML = '<div class="text-muted text-center py-4">No invoices found</div>';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1590,6 +1665,10 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td class="customer-cell">${invoice.customer_name}</td>
|
<td class="customer-cell">${invoice.customer_name}</td>
|
||||||
|
<!-- NEW: Container column -->
|
||||||
|
<td class="customer-cell">
|
||||||
|
${invoice.container ? (invoice.container.container_number ?? '—') : '—'}
|
||||||
|
</td>
|
||||||
<td class="amount-cell">₹${parseFloat(invoice.final_amount).toLocaleString('en-IN', {minimumFractionDigits: 2, maximumFractionDigits: 2})}</td>
|
<td class="amount-cell">₹${parseFloat(invoice.final_amount).toLocaleString('en-IN', {minimumFractionDigits: 2, maximumFractionDigits: 2})}</td>
|
||||||
<td class="gst-cell">${invoice.gst_percent}%</td>
|
<td class="gst-cell">${invoice.gst_percent}%</td>
|
||||||
<td class="amount-cell">₹${parseFloat(invoice.final_amount_with_gst).toLocaleString('en-IN', {minimumFractionDigits: 2, maximumFractionDigits: 2})}</td>
|
<td class="amount-cell">₹${parseFloat(invoice.final_amount_with_gst).toLocaleString('en-IN', {minimumFractionDigits: 2, maximumFractionDigits: 2})}</td>
|
||||||
@@ -1639,6 +1718,13 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
<span class="mobile-detail-label">Customer</span>
|
<span class="mobile-detail-label">Customer</span>
|
||||||
<span class="mobile-detail-value">${invoice.customer_name}</span>
|
<span class="mobile-detail-value">${invoice.customer_name}</span>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- NEW: Container for mobile -->
|
||||||
|
<div class="mobile-detail-item">
|
||||||
|
<span class="mobile-detail-label">Container</span>
|
||||||
|
<span class="mobile-detail-value">
|
||||||
|
${invoice.container ? (invoice.container.container_number ?? '—') : '—'}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
<div class="mobile-detail-item">
|
<div class="mobile-detail-item">
|
||||||
<span class="mobile-detail-label">Amount</span>
|
<span class="mobile-detail-label">Amount</span>
|
||||||
<span class="mobile-detail-value">₹${parseFloat(invoice.final_amount).toLocaleString('en-IN', {minimumFractionDigits: 2, maximumFractionDigits: 2})}</span>
|
<span class="mobile-detail-value">₹${parseFloat(invoice.final_amount).toLocaleString('en-IN', {minimumFractionDigits: 2, maximumFractionDigits: 2})}</span>
|
||||||
@@ -1683,11 +1769,6 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@endsection
|
@endsection
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
@extends('admin.layouts.app')
|
@extends('admin.layouts.app')
|
||||||
|
|
||||||
|
|
||||||
@section('page-title', 'Edit Invoice')
|
@section('page-title', 'Edit Invoice')
|
||||||
|
|
||||||
|
|
||||||
@section('content')
|
@section('content')
|
||||||
<style>
|
<style>
|
||||||
:root {
|
:root {
|
||||||
@@ -460,11 +462,12 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
@php
|
@php
|
||||||
$totalPaid = $invoice->installments()->sum('amount');
|
// आता helpers वापरू: totalPaid() आणि remainingAmount()
|
||||||
$remaining = $invoice->final_amount_with_gst - $totalPaid;
|
$totalPaid = $invoice->totalPaid();
|
||||||
|
$remaining = $invoice->remainingAmount();
|
||||||
@endphp
|
@endphp
|
||||||
|
|
||||||
{{-- Amount Breakdown --}}
|
{{-- Amount Breakdown (items + GST + groups) --}}
|
||||||
<div class="amount-breakdown-compact">
|
<div class="amount-breakdown-compact">
|
||||||
<h6 class="fw-bold mb-3 text-dark">
|
<h6 class="fw-bold mb-3 text-dark">
|
||||||
<i class="fas fa-calculator me-2"></i>Amount Breakdown
|
<i class="fas fa-calculator me-2"></i>Amount Breakdown
|
||||||
@@ -506,10 +509,17 @@
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="breakdown-row">
|
||||||
|
<span class="breakdown-label">Charge Groups Total</span>
|
||||||
|
<span class="breakdown-value text-info" id="chargeGroupsTotal">
|
||||||
|
₹{{ number_format($invoice->charge_groups_total, 2) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="breakdown-row" style="border-top: 2px solid #e2e8f0; padding-top: 0.75rem;">
|
<div class="breakdown-row" style="border-top: 2px solid #e2e8f0; padding-top: 0.75rem;">
|
||||||
<span class="breakdown-label fw-bold">Total Invoice Amount Including GST</span>
|
<span class="breakdown-label fw-bold">Grand Total (Items + GST + Groups)</span>
|
||||||
<span class="breakdown-value fw-bold text-dark" id="totalInvoiceWithGst">
|
<span class="breakdown-value fw-bold text-dark" id="totalInvoiceWithGst">
|
||||||
₹{{ number_format($invoice->final_amount_with_gst, 2) }}
|
₹{{ number_format($invoice->grand_total_with_charges, 2) }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -523,18 +533,18 @@
|
|||||||
<div class="breakdown-row" style="border-bottom: none;">
|
<div class="breakdown-row" style="border-bottom: none;">
|
||||||
<span class="breakdown-label text-danger">Remaining</span>
|
<span class="breakdown-label text-danger">Remaining</span>
|
||||||
<span class="breakdown-value fw-bold text-danger" id="remainingAmount">
|
<span class="breakdown-value fw-bold text-danger" id="remainingAmount">
|
||||||
₹{{ number_format($remaining, 2) }}
|
₹{{ number_format(max(0, $remaining), 2) }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{-- Installment Summary --}}
|
{{-- Installment Summary (top cards) --}}
|
||||||
<div class="summary-grid-compact">
|
<div class="summary-grid-compact">
|
||||||
<div class="summary-card-compact total">
|
<div class="summary-card-compact total">
|
||||||
<div class="summary-value-compact text-success" id="totalInvoiceWithGstCard">
|
<div class="summary-value-compact text-success" id="totalInvoiceWithGstCard">
|
||||||
₹{{ number_format($invoice->final_amount_with_gst, 2) }}
|
₹{{ number_format($invoice->grand_total_with_charges, 2) }}
|
||||||
</div>
|
</div>
|
||||||
<div class="summary-label-compact">Total Amount</div>
|
<div class="summary-label-compact">Grand Total (Items + GST + Groups)</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="summary-card-compact paid">
|
<div class="summary-card-compact paid">
|
||||||
<div class="summary-value-compact text-primary">
|
<div class="summary-value-compact text-primary">
|
||||||
@@ -544,7 +554,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="summary-card-compact remaining">
|
<div class="summary-card-compact remaining">
|
||||||
<div class="summary-value-compact text-warning">
|
<div class="summary-value-compact text-warning">
|
||||||
₹{{ number_format($remaining, 2) }}
|
₹{{ number_format(max(0, $remaining), 2) }}
|
||||||
</div>
|
</div>
|
||||||
<div class="summary-label-compact">Remaining</div>
|
<div class="summary-label-compact">Remaining</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -621,7 +631,7 @@
|
|||||||
class="form-control-compact"
|
class="form-control-compact"
|
||||||
step="0.01"
|
step="0.01"
|
||||||
min="1"
|
min="1"
|
||||||
max="{{ $remaining }}"
|
max="{{ max(0, $remaining) }}"
|
||||||
required
|
required
|
||||||
placeholder="Enter installment amount">
|
placeholder="Enter installment amount">
|
||||||
</div>
|
</div>
|
||||||
@@ -784,6 +794,8 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
if (document.getElementById("gstAmount")) {
|
if (document.getElementById("gstAmount")) {
|
||||||
document.getElementById("gstAmount").textContent = "₹" + formatINR(data.gstAmount);
|
document.getElementById("gstAmount").textContent = "₹" + formatINR(data.gstAmount);
|
||||||
}
|
}
|
||||||
|
// grand total आता finalAmountWithGst नाही, पण API अजून तेच key देत आहे,
|
||||||
|
// त्यामुळे इथे फक्त card आणि breakdown value update करतो:
|
||||||
if (document.getElementById("totalInvoiceWithGst")) {
|
if (document.getElementById("totalInvoiceWithGst")) {
|
||||||
document.getElementById("totalInvoiceWithGst").textContent = "₹" + formatINR(data.finalAmountWithGst);
|
document.getElementById("totalInvoiceWithGst").textContent = "₹" + formatINR(data.finalAmountWithGst);
|
||||||
}
|
}
|
||||||
@@ -876,7 +888,6 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
const invoiceDateInput = document.querySelector('input[name="invoice_date"]');
|
const invoiceDateInput = document.querySelector('input[name="invoice_date"]');
|
||||||
const dueDateInput = document.querySelector('input[name="due_date"]');
|
const dueDateInput = document.querySelector('input[name="due_date"]');
|
||||||
@@ -886,14 +897,10 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
const selectedDate = new Date(this.value);
|
const selectedDate = new Date(this.value);
|
||||||
|
|
||||||
if (!isNaN(selectedDate.getTime())) {
|
if (!isNaN(selectedDate.getTime())) {
|
||||||
// १० दिवस पुढे नेण्यासाठी logic
|
|
||||||
selectedDate.setDate(selectedDate.getDate() + 10);
|
selectedDate.setDate(selectedDate.getDate() + 10);
|
||||||
|
|
||||||
// तारीख YYYY-MM-DD format मध्ये करण्यासाठी
|
|
||||||
const year = selectedDate.getFullYear();
|
const year = selectedDate.getFullYear();
|
||||||
const month = String(selectedDate.getMonth() + 1).padStart(2, '0');
|
const month = String(selectedDate.getMonth() + 1).padStart(2, '0');
|
||||||
const day = String(selectedDate.getDate()).padStart(2, '0');
|
const day = String(selectedDate.getDate()).padStart(2, '0');
|
||||||
|
|
||||||
dueDateInput.value = `${year}-${month}-${day}`;
|
dueDateInput.value = `${year}-${month}-${day}`;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -65,40 +65,77 @@
|
|||||||
|
|
||||||
.stats-container {
|
.stats-container {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
|
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||||
gap: 16px;
|
gap: 16px;
|
||||||
margin-bottom: 24px;
|
margin-bottom: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.stat-card {
|
.stat-card {
|
||||||
background: white;
|
|
||||||
border-radius: 12px;
|
|
||||||
padding: 20px;
|
|
||||||
box-shadow: var(--shadow-sm);
|
|
||||||
border-left: 4px solid;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
align-items: center;
|
||||||
|
gap: 16px;
|
||||||
|
padding: 20px 22px;
|
||||||
|
border-radius: 14px;
|
||||||
|
border-left: 4px solid;
|
||||||
|
box-shadow: 0 2px 8px rgba(0,0,0,0.06);
|
||||||
transition: transform 0.3s ease, box-shadow 0.3s ease;
|
transition: transform 0.3s ease, box-shadow 0.3s ease;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.stat-card:hover {
|
.stat-card:hover {
|
||||||
transform: translateY(-5px);
|
transform: translateY(-4px);
|
||||||
box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.1);
|
box-shadow: 0 8px 20px rgba(0,0,0,0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.stat-card.total { border-left-color: #667eea; }
|
.stat-card.total {
|
||||||
.stat-card.paid { border-left-color: #10b981; }
|
background: linear-gradient(135deg, #eff6ff 0%, #dbeafe 100%);
|
||||||
.stat-card.pending { border-left-color: #f59e0b; }
|
border-left-color: #3b82f6;
|
||||||
.stat-card.overdue { border-left-color: #ef4444; }
|
}
|
||||||
|
.stat-card.paid {
|
||||||
|
background: linear-gradient(135deg, #f0fdf4 0%, #dcfce7 100%);
|
||||||
|
border-left-color: #22c55e;
|
||||||
|
}
|
||||||
|
.stat-card.pending {
|
||||||
|
background: linear-gradient(135deg, #fffbeb 0%, #fef9c3 100%);
|
||||||
|
border-left-color: #f59e0b;
|
||||||
|
}
|
||||||
|
.stat-card.overdue {
|
||||||
|
background: linear-gradient(135deg, #fff5f5 0%, #fee2e2 100%);
|
||||||
|
border-left-color: #ef4444;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-icon-wrap {
|
||||||
|
width: 48px;
|
||||||
|
height: 48px;
|
||||||
|
border-radius: 12px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-shrink: 0;
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-card.total .stat-icon-wrap { background: rgba(59,130,246,0.12); color: #3b82f6; }
|
||||||
|
.stat-card.paid .stat-icon-wrap { background: rgba(34,197,94,0.15); color: #16a34a; }
|
||||||
|
.stat-card.pending .stat-icon-wrap{ background: rgba(245,158,11,0.15); color: #d97706; }
|
||||||
|
.stat-card.overdue .stat-icon-wrap{ background: rgba(239,68,68,0.12); color: #dc2626; }
|
||||||
|
|
||||||
|
.stat-text {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
.stat-value {
|
.stat-value {
|
||||||
font-size: 28px;
|
font-size: 26px;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
margin-bottom: 8px;
|
line-height: 1;
|
||||||
|
color: var(--text-dark);
|
||||||
}
|
}
|
||||||
|
|
||||||
.stat-label {
|
.stat-label {
|
||||||
font-size: 14px;
|
font-size: 13px;
|
||||||
color: var(--text-muted);
|
color: var(--text-muted);
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
@@ -483,9 +520,9 @@
|
|||||||
@media(max-width: 768px) {
|
@media(max-width: 768px) {
|
||||||
.orders-title { font-size: 24px; }
|
.orders-title { font-size: 24px; }
|
||||||
.orders-container { padding: 16px; }
|
.orders-container { padding: 16px; }
|
||||||
.stats-container { grid-template-columns: 1fr; }
|
.stats-container { grid-template-columns: 1fr 1fr; }
|
||||||
.stat-card { padding: 16px; }
|
.stat-card { padding: 14px 16px; }
|
||||||
.stat-value { font-size: 24px; }
|
.stat-value { font-size: 22px; }
|
||||||
|
|
||||||
.download-section { justify-content: stretch; }
|
.download-section { justify-content: stretch; }
|
||||||
.download-btn { flex: 1; justify-content: center; }
|
.download-btn { flex: 1; justify-content: center; }
|
||||||
@@ -497,6 +534,10 @@
|
|||||||
|
|
||||||
.pagination-container { flex-direction: column; gap: 16px; }
|
.pagination-container { flex-direction: column; gap: 16px; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media(max-width: 480px) {
|
||||||
|
.stats-container { grid-template-columns: 1fr; }
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<div class="orders-container">
|
<div class="orders-container">
|
||||||
@@ -504,7 +545,6 @@
|
|||||||
<i class="fas fa-file-invoice-dollar"></i> Invoices Management
|
<i class="fas fa-file-invoice-dollar"></i> Invoices Management
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{-- Stats Cards based on invoices --}}
|
|
||||||
<div class="stats-container">
|
<div class="stats-container">
|
||||||
@php
|
@php
|
||||||
$totalInvoices = $invoices->count();
|
$totalInvoices = $invoices->count();
|
||||||
@@ -514,22 +554,45 @@
|
|||||||
@endphp
|
@endphp
|
||||||
|
|
||||||
<div class="stat-card total">
|
<div class="stat-card total">
|
||||||
|
<div class="stat-icon-wrap">
|
||||||
|
<i class="fas fa-file-invoice"></i>
|
||||||
|
</div>
|
||||||
|
<div class="stat-text">
|
||||||
<div class="stat-value">{{ $totalInvoices }}</div>
|
<div class="stat-value">{{ $totalInvoices }}</div>
|
||||||
<div class="stat-label">Total Invoices</div>
|
<div class="stat-label">Total Invoices</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="stat-card paid">
|
<div class="stat-card paid">
|
||||||
|
<div class="stat-icon-wrap">
|
||||||
|
<i class="fas fa-check-circle"></i>
|
||||||
|
</div>
|
||||||
|
<div class="stat-text">
|
||||||
<div class="stat-value">{{ $paidInvoices }}</div>
|
<div class="stat-value">{{ $paidInvoices }}</div>
|
||||||
<div class="stat-label">Paid Invoices</div>
|
<div class="stat-label">Paid Invoices</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="stat-card pending">
|
<div class="stat-card pending">
|
||||||
|
<div class="stat-icon-wrap">
|
||||||
|
<i class="fas fa-clock"></i>
|
||||||
|
</div>
|
||||||
|
<div class="stat-text">
|
||||||
<div class="stat-value">{{ $pendingInvoices }}</div>
|
<div class="stat-value">{{ $pendingInvoices }}</div>
|
||||||
<div class="stat-label">Pending Invoices</div>
|
<div class="stat-label">Pending Invoices</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="stat-card overdue">
|
<div class="stat-card overdue">
|
||||||
|
<div class="stat-icon-wrap">
|
||||||
|
<i class="fas fa-exclamation-triangle"></i>
|
||||||
|
</div>
|
||||||
|
<div class="stat-text">
|
||||||
<div class="stat-value">{{ $overdueInvoices }}</div>
|
<div class="stat-value">{{ $overdueInvoices }}</div>
|
||||||
<div class="stat-label">Overdue Invoices</div>
|
<div class="stat-label">Overdue Invoices</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="download-section">
|
<div class="download-section">
|
||||||
<button class="download-btn pdf" id="downloadPdf" {{ $invoices->count() == 0 ? 'disabled' : '' }}>
|
<button class="download-btn pdf" id="downloadPdf" {{ $invoices->count() == 0 ? 'disabled' : '' }}>
|
||||||
@@ -570,7 +633,7 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<th>Invoice No</th>
|
<th>Invoice No</th>
|
||||||
<th>Invoice Date</th>
|
<th>Invoice Date</th>
|
||||||
<th>Mark No</th>
|
{{-- <th>Mark No</th> --}}
|
||||||
<th>Container No</th>
|
<th>Container No</th>
|
||||||
<th>Container Date</th>
|
<th>Container Date</th>
|
||||||
<th>Company</th>
|
<th>Company</th>
|
||||||
@@ -586,10 +649,30 @@
|
|||||||
$status = strtolower($inv->invoice_status ?? 'pending');
|
$status = strtolower($inv->invoice_status ?? 'pending');
|
||||||
@endphp
|
@endphp
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ $inv->invoice_number }}</td>
|
<td>
|
||||||
|
@if($inv->invoice_number)
|
||||||
|
<a href="javascript:void(0);"
|
||||||
|
class="invoice-popup-link"
|
||||||
|
data-invoice-id="{{ $inv->id }}">
|
||||||
|
{{ $inv->invoice_number }}
|
||||||
|
</a>
|
||||||
|
@else
|
||||||
|
-
|
||||||
|
@endif
|
||||||
|
</td>
|
||||||
<td>{{ $inv->invoice_date ? \Carbon\Carbon::parse($inv->invoice_date)->format('d-m-Y') : '-' }}</td>
|
<td>{{ $inv->invoice_date ? \Carbon\Carbon::parse($inv->invoice_date)->format('d-m-Y') : '-' }}</td>
|
||||||
<td>{{ $inv->mark_no ?? '-' }}</td>
|
{{-- <td>{{ $inv->mark_no ?? '-' }}</td> --}}
|
||||||
<td>{{ $inv->container_number ?? '-' }}</td>
|
<td>
|
||||||
|
@if(!empty($inv->container_id) && !empty($inv->container_number))
|
||||||
|
<a href="javascript:void(0);"
|
||||||
|
class="container-popup-link"
|
||||||
|
data-container-id="{{ $inv->container_id }}">
|
||||||
|
{{ $inv->container_number }}
|
||||||
|
</a>
|
||||||
|
@else
|
||||||
|
-
|
||||||
|
@endif
|
||||||
|
</td>
|
||||||
<td>{{ $inv->container_date ? \Carbon\Carbon::parse($inv->container_date)->format('d-m-Y') : '-' }}</td>
|
<td>{{ $inv->container_date ? \Carbon\Carbon::parse($inv->container_date)->format('d-m-Y') : '-' }}</td>
|
||||||
<td>{{ $inv->company_name ?? '-' }}</td>
|
<td>{{ $inv->company_name ?? '-' }}</td>
|
||||||
<td>{{ $inv->customer_name ?? '-' }}</td>
|
<td>{{ $inv->customer_name ?? '-' }}</td>
|
||||||
@@ -633,6 +716,20 @@
|
|||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="modal fade" id="invoiceModal" tabindex="-1" aria-hidden="true">
|
||||||
|
<div class="modal-dialog modal-xl modal-dialog-scrollable">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="invoiceModalTitle">Invoice Details</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body" id="invoiceModalBody">
|
||||||
|
<p class="text-center text-muted">Loading...</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
let currentPage = 1;
|
let currentPage = 1;
|
||||||
const itemsPerPage = 10;
|
const itemsPerPage = 10;
|
||||||
@@ -680,7 +777,7 @@
|
|||||||
const term = searchTerm.toLowerCase();
|
const term = searchTerm.toLowerCase();
|
||||||
const fields = [
|
const fields = [
|
||||||
inv.invoice_number?.toString().toLowerCase(),
|
inv.invoice_number?.toString().toLowerCase(),
|
||||||
inv.mark_no?.toString().toLowerCase(),
|
// inv.mark_no?.toString().toLowerCase(),
|
||||||
inv.container_number?.toString().toLowerCase(),
|
inv.container_number?.toString().toLowerCase(),
|
||||||
inv.company_name?.toString().toLowerCase(),
|
inv.company_name?.toString().toLowerCase(),
|
||||||
inv.customer_name?.toString().toLowerCase()
|
inv.customer_name?.toString().toLowerCase()
|
||||||
@@ -711,6 +808,21 @@
|
|||||||
|
|
||||||
document.getElementById('downloadPdf').addEventListener('click', downloadPdf);
|
document.getElementById('downloadPdf').addEventListener('click', downloadPdf);
|
||||||
document.getElementById('downloadExcel').addEventListener('click', downloadExcel);
|
document.getElementById('downloadExcel').addEventListener('click', downloadExcel);
|
||||||
|
|
||||||
|
document.addEventListener('click', function(e) {
|
||||||
|
const invLink = e.target.closest('.invoice-popup-link');
|
||||||
|
if (invLink) {
|
||||||
|
e.preventDefault();
|
||||||
|
openInvoicePopup(invLink.dataset.invoiceId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const contLink = e.target.closest('.container-popup-link');
|
||||||
|
if (contLink) {
|
||||||
|
e.preventDefault();
|
||||||
|
openContainerPopup(contLink.dataset.containerId);
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
function handleFilter() {
|
function handleFilter() {
|
||||||
@@ -797,7 +909,7 @@
|
|||||||
tbody.innerHTML = '';
|
tbody.innerHTML = '';
|
||||||
|
|
||||||
if (filteredInvoices.length === 0) {
|
if (filteredInvoices.length === 0) {
|
||||||
tbody.innerHTML = '<tr><td colspan="10" class="text-center py-4 text-muted">No invoices found matching your criteria.</td></tr>';
|
tbody.innerHTML = '<tr><td colspan="9" class="text-center py-4 text-muted">No invoices found matching your criteria.</td></tr>';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -809,10 +921,26 @@
|
|||||||
const status = (inv.invoice_status || 'pending').toLowerCase();
|
const status = (inv.invoice_status || 'pending').toLowerCase();
|
||||||
const row = document.createElement('tr');
|
const row = document.createElement('tr');
|
||||||
row.innerHTML = `
|
row.innerHTML = `
|
||||||
<td>${inv.invoice_number || '-'}</td>
|
<td>
|
||||||
|
${
|
||||||
|
inv.invoice_number
|
||||||
|
? `<a href="javascript:void(0);" class="invoice-popup-link" data-invoice-id="${inv.id}">
|
||||||
|
${inv.invoice_number}
|
||||||
|
</a>`
|
||||||
|
: '-'
|
||||||
|
}
|
||||||
|
</td>
|
||||||
<td>${inv.invoice_date ? new Date(inv.invoice_date).toLocaleDateString('en-GB') : '-'}</td>
|
<td>${inv.invoice_date ? new Date(inv.invoice_date).toLocaleDateString('en-GB') : '-'}</td>
|
||||||
<td>${inv.mark_no || '-'}</td>
|
<!-- <td>${inv.mark_no || '-'}</td> -->
|
||||||
<td>${inv.container_number || '-'}</td>
|
<td>
|
||||||
|
${
|
||||||
|
inv.container_id && inv.container_number
|
||||||
|
? `<a href="javascript:void(0);" class="container-popup-link" data-container-id="${inv.container_id}">
|
||||||
|
${inv.container_number}
|
||||||
|
</a>`
|
||||||
|
: '-'
|
||||||
|
}
|
||||||
|
</td>
|
||||||
<td>${inv.container_date ? new Date(inv.container_date).toLocaleDateString('en-GB') : '-'}</td>
|
<td>${inv.container_date ? new Date(inv.container_date).toLocaleDateString('en-GB') : '-'}</td>
|
||||||
<td>${inv.company_name || '-'}</td>
|
<td>${inv.company_name || '-'}</td>
|
||||||
<td>${inv.customer_name || '-'}</td>
|
<td>${inv.customer_name || '-'}</td>
|
||||||
@@ -828,6 +956,66 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function openInvoicePopup(invoiceId) {
|
||||||
|
const modal = new bootstrap.Modal(document.getElementById('invoiceModal'));
|
||||||
|
|
||||||
|
document.getElementById('invoiceModalTitle').textContent = 'Invoice Details';
|
||||||
|
document.getElementById('invoiceModalBody').innerHTML =
|
||||||
|
"<div class='text-center py-4'>" +
|
||||||
|
"<div class='spinner-border text-primary' role='status'></div>" +
|
||||||
|
"<p class='mt-2 text-muted'>Loading invoice details...</p>" +
|
||||||
|
"</div>";
|
||||||
|
|
||||||
|
modal.show();
|
||||||
|
|
||||||
|
fetch(`/admin/invoices/${invoiceId}/popup`)
|
||||||
|
.then(res => {
|
||||||
|
if (!res.ok) throw new Error('Network error');
|
||||||
|
return res.text();
|
||||||
|
})
|
||||||
|
.then(html => {
|
||||||
|
document.getElementById('invoiceModalBody').innerHTML = html;
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error(error);
|
||||||
|
document.getElementById('invoiceModalBody').innerHTML =
|
||||||
|
"<div class='text-center py-4 text-danger'>" +
|
||||||
|
"<i class='bi bi-exclamation-triangle fs-1'></i>" +
|
||||||
|
"<p class='mt-2'>Failed to load invoice details. Please try again.</p>" +
|
||||||
|
"</div>";
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function openContainerPopup(containerId) {
|
||||||
|
const modal = new bootstrap.Modal(document.getElementById('invoiceModal'));
|
||||||
|
|
||||||
|
document.getElementById('invoiceModalTitle').textContent = 'Container Details';
|
||||||
|
document.getElementById('invoiceModalBody').innerHTML =
|
||||||
|
"<div class='text-center py-4'>" +
|
||||||
|
"<div class='spinner-border text-primary' role='status'></div>" +
|
||||||
|
"<p class='mt-2 text-muted'>Loading container details...</p>" +
|
||||||
|
"</div>";
|
||||||
|
|
||||||
|
modal.show();
|
||||||
|
|
||||||
|
fetch(`/admin/containers/${containerId}/popup`)
|
||||||
|
.then(res => {
|
||||||
|
if (!res.ok) throw new Error('Network error');
|
||||||
|
return res.text();
|
||||||
|
})
|
||||||
|
.then(html => {
|
||||||
|
document.getElementById('invoiceModalBody').innerHTML = html;
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error(error);
|
||||||
|
document.getElementById('invoiceModalBody').innerHTML =
|
||||||
|
"<div class='text-center py-4 text-danger'>" +
|
||||||
|
"<i class='bi bi-exclamation-triangle fs-1'></i>" +
|
||||||
|
"<p class='mt-2'>Failed to load container details.</p>" +
|
||||||
|
"</div>";
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function downloadPdf() {
|
function downloadPdf() {
|
||||||
if (filteredInvoices.length === 0) {
|
if (filteredInvoices.length === 0) {
|
||||||
showNotification('No data available to download', 'warning');
|
showNotification('No data available to download', 'warning');
|
||||||
|
|||||||
@@ -477,7 +477,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* ── SUMMARY ── */
|
/* ── SUMMARY ── */
|
||||||
.summary-wrap {
|
/* .summary-wrap {
|
||||||
padding: 0 2.5rem 2rem;
|
padding: 0 2.5rem 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -516,7 +516,79 @@
|
|||||||
.summary-row .value { font-weight: 700; color: var(--primary); }
|
.summary-row .value { font-weight: 700; color: var(--primary); }
|
||||||
.summary-row.total .label { font-size: 1rem; font-weight: 700; color: var(--primary); }
|
.summary-row.total .label { font-size: 1rem; font-weight: 700; color: var(--primary); }
|
||||||
.summary-row.total .value { font-size: 1.15rem; color: #10b981; }
|
.summary-row.total .value { font-size: 1.15rem; color: #10b981; }
|
||||||
.summary-row .value.red { color: #ef4444; }
|
.summary-row .value.red { color: #ef4444; } */
|
||||||
|
.summary-card-compact {
|
||||||
|
margin: 1.5rem 2.5rem 1.5rem;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: var(--radius);
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: var(--shadow-lg);
|
||||||
|
background: var(--surface);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Header full width, curved top corners */
|
||||||
|
.summary-header-compact {
|
||||||
|
background: var(--primary);
|
||||||
|
color: #fff;
|
||||||
|
padding: 0.8rem 1.25rem;
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
width: 100%;
|
||||||
|
border-top-left-radius: var(--radius);
|
||||||
|
border-top-right-radius: var(--radius);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Icon थोडा highlight */
|
||||||
|
.summary-header-compact i {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-body-compact {
|
||||||
|
padding: 1rem 1.25rem 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-row-compact {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0.45rem 0;
|
||||||
|
border-bottom: 1px solid var(--border);
|
||||||
|
font-size: 0.88rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-row-compact:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-row-compact .label {
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-row-compact .value {
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-row-compact.total .label {
|
||||||
|
font-size: 0.95rem;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-row-compact.total .value {
|
||||||
|
font-size: 1.05rem;
|
||||||
|
color: #10b981;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-row-compact.muted .value {
|
||||||
|
color: var(--text-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ── FOOTER ── */
|
/* ── FOOTER ── */
|
||||||
.invoice-footer {
|
.invoice-footer {
|
||||||
@@ -653,8 +725,8 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- ═══════════════════════════ ID BOXES ═══════════════════════════ -->
|
<!-- ═══════════════════════════ ID BOXES ═══════════════════════════ -->
|
||||||
<div class="id-grid">
|
<!-- <div class="id-grid">
|
||||||
<!-- Invoice ID -->
|
|
||||||
<div class="id-box">
|
<div class="id-box">
|
||||||
<div class="id-icon-wrap id-icon-blue">
|
<div class="id-icon-wrap id-icon-blue">
|
||||||
<i class="fas fa-receipt"></i>
|
<i class="fas fa-receipt"></i>
|
||||||
@@ -663,10 +735,10 @@
|
|||||||
<div class="id-label">Invoice ID</div>
|
<div class="id-label">Invoice ID</div>
|
||||||
<div class="id-value">{{ $invoice->invoice_number }}</div>
|
<div class="id-value">{{ $invoice->invoice_number }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div> -->
|
||||||
|
|
||||||
<!-- Container ID -->
|
<!-- Container ID -->
|
||||||
<div class="id-box">
|
<!-- <div class="id-box">
|
||||||
<div class="id-icon-wrap id-icon-green">
|
<div class="id-icon-wrap id-icon-green">
|
||||||
<i class="fas fa-box"></i>
|
<i class="fas fa-box"></i>
|
||||||
</div>
|
</div>
|
||||||
@@ -684,25 +756,56 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div> -->
|
||||||
|
|
||||||
<!-- ═══════════════════════════ DATES ═══════════════════════════ -->
|
<!-- ═══════════════════════════ DATES ═══════════════════════════ -->
|
||||||
<div class="date-strip">
|
<!-- ═══════════════════════════ ID + DATES (ONE ROW) ═══════════════════════════ -->
|
||||||
|
<!-- <div class="date-strip">
|
||||||
<div class="date-row">
|
<div class="date-row">
|
||||||
|
|
||||||
|
{{-- Container ID --}}
|
||||||
|
<div class="date-card" style="flex: 1.2;">
|
||||||
|
<div class="date-icon-wrap" style="background:#ecfeff;color:#0e7490;">
|
||||||
|
<i class="fas fa-box"></i>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="date-label">Container ID</div>
|
||||||
|
<div class="date-value">
|
||||||
|
@if($invoice->container && $invoice->container->container_number)
|
||||||
|
{{ $invoice->container->container_number }}
|
||||||
|
@elseif($invoice->container_id)
|
||||||
|
{{ $invoice->container_id }}
|
||||||
|
@else
|
||||||
|
N/A
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{-- छोटा arrow --}}
|
||||||
|
<div class="date-arrow">
|
||||||
|
<i class="fas fa-arrow-right"></i>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{-- Invoice Date --}}
|
||||||
<div class="date-card">
|
<div class="date-card">
|
||||||
<div class="date-icon-wrap">
|
<div class="date-icon-wrap">
|
||||||
<i class="fas fa-calendar-alt"></i>
|
<i class="fas fa-calendar-alt"></i>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div class="date-label">Invoice Date</div>
|
<div class="date-label">Invoice Date</div>
|
||||||
<div class="date-value">{{ \Carbon\Carbon::parse($invoice->invoice_date)->format('M d, Y') }}</div>
|
<div class="date-value">
|
||||||
|
{{ \Carbon\Carbon::parse($invoice->invoice_date)->format('M d, Y') }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{{-- दुसरा arrow --}}
|
||||||
<div class="date-arrow">
|
<div class="date-arrow">
|
||||||
<i class="fas fa-arrow-right"></i>
|
<i class="fas fa-arrow-right"></i>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{{-- Due Date --}}
|
||||||
<div class="date-card">
|
<div class="date-card">
|
||||||
<div class="date-icon-wrap" style="background:#fff7ed;color:#f59e0b;">
|
<div class="date-icon-wrap" style="background:#fff7ed;color:#f59e0b;">
|
||||||
<i class="fas fa-clock"></i>
|
<i class="fas fa-clock"></i>
|
||||||
@@ -714,8 +817,74 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
-->
|
||||||
|
|
||||||
|
|
||||||
|
<!-- ═══════════════════════════ CONTAINER + INVOICE DATE + DUE DATE (ONE ROW) ═══════════════════════════ -->
|
||||||
|
<div class="date-strip">
|
||||||
|
<div class="date-row">
|
||||||
|
|
||||||
|
{{-- Container ID --}}
|
||||||
|
<div class="date-card" style="flex: 1.2;">
|
||||||
|
<div class="date-icon-wrap" style="background:#ecfeff;color:#0e7490;">
|
||||||
|
<i class="fas fa-box"></i>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="date-label">Container ID</div>
|
||||||
|
<div class="date-value">
|
||||||
|
@if($invoice->container && $invoice->container->container_number)
|
||||||
|
{{ $invoice->container->container_number }}
|
||||||
|
@elseif($invoice->container_id)
|
||||||
|
{{ $invoice->container_id }}
|
||||||
|
@else
|
||||||
|
N/A
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{-- छोटा arrow --}}
|
||||||
|
<div class="date-arrow">
|
||||||
|
<i class="fas fa-arrow-right"></i>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{-- Invoice Date --}}
|
||||||
|
<div class="date-card">
|
||||||
|
<div class="date-icon-wrap">
|
||||||
|
<i class="fas fa-calendar-alt"></i>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="date-label">Invoice Date</div>
|
||||||
|
<div class="date-value">
|
||||||
|
{{ \Carbon\Carbon::parse($invoice->invoice_date)->format('M d, Y') }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{-- दुसरा arrow --}}
|
||||||
|
<div class="date-arrow">
|
||||||
|
<i class="fas fa-arrow-right"></i>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{-- Due Date --}}
|
||||||
|
<div class="date-card">
|
||||||
|
<div class="date-icon-wrap" style="background:#fff7ed;color:#f59e0b;">
|
||||||
|
<i class="fas fa-clock"></i>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="date-label">Due Date</div>
|
||||||
|
<div class="date-value {{ $invoice->status == 'overdue' ? 'overdue' : '' }}">
|
||||||
|
{{ \Carbon\Carbon::parse($invoice->due_date)->format('M d, Y') }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<!-- ═══════════════════════════ CUSTOMER ═══════════════════════════ -->
|
<!-- ═══════════════════════════ CUSTOMER ═══════════════════════════ -->
|
||||||
<div class="panel">
|
<div class="panel">
|
||||||
@@ -788,6 +957,7 @@
|
|||||||
<td class="text-center">
|
<td class="text-center">
|
||||||
<input type="checkbox"
|
<input type="checkbox"
|
||||||
class="item-select-checkbox"
|
class="item-select-checkbox"
|
||||||
|
name="item_ids[]"
|
||||||
value="{{ $item->id }}"
|
value="{{ $item->id }}"
|
||||||
{{ $alreadyGrouped ? 'disabled' : '' }}>
|
{{ $alreadyGrouped ? 'disabled' : '' }}>
|
||||||
</td>
|
</td>
|
||||||
@@ -1065,7 +1235,7 @@
|
|||||||
@endif
|
@endif
|
||||||
|
|
||||||
<!-- ═══════════════════════════ SUMMARY ═══════════════════════════ -->
|
<!-- ═══════════════════════════ SUMMARY ═══════════════════════════ -->
|
||||||
<div class="summary-wrap">
|
<!-- <div class="summary-wrap">
|
||||||
<div class="row justify-content-end">
|
<div class="row justify-content-end">
|
||||||
<div class="col-md-5">
|
<div class="col-md-5">
|
||||||
<div class="summary-card">
|
<div class="summary-card">
|
||||||
@@ -1107,8 +1277,70 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div> -->
|
||||||
|
{{-- ===== FINAL SUMMARY (POPUP) ===== --}}
|
||||||
|
<div class="summary-card-compact mt-3">
|
||||||
|
<div class="summary-header-compact">
|
||||||
|
<i class="fas fa-calculator"></i>
|
||||||
|
<span>Final Summary</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="summary-body-compact">
|
||||||
|
@if($invoice->tax_type === 'gst')
|
||||||
|
<div class="summary-row-compact">
|
||||||
|
<span class="label">
|
||||||
|
CGST {{ $invoice->cgst_percent ?? $invoice->gst_percent / 2 }}%
|
||||||
|
</span>
|
||||||
|
<span class="value red">
|
||||||
|
₹{{ number_format($invoice->gst_amount / 2, 2) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="summary-row-compact">
|
||||||
|
<span class="label">
|
||||||
|
SGST {{ $invoice->sgst_percent ?? $invoice->gst_percent / 2 }}%
|
||||||
|
</span>
|
||||||
|
<span class="value red">
|
||||||
|
₹{{ number_format($invoice->gst_amount / 2, 2) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
@elseif($invoice->tax_type === 'igst')
|
||||||
|
<div class="summary-row-compact">
|
||||||
|
<span class="label">
|
||||||
|
IGST {{ $invoice->igst_percent ?? $invoice->gst_percent }}%
|
||||||
|
</span>
|
||||||
|
<span class="value red">
|
||||||
|
₹{{ number_format($invoice->gst_amount, 2) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
@else
|
||||||
|
<div class="summary-row-compact">
|
||||||
|
<span class="label">
|
||||||
|
GST {{ $invoice->gst_percent }}%
|
||||||
|
</span>
|
||||||
|
<span class="value red">
|
||||||
|
₹{{ number_format($invoice->gst_amount, 2) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
<div class="summary-row-compact">
|
||||||
|
<span class="label">Charge Groups Total</span>
|
||||||
|
<span class="value">
|
||||||
|
₹{{ number_format($invoice->charge_groups_total, 2) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- <div class="summary-row-compact total">
|
||||||
|
<span class="label">Grand Total (Items + GST + Groups)</span>
|
||||||
|
<span class="value">
|
||||||
|
₹{{ number_format($invoice->grand_total_with_charges, 2) }}
|
||||||
|
</span>
|
||||||
|
</div> -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<!-- ═══════════════════════════ FOOTER ═══════════════════════════ -->
|
<!-- ═══════════════════════════ FOOTER ═══════════════════════════ -->
|
||||||
<div class="invoice-footer">
|
<div class="invoice-footer">
|
||||||
@if($invoice->pdf_path && $showActions)
|
@if($invoice->pdf_path && $showActions)
|
||||||
@@ -1125,7 +1357,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
<script>
|
<script>
|
||||||
function shareInvoice() {
|
function shareInvoice() {
|
||||||
@@ -1142,6 +1373,28 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renumberChargeGroups() {
|
||||||
|
const groupsTbody = document.querySelector(
|
||||||
|
'.cg-groups-panel table.invoice-table tbody'
|
||||||
|
);
|
||||||
|
if (!groupsTbody) return;
|
||||||
|
|
||||||
|
let index = 1;
|
||||||
|
|
||||||
|
groupsTbody.querySelectorAll('tr').forEach(row => {
|
||||||
|
if (row.classList.contains('cg-items-row')) return;
|
||||||
|
|
||||||
|
const next = row.nextElementSibling;
|
||||||
|
const isMainGroupRow = next && next.classList.contains('cg-items-row');
|
||||||
|
if (!isMainGroupRow) return;
|
||||||
|
|
||||||
|
const firstCell = row.querySelector('td:first-child');
|
||||||
|
if (firstCell) {
|
||||||
|
firstCell.textContent = index++;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', function () {
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
const selectAll = document.getElementById('selectAllItems');
|
const selectAll = document.getElementById('selectAllItems');
|
||||||
const itemCheckboxes = document.querySelectorAll('.item-select-checkbox');
|
const itemCheckboxes = document.querySelectorAll('.item-select-checkbox');
|
||||||
@@ -1166,6 +1419,8 @@ document.addEventListener('DOMContentLoaded', function () {
|
|||||||
const cgRateHidden = document.getElementById('cgRateHidden');
|
const cgRateHidden = document.getElementById('cgRateHidden');
|
||||||
const cgForm = document.getElementById('chargeGroupForm');
|
const cgForm = document.getElementById('chargeGroupForm');
|
||||||
|
|
||||||
|
const cgGroupName = document.getElementById('cgGroupName');
|
||||||
|
|
||||||
function updateSelectionState() {
|
function updateSelectionState() {
|
||||||
let selectedCount = 0;
|
let selectedCount = 0;
|
||||||
itemCheckboxes.forEach(cb => {
|
itemCheckboxes.forEach(cb => {
|
||||||
@@ -1271,7 +1526,7 @@ document.addEventListener('DOMContentLoaded', function () {
|
|||||||
selectAll.checked = (checked > 0 && checked === total);
|
selectAll.checked = (checked > 0 && checked === total);
|
||||||
selectAll.indeterminate = (checked > 0 && checked < total);
|
selectAll.indeterminate = (checked > 0 && checked < total);
|
||||||
}
|
}
|
||||||
if (!chargeGroupBox.classList.contains('d-none')) {
|
if (chargeGroupBox && !chargeGroupBox.classList.contains('d-none')) {
|
||||||
fillChargeGroupItemsTable();
|
fillChargeGroupItemsTable();
|
||||||
refreshBasisSummaryAndSuggestion();
|
refreshBasisSummaryAndSuggestion();
|
||||||
}
|
}
|
||||||
@@ -1282,7 +1537,7 @@ document.addEventListener('DOMContentLoaded', function () {
|
|||||||
selectAll.addEventListener('change', function () {
|
selectAll.addEventListener('change', function () {
|
||||||
itemCheckboxes.forEach(cb => { if (!cb.disabled) cb.checked = selectAll.checked; });
|
itemCheckboxes.forEach(cb => { if (!cb.disabled) cb.checked = selectAll.checked; });
|
||||||
updateSelectionState();
|
updateSelectionState();
|
||||||
if (!chargeGroupBox.classList.contains('d-none')) {
|
if (chargeGroupBox && !chargeGroupBox.classList.contains('d-none')) {
|
||||||
fillChargeGroupItemsTable();
|
fillChargeGroupItemsTable();
|
||||||
refreshBasisSummaryAndSuggestion();
|
refreshBasisSummaryAndSuggestion();
|
||||||
}
|
}
|
||||||
@@ -1312,12 +1567,9 @@ document.addEventListener('DOMContentLoaded', function () {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MAIN CHANGE: normal form submit, फक्त hidden item_ids तयार करतो
|
||||||
if (cgForm) {
|
if (cgForm) {
|
||||||
cgForm.addEventListener('submit', function (e) {
|
cgForm.addEventListener('submit', function () {
|
||||||
// Stop normal form submit (no page reload)
|
|
||||||
e.preventDefault();
|
|
||||||
|
|
||||||
// 1) Collect selected item IDs
|
|
||||||
const selectedIds = [];
|
const selectedIds = [];
|
||||||
itemCheckboxes.forEach(cb => {
|
itemCheckboxes.forEach(cb => {
|
||||||
if (cb.checked && !cb.disabled) {
|
if (cb.checked && !cb.disabled) {
|
||||||
@@ -1325,31 +1577,28 @@ document.addEventListener('DOMContentLoaded', function () {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 2) Frontend validations (same as before)
|
|
||||||
if (selectedIds.length === 0) {
|
if (selectedIds.length === 0) {
|
||||||
alert('Please select at least one item for this charge group.');
|
alert('Please select at least one item for this charge group.');
|
||||||
return;
|
// default submit रोखण्यासाठी return false
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!cgBasisSelect || !cgBasisSelect.value) {
|
if (!cgBasisSelect || !cgBasisSelect.value) {
|
||||||
alert('Please select a basis for this charge group.');
|
alert('Please select a basis for this charge group.');
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (!cgTotalChargeInput ||
|
||||||
!cgTotalChargeInput ||
|
|
||||||
!cgTotalChargeInput.value ||
|
!cgTotalChargeInput.value ||
|
||||||
parseFloat(cgTotalChargeInput.value) <= 0
|
parseFloat(cgTotalChargeInput.value) <= 0
|
||||||
) {
|
) {
|
||||||
alert('Please enter total charges for this group.');
|
alert('Please enter total charges for this group.');
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3) Remove previously added hidden item_ids[]
|
|
||||||
const oldHidden = cgForm.querySelectorAll('input[name="item_ids[]"]');
|
const oldHidden = cgForm.querySelectorAll('input[name="item_ids[]"]');
|
||||||
oldHidden.forEach(el => el.remove());
|
oldHidden.forEach(el => el.remove());
|
||||||
|
|
||||||
// 4) Add fresh hidden item_ids[] for current selection
|
|
||||||
selectedIds.forEach(id => {
|
selectedIds.forEach(id => {
|
||||||
const input = document.createElement('input');
|
const input = document.createElement('input');
|
||||||
input.type = 'hidden';
|
input.type = 'hidden';
|
||||||
@@ -1358,198 +1607,25 @@ document.addEventListener('DOMContentLoaded', function () {
|
|||||||
cgForm.appendChild(input);
|
cgForm.appendChild(input);
|
||||||
});
|
});
|
||||||
|
|
||||||
// 5) Build AJAX request
|
// इथे e.preventDefault नाही; normal submit होऊ दे
|
||||||
const url = cgForm.action;
|
return true;
|
||||||
const formData = new FormData(cgForm);
|
|
||||||
|
|
||||||
// Optional: disable save button while processing
|
|
||||||
if (cgSaveBtn) {
|
|
||||||
cgSaveBtn.disabled = true;
|
|
||||||
cgSaveBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Saving...';
|
|
||||||
}
|
|
||||||
|
|
||||||
fetch(url, {
|
|
||||||
method: 'POST',
|
|
||||||
body: formData,
|
|
||||||
headers: {
|
|
||||||
'X-Requested-With': 'XMLHttpRequest'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.then(res => {
|
|
||||||
if (!res.ok) {
|
|
||||||
throw new Error('Request failed with status ' + res.status);
|
|
||||||
}
|
|
||||||
return res.json();
|
|
||||||
})
|
|
||||||
.then(data => {
|
|
||||||
if (!data.success) {
|
|
||||||
throw new Error(data.message || 'Failed to create charge group.');
|
|
||||||
}
|
|
||||||
|
|
||||||
const group = data.group;
|
|
||||||
|
|
||||||
// 6) Append new group main row + hidden items row
|
|
||||||
const groupsTbody = document.querySelector(
|
|
||||||
'.cg-groups-panel table.invoice-table tbody'
|
|
||||||
);
|
|
||||||
|
|
||||||
if (groupsTbody && group) {
|
|
||||||
const currentMainRows = groupsTbody.querySelectorAll(
|
|
||||||
'tr:not(.cg-items-row)'
|
|
||||||
).length;
|
|
||||||
const index = currentMainRows + 1;
|
|
||||||
|
|
||||||
// Main summary row (same structure as Blade)
|
|
||||||
const mainRow = document.createElement('tr');
|
|
||||||
mainRow.innerHTML = `
|
|
||||||
<td>${index}</td>
|
|
||||||
<td style="font-weight:600;">
|
|
||||||
${group.group_name ? group.group_name : 'Group #' + group.id}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<span style="text-transform:uppercase;font-size:0.75rem;font-weight:700;color:var(--text-muted);">
|
|
||||||
${group.basis_type}
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
<td class="text-end" style="font-family:'JetBrains Mono',monospace;font-size:0.82rem;">
|
|
||||||
${Number(group.basis_value).toFixed(3)}
|
|
||||||
</td>
|
|
||||||
<td class="text-end" style="font-family:'JetBrains Mono',monospace;font-size:0.82rem;">
|
|
||||||
${Number(group.rate).toFixed(2)}
|
|
||||||
</td>
|
|
||||||
<td class="text-end price-blue">
|
|
||||||
${Number(group.total_charge).toFixed(2)}
|
|
||||||
</td>
|
|
||||||
<td class="text-center">
|
|
||||||
<button type="button"
|
|
||||||
class="cg-toggle-btn cg-toggle-items"
|
|
||||||
data-group-id="${group.id}">
|
|
||||||
<i class="fas fa-eye"></i> View
|
|
||||||
</button>
|
|
||||||
</td>
|
|
||||||
`;
|
|
||||||
|
|
||||||
// Hidden items row (same idea as Blade .cg-items-row)
|
|
||||||
const itemsRow = document.createElement('tr');
|
|
||||||
itemsRow.className = 'cg-items-row d-none';
|
|
||||||
itemsRow.setAttribute('data-group-id', group.id);
|
|
||||||
|
|
||||||
let itemsTableHtml = `
|
|
||||||
<div style="padding:1rem;background:#f8faff;border-radius:8px;margin:0.25rem 0;">
|
|
||||||
<div style="font-weight:700;font-size:0.8rem;margin-bottom:0.6rem;color:var(--primary);">
|
|
||||||
Items in this group
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
if (!group.items || group.items.length === 0) {
|
|
||||||
itemsTableHtml += `
|
|
||||||
<div style="color:var(--text-muted);font-size:0.82rem;">
|
|
||||||
No items linked.
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
} else {
|
|
||||||
itemsTableHtml += `
|
|
||||||
<div class="table-responsive">
|
|
||||||
<table class="invoice-table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>#</th>
|
|
||||||
<th>Description</th>
|
|
||||||
<th class="text-center">QTY</th>
|
|
||||||
<th class="text-center">TTL QTY</th>
|
|
||||||
<th class="text-center">CBM</th>
|
|
||||||
<th class="text-center">TTL CBM</th>
|
|
||||||
<th class="text-center">KG</th>
|
|
||||||
<th class="text-center">TTL KG</th>
|
|
||||||
<th class="text-end">TTL Amount</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
`;
|
|
||||||
|
|
||||||
group.items.forEach((it, idx) => {
|
|
||||||
itemsTableHtml += `
|
|
||||||
<tr>
|
|
||||||
<td>${idx + 1}</td>
|
|
||||||
<td>${it.description ?? ''}</td>
|
|
||||||
<td class="text-center">${it.qty ?? ''}</td>
|
|
||||||
<td class="text-center">${it.ttlqty ?? ''}</td>
|
|
||||||
<td class="text-center">${it.cbm ?? ''}</td>
|
|
||||||
<td class="text-center">${it.ttlcbm ?? ''}</td>
|
|
||||||
<td class="text-center">${it.kg ?? ''}</td>
|
|
||||||
<td class="text-center">${it.ttlkg ?? ''}</td>
|
|
||||||
<td class="text-end">${Number(it.amount ?? 0).toFixed(2)}</td>
|
|
||||||
</tr>
|
|
||||||
`;
|
|
||||||
});
|
|
||||||
|
|
||||||
itemsTableHtml += `
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
itemsTableHtml += `</div>`;
|
|
||||||
|
|
||||||
itemsRow.innerHTML = `
|
|
||||||
<td colspan="7">
|
|
||||||
${itemsTableHtml}
|
|
||||||
</td>
|
|
||||||
`;
|
|
||||||
|
|
||||||
// Append main row + items row in order
|
|
||||||
groupsTbody.appendChild(mainRow);
|
|
||||||
groupsTbody.appendChild(itemsRow);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 7) Reset Charge Group UI (hide panel, uncheck items)
|
|
||||||
if (chargeGroupBox) {
|
|
||||||
chargeGroupBox.classList.add('d-none');
|
|
||||||
}
|
|
||||||
itemCheckboxes.forEach(cb => {
|
|
||||||
if (!cb.disabled) cb.checked = false;
|
|
||||||
});
|
|
||||||
if (selectAll) {
|
|
||||||
selectAll.checked = false;
|
|
||||||
selectAll.indeterminate = false;
|
|
||||||
}
|
|
||||||
updateSelectionState();
|
|
||||||
|
|
||||||
// Optional: clear form
|
|
||||||
if (cgGroupName) cgGroupName.value = '';
|
|
||||||
if (cgBasisSelect) cgBasisSelect.value = '';
|
|
||||||
if (cgRateInput) cgRateInput.value = '';
|
|
||||||
if (cgBasisValueSpan) cgBasisValueSpan.textContent = '0';
|
|
||||||
if (cgBasisLabelSpan) cgBasisLabelSpan.textContent = '–';
|
|
||||||
if (cgSuggestedTotalSpan) cgSuggestedTotalSpan.textContent = '0';
|
|
||||||
if (cgTotalChargeInput) cgTotalChargeInput.value = '';
|
|
||||||
})
|
|
||||||
|
|
||||||
.catch(err => {
|
|
||||||
console.error(err);
|
|
||||||
alert('Error creating charge group. Please try again.');
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
if (cgSaveBtn) {
|
|
||||||
cgSaveBtn.disabled = false;
|
|
||||||
cgSaveBtn.innerHTML = '<i class="fas fa-save"></i> Save Charge Group';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
updateSelectionState();
|
updateSelectionState();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// View/Hide group items
|
||||||
document.addEventListener('DOMContentLoaded', function () {
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
document.addEventListener('click', function (e) {
|
document.addEventListener('click', function (e) {
|
||||||
if (!e.target.classList.contains('cg-toggle-items') &&
|
if (!e.target.classList.contains('cg-toggle-items') &&
|
||||||
!e.target.closest('.cg-toggle-items')) return;
|
!e.target.closest('.cg-toggle-items')) return;
|
||||||
|
|
||||||
const btn = e.target.closest('.cg-toggle-items') || e.target;
|
const btn = e.target.closest('.cg-toggle-items') || e.target;
|
||||||
const groupId = btn.getAttribute('data-group-id');
|
const groupId = btn.getAttribute('data-group-id');
|
||||||
const row = document.querySelector('.cg-items-row[data-group-id="' + groupId + '"]');
|
const row = document.querySelector('.cg-items-row[data-group-id="' + groupId + '"]');
|
||||||
if (!row) return;
|
if (!row) return;
|
||||||
|
|
||||||
row.classList.toggle('d-none');
|
row.classList.toggle('d-none');
|
||||||
const isHidden = row.classList.contains('d-none');
|
const isHidden = row.classList.contains('d-none');
|
||||||
btn.innerHTML = isHidden
|
btn.innerHTML = isHidden
|
||||||
@@ -1557,6 +1633,25 @@ document.addEventListener('DOMContentLoaded', function () {
|
|||||||
: '<i class="fas fa-eye-slash"></i> Hide';
|
: '<i class="fas fa-eye-slash"></i> Hide';
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// simple select all (duplicate राहिला तरी harmless; गरज असल्यास काढू शकतो)
|
||||||
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
|
const selectAll = document.getElementById('selectAllItems');
|
||||||
|
const checkboxes = document.querySelectorAll('.item-select-checkbox');
|
||||||
|
|
||||||
|
if (selectAll) {
|
||||||
|
selectAll.addEventListener('change', function () {
|
||||||
|
checkboxes.forEach(cb => {
|
||||||
|
if (!cb.disabled) {
|
||||||
|
cb.checked = selectAll.checked;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -577,7 +577,7 @@
|
|||||||
<th>Container Date</th>
|
<th>Container Date</th>
|
||||||
<th>Company Name</th>
|
<th>Company Name</th>
|
||||||
<th>Customer Name</th>
|
<th>Customer Name</th>
|
||||||
<th>Mark No</th>
|
{{-- <th>Mark No</th> --}}
|
||||||
<th>Invoice No</th>
|
<th>Invoice No</th>
|
||||||
<th>Invoice Date</th>
|
<th>Invoice Date</th>
|
||||||
<th>Invoice Amount</th>
|
<th>Invoice Amount</th>
|
||||||
@@ -609,11 +609,11 @@
|
|||||||
{{ $r->customer_name ?? '-' }}
|
{{ $r->customer_name ?? '-' }}
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
{{-- <td>
|
||||||
<span class="data-highlight" title="{{ $r->mark_no }}">
|
<span class="data-highlight" title="{{ $r->mark_no }}">
|
||||||
{{ $r->mark_no ?? '-' }}
|
{{ $r->mark_no ?? '-' }}
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td> --}}
|
||||||
<td>
|
<td>
|
||||||
<span class="data-highlight" title="{{ $r->invoice_number }}">
|
<span class="data-highlight" title="{{ $r->invoice_number }}">
|
||||||
{{ $r->invoice_number }}
|
{{ $r->invoice_number }}
|
||||||
@@ -640,7 +640,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
@empty
|
@empty
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="9">
|
<td colspan="8">
|
||||||
<div class="empty-state">
|
<div class="empty-state">
|
||||||
<div class="empty-icon">
|
<div class="empty-icon">
|
||||||
<i class="fas fa-inbox"></i>
|
<i class="fas fa-inbox"></i>
|
||||||
@@ -799,7 +799,7 @@
|
|||||||
if (filteredReports.length === 0) {
|
if (filteredReports.length === 0) {
|
||||||
tbody.innerHTML = `
|
tbody.innerHTML = `
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="9">
|
<td colspan="8">
|
||||||
<div class="empty-state">
|
<div class="empty-state">
|
||||||
<div class="empty-icon">
|
<div class="empty-icon">
|
||||||
<i class="fas fa-inbox"></i>
|
<i class="fas fa-inbox"></i>
|
||||||
@@ -842,11 +842,11 @@
|
|||||||
${report.customer_name || '-'}
|
${report.customer_name || '-'}
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<!-- <td>
|
||||||
<span class="data-highlight" title="${report.mark_no || ''}">
|
<span class="data-highlight" title="${report.mark_no || ''}">
|
||||||
${report.mark_no || '-'}
|
${report.mark_no || '-'}
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td> -->
|
||||||
<td>
|
<td>
|
||||||
<span class="data-highlight" title="${report.invoice_number || ''}">
|
<span class="data-highlight" title="${report.invoice_number || ''}">
|
||||||
${report.invoice_number || '-'}
|
${report.invoice_number || '-'}
|
||||||
|
|||||||
@@ -15,11 +15,19 @@
|
|||||||
@endphp
|
@endphp
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
/* [ALL YOUR ORIGINAL CSS HERE - SAME AS BEFORE] */
|
|
||||||
@keyframes fadeInUp {0% { transform: translateY(20px); opacity: 0; }100% { transform: translateY(0); opacity: 1; }}
|
@keyframes fadeInUp {0% { transform: translateY(20px); opacity: 0; }100% { transform: translateY(0); opacity: 1; }}
|
||||||
.card, .custom-table-wrapper { animation: fadeInUp 0.8s ease both; }
|
.card, .custom-table-wrapper { animation: fadeInUp 0.8s ease both; }
|
||||||
.custom-table tbody tr { transition: all 0.25s ease-in-out; }
|
|
||||||
.custom-table tbody tr:hover { background-color: #fffbea; transform: scale(1.01); box-shadow: 0 2px 6px rgba(0,0,0,0.05); }
|
/* ✅ ROW HOVER EFFECT COMPLETELY DISABLED */
|
||||||
|
.custom-table tbody tr {
|
||||||
|
transition: none !important;
|
||||||
|
}
|
||||||
|
.custom-table tbody tr:hover {
|
||||||
|
background-color: transparent !important;
|
||||||
|
transform: none !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
.priority-badge {
|
.priority-badge {
|
||||||
display: inline-flex; align-items: center; font-size: 13.5px; padding: 6px 16px; border-radius: 12px; font-weight: 600;
|
display: inline-flex; align-items: center; font-size: 13.5px; padding: 6px 16px; border-radius: 12px; font-weight: 600;
|
||||||
box-shadow: 0 1px 2px 0 rgba(230, 206, 206, 0.15); width: 90px; min-height: 28px; justify-content: center;
|
box-shadow: 0 1px 2px 0 rgba(230, 206, 206, 0.15); width: 90px; min-height: 28px; justify-content: center;
|
||||||
@@ -29,18 +37,22 @@
|
|||||||
.priority-high { background: linear-gradient(135deg, #ff8a8a, #d12929); }
|
.priority-high { background: linear-gradient(135deg, #ff8a8a, #d12929); }
|
||||||
.priority-medium { background: linear-gradient(135deg, #ffe390, #f5b041); }
|
.priority-medium { background: linear-gradient(135deg, #ffe390, #f5b041); }
|
||||||
.priority-low { background: linear-gradient(135deg, #b8f0c2, #1d8660); }
|
.priority-low { background: linear-gradient(135deg, #b8f0c2, #1d8660); }
|
||||||
|
|
||||||
.custom-table thead th {
|
.custom-table thead th {
|
||||||
text-align: center; font-weight: 700; color: #ffffffff; padding: 14px; font-size: 17px; letter-spacing: 0.5px;
|
text-align: center; font-weight: 700; color: #ffffffff; padding: 14px; font-size: 17px; letter-spacing: 0.5px;
|
||||||
border-bottom: 2px solid #bfbfbf; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);;
|
border-bottom: 2px solid #bfbfbf; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
}
|
}
|
||||||
.custom-table thead tr:first-child th:first-child { border-top-left-radius: 12px; }
|
.custom-table thead tr:first-child th:first-child { border-top-left-radius: 12px; }
|
||||||
.custom-table thead tr:first-child th:last-child { border-top-right-radius: 12px; }
|
.custom-table thead tr:first-child th:last-child { border-top-right-radius: 12px; }
|
||||||
.custom-table tbody tr:last-child td:first-child { border-bottom-left-radius: 12px; }
|
.custom-table tbody tr:last-child td:first-child { border-bottom-left-radius: 12px; }
|
||||||
.custom-table tbody tr:last-child td:last-child { border-bottom-right-radius: 12px; }
|
.custom-table tbody tr:last-child td:last-child { border-bottom-right-radius: 12px; }
|
||||||
|
|
||||||
.input-group input { border-radius: 10px 0 0 10px; border: 1px solid #ccc; box-shadow: inset 0 1px 3px rgba(0,0,0,0.05); }
|
.input-group input { border-radius: 10px 0 0 10px; border: 1px solid #ccc; box-shadow: inset 0 1px 3px rgba(0,0,0,0.05); }
|
||||||
.input-group .btn { border-radius: 0 10px 10px 0; transition: all 0.2s ease-in-out; }
|
.input-group .btn { border-radius: 0 10px 10px 0; transition: all 0.2s ease-in-out; }
|
||||||
.input-group .btn:hover { background: #ffd65a; border-color: #ffd65a; color: #000; }
|
.input-group .btn:hover { background: #ffd65a; border-color: #ffd65a; color: #000; }
|
||||||
|
|
||||||
.card { border-radius: 16px; border: none; box-shadow: 0 4px 10px rgba(0,0,0,0.08); background: #fff; }
|
.card { border-radius: 16px; border: none; box-shadow: 0 4px 10px rgba(0,0,0,0.08); background: #fff; }
|
||||||
|
|
||||||
.badge {
|
.badge {
|
||||||
font-size: 11px !important; font-weight: 600 !important; padding: 7px 13px !important; border-radius: 20px !important;
|
font-size: 11px !important; font-weight: 600 !important; padding: 7px 13px !important; border-radius: 20px !important;
|
||||||
text-transform: uppercase; letter-spacing: 0.3px; display: inline-flex !important; align-items: center; justify-content: center;
|
text-transform: uppercase; letter-spacing: 0.3px; display: inline-flex !important; align-items: center; justify-content: center;
|
||||||
@@ -54,7 +66,12 @@
|
|||||||
@keyframes pulse {0% { box-shadow: 0 0 8px rgba(0,0,0,0.1); }50% { box-shadow: 0 0 14px rgba(0,0,0,0.15); }100% { box-shadow: 0 0 8px rgba(0,0,0,0.1); }}
|
@keyframes pulse {0% { box-shadow: 0 0 8px rgba(0,0,0,0.1); }50% { box-shadow: 0 0 14px rgba(0,0,0,0.15); }100% { box-shadow: 0 0 8px rgba(0,0,0,0.1); }}
|
||||||
.count-badge { --bs-badge-padding-x: 0.65em; --bs-badge-padding-y: 0.35em; --bs-badge-font-size: 0.75em; --bs-badge-font-weight: 700; --bs-badge-color: #fff; --bs-badge-border-radius: var(--bs-border-radius); }
|
.count-badge { --bs-badge-padding-x: 0.65em; --bs-badge-padding-y: 0.35em; --bs-badge-font-size: 0.75em; --bs-badge-font-weight: 700; --bs-badge-color: #fff; --bs-badge-border-radius: var(--bs-border-radius); }
|
||||||
h4.fw-bold { background: linear-gradient(90deg, #000000ff 0%, #030302ff 100%); -webkit-background-clip: text; -webkit-text-fill-color: transparent; font-weight: 800; letter-spacing: 1px; }
|
h4.fw-bold { background: linear-gradient(90deg, #000000ff 0%, #030302ff 100%); -webkit-background-clip: text; -webkit-text-fill-color: transparent; font-weight: 800; letter-spacing: 1px; }
|
||||||
.custom-table tbody td:last-child { text-align: center !important; vertical-align: middle !important; }
|
|
||||||
|
.custom-table th,
|
||||||
|
.custom-table td {
|
||||||
|
text-align: center;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
/* ===== PAGINATION STYLES ===== */
|
/* ===== PAGINATION STYLES ===== */
|
||||||
.pagination-container {
|
.pagination-container {
|
||||||
@@ -283,6 +300,15 @@
|
|||||||
border: 1px solid #ef4444;
|
border: 1px solid #ef4444;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ✅ TABLE WRAPPER – REMOVE HORIZONTAL SCROLL */
|
||||||
|
.custom-table-wrapper {
|
||||||
|
overflow-x: hidden !important;
|
||||||
|
}
|
||||||
|
.custom-table {
|
||||||
|
width: 100%;
|
||||||
|
table-layout: auto;
|
||||||
|
}
|
||||||
|
|
||||||
/* Responsive styles */
|
/* Responsive styles */
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.pagination-container {
|
.pagination-container {
|
||||||
@@ -307,17 +333,13 @@
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* ==============================================
|
|
||||||
PROFILE UPDATE REQUEST BUTTON BADGE FIX
|
|
||||||
============================================== */
|
|
||||||
|
|
||||||
/* Ensure button is positioning context */
|
/* PROFILE UPDATE REQUEST BUTTON BADGE FIX */
|
||||||
a.btn.btn-primary.position-relative {
|
a.btn.btn-primary.position-relative {
|
||||||
position: relative;
|
position: relative;
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Fix badge inside Profile Update Requests button */
|
|
||||||
a.btn.btn-primary.position-relative .badge {
|
a.btn.btn-primary.position-relative .badge {
|
||||||
width: 30px !important;
|
width: 30px !important;
|
||||||
height: 30px !important;
|
height: 30px !important;
|
||||||
@@ -331,14 +353,7 @@ a.btn.btn-primary.position-relative .badge {
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
animation: none !important;
|
animation: none !important;
|
||||||
box-shadow: 0 0 0 2px #ffffff;
|
box-shadow: 0 0 0 2px #ffffff;
|
||||||
|
|
||||||
}
|
}
|
||||||
.custom-table th,
|
|
||||||
.custom-table td {
|
|
||||||
text-align: center;
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<!-- Counts -->
|
<!-- Counts -->
|
||||||
@@ -359,11 +374,9 @@ a.btn.btn-primary.position-relative .badge {
|
|||||||
@endcan
|
@endcan
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<!-- Search + Table -->
|
<!-- Search + Table -->
|
||||||
<div class="card mb-4 shadow-sm">
|
<div class="card mb-4 shadow-sm">
|
||||||
<div class="card-body pb-1">
|
<div class="card-body pb-1">
|
||||||
<!-- Updated Search Bar with Status Badges in the same line -->
|
|
||||||
<div class="search-container">
|
<div class="search-container">
|
||||||
<form method="GET" action="" class="search-form">
|
<form method="GET" action="" class="search-form">
|
||||||
<div class="search-input-group">
|
<div class="search-input-group">
|
||||||
@@ -385,9 +398,21 @@ a.btn.btn-primary.position-relative .badge {
|
|||||||
|
|
||||||
<div class="table-responsive custom-table-wrapper">
|
<div class="table-responsive custom-table-wrapper">
|
||||||
<table class="table align-middle mb-0 custom-table">
|
<table class="table align-middle mb-0 custom-table">
|
||||||
<thead><tr>
|
<thead>
|
||||||
<th>#</th><th>Request ID</th><th>Name</th><th>Company</th><th>Email</th><th>Mobile</th><th>Address</th><th>Priority</th><th>Date</th><th>Status</th><th>Actions</th>
|
<tr>
|
||||||
</tr></thead>
|
<th>#</th>
|
||||||
|
<th>Request ID</th>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Company</th>
|
||||||
|
<th>Email</th>
|
||||||
|
<th>Mobile</th>
|
||||||
|
<th>Address</th>
|
||||||
|
<th>Priority</th>
|
||||||
|
<th>Date</th>
|
||||||
|
<th>Status</th>
|
||||||
|
<th>Actions</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@forelse($currentItems as $index => $req)
|
@forelse($currentItems as $index => $req)
|
||||||
<tr>
|
<tr>
|
||||||
@@ -399,16 +424,25 @@ a.btn.btn-primary.position-relative .badge {
|
|||||||
<td>{{ $req->mobile_no }}</td>
|
<td>{{ $req->mobile_no }}</td>
|
||||||
<td>{{ Str::limit($req->address, 30) }}</td>
|
<td>{{ Str::limit($req->address, 30) }}</td>
|
||||||
<td>
|
<td>
|
||||||
@if(strtolower($req->priority) == 'high')<span class="priority-badge priority-high">High</span>
|
@if(strtolower($req->priority) == 'high')
|
||||||
@elseif(strtolower($req->priority) == 'medium')<span class="priority-badge priority-medium">Medium</span>
|
<span class="priority-badge priority-high">High</span>
|
||||||
@elseif(strtolower($req->priority) == 'low')<span class="priority-badge priority-low">Low</span>
|
@elseif(strtolower($req->priority) == 'medium')
|
||||||
@else{{ $req->priority ?? 'N/A' }}@endif
|
<span class="priority-badge priority-medium">Medium</span>
|
||||||
|
@elseif(strtolower($req->priority) == 'low')
|
||||||
|
<span class="priority-badge priority-low">Low</span>
|
||||||
|
@else
|
||||||
|
{{ $req->priority ?? 'N/A' }}
|
||||||
|
@endif
|
||||||
</td>
|
</td>
|
||||||
<td>{{ $req->date }}</td>
|
<td>{{ $req->date }}</td>
|
||||||
<td>
|
<td>
|
||||||
@if($req->status == 'approved')<span class="badge badge-approved"><i class="bi bi-check-circle-fill status-icon"></i>Approved</span>
|
@if($req->status == 'approved')
|
||||||
@elseif($req->status == 'rejected')<span class="badge badge-rejected"><i class="bi bi-x-circle-fill status-icon"></i>Rejected</span>
|
<span class="badge badge-approved"><i class="bi bi-check-circle-fill status-icon"></i>Approved</span>
|
||||||
@else<span class="badge badge-pending"><i class="bi bi-clock-fill status-icon"></i>Pending</span>@endif
|
@elseif($req->status == 'rejected')
|
||||||
|
<span class="badge badge-rejected"><i class="bi bi-x-circle-fill status-icon"></i>Rejected</span>
|
||||||
|
@else
|
||||||
|
<span class="badge badge-pending"><i class="bi bi-clock-fill status-icon"></i>Pending</span>
|
||||||
|
@endif
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
@if($req->status == 'pending')
|
@if($req->status == 'pending')
|
||||||
@@ -425,7 +459,6 @@ a.btn.btn-primary.position-relative .badge {
|
|||||||
<span class="text-muted">No Action</span>
|
<span class="text-muted">No Action</span>
|
||||||
@endif
|
@endif
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
</tr>
|
</tr>
|
||||||
@empty
|
@empty
|
||||||
<tr><td colspan="11" class="text-center text-muted py-4">No records found.</td></tr>
|
<tr><td colspan="11" class="text-center text-muted py-4">No records found.</td></tr>
|
||||||
|
|||||||
@@ -4,6 +4,12 @@
|
|||||||
|
|
||||||
@section('content')
|
@section('content')
|
||||||
<style>
|
<style>
|
||||||
|
html, body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
width: 100%;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
/* Hide scrollbar but keep scroll functionality */
|
/* Hide scrollbar but keep scroll functionality */
|
||||||
html,
|
html,
|
||||||
|
|||||||
@@ -4,6 +4,12 @@
|
|||||||
|
|
||||||
@section('content')
|
@section('content')
|
||||||
<style>
|
<style>
|
||||||
|
html, body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
width: 100%;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
:root {
|
:root {
|
||||||
--primary-gradient: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
--primary-gradient: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
--secondary-gradient: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
|
--secondary-gradient: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
|
||||||
|
|||||||
@@ -4,6 +4,12 @@
|
|||||||
|
|
||||||
@section('content')
|
@section('content')
|
||||||
<style>
|
<style>
|
||||||
|
html, body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
width: 100%;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
:root {
|
:root {
|
||||||
--primary: #4361ee;
|
--primary: #4361ee;
|
||||||
--primary-dark: #3a56d4;
|
--primary-dark: #3a56d4;
|
||||||
@@ -35,7 +41,6 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-staff-bar::before {
|
.search-staff-bar::before {
|
||||||
content: '';
|
content: '';
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@@ -46,12 +51,10 @@
|
|||||||
background: rgba(255,255,255,0.1);
|
background: rgba(255,255,255,0.1);
|
||||||
z-index: 0;
|
z-index: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-staff-bar > * {
|
.search-staff-bar > * {
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-staff-bar input,
|
.search-staff-bar input,
|
||||||
.search-staff-bar select {
|
.search-staff-bar select {
|
||||||
padding: 12px 16px;
|
padding: 12px 16px;
|
||||||
@@ -64,14 +67,12 @@
|
|||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
color: var(--dark);
|
color: var(--dark);
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-staff-bar input:focus,
|
.search-staff-bar input:focus,
|
||||||
.search-staff-bar select:focus {
|
.search-staff-bar select:focus {
|
||||||
background: white;
|
background: white;
|
||||||
box-shadow: 0 0 0 3px rgba(255,255,255,0.3);
|
box-shadow: 0 0 0 3px rgba(255,255,255,0.3);
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-add-staff {
|
.btn-add-staff {
|
||||||
background: rgba(255,255,255,0.2);
|
background: rgba(255,255,255,0.2);
|
||||||
backdrop-filter: blur(10px);
|
backdrop-filter: blur(10px);
|
||||||
@@ -88,18 +89,15 @@
|
|||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-add-staff:hover {
|
.btn-add-staff:hover {
|
||||||
background: rgba(255,255,255,0.3);
|
background: rgba(255,255,255,0.3);
|
||||||
transform: translateY(-2px);
|
transform: translateY(-2px);
|
||||||
box-shadow: 0 10px 20px rgba(0,0,0,0.1);
|
box-shadow: 0 10px 20px rgba(0,0,0,0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-icon {
|
.search-icon {
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
filter: drop-shadow(0 2px 4px rgba(0,0,0,0.1));
|
filter: drop-shadow(0 2px 4px rgba(0,0,0,0.1));
|
||||||
}
|
}
|
||||||
|
|
||||||
.user-icon {
|
.user-icon {
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
}
|
}
|
||||||
@@ -123,12 +121,10 @@
|
|||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card:hover {
|
.card:hover {
|
||||||
transform: translateY(-5px);
|
transform: translateY(-5px);
|
||||||
box-shadow: var(--hover-shadow);
|
box-shadow: var(--hover-shadow);
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-header {
|
.card-header {
|
||||||
background: var(--gradient-primary);
|
background: var(--gradient-primary);
|
||||||
color: white;
|
color: white;
|
||||||
@@ -136,7 +132,6 @@
|
|||||||
padding: 20px 25px;
|
padding: 20px 25px;
|
||||||
border-radius: 16px 16px 0 0 !important;
|
border-radius: 16px 16px 0 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-header h5 {
|
.card-header h5 {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
@@ -148,9 +143,8 @@
|
|||||||
/* Table Styles - Similar to Shipment */
|
/* Table Styles - Similar to Shipment */
|
||||||
.table-responsive {
|
.table-responsive {
|
||||||
border-radius: 0 0 16px 16px;
|
border-radius: 0 0 16px 16px;
|
||||||
overflow-x: auto;
|
overflow-x: hidden; /* horizontal scroll remove */
|
||||||
}
|
}
|
||||||
|
|
||||||
.table {
|
.table {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
border-collapse: separate;
|
border-collapse: separate;
|
||||||
@@ -158,7 +152,6 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.table thead th {
|
.table thead th {
|
||||||
background: #f8f9fa;
|
background: #f8f9fa;
|
||||||
border: none;
|
border: none;
|
||||||
@@ -170,17 +163,15 @@
|
|||||||
border-bottom: 2px solid var(--border);
|
border-bottom: 2px solid var(--border);
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.table tbody tr {
|
.table tbody tr {
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
}
|
}
|
||||||
|
/* hover stable ठेवण्यासाठी */
|
||||||
.table tbody tr:hover {
|
.table tbody tr:hover {
|
||||||
background-color: #f8f9ff;
|
background-color: inherit;
|
||||||
transform: scale(1.01);
|
transform: none;
|
||||||
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.table tbody td {
|
.table tbody td {
|
||||||
padding: 14px 12px;
|
padding: 14px 12px;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
@@ -188,12 +179,11 @@
|
|||||||
border-bottom: 1px solid var(--border);
|
border-bottom: 1px solid var(--border);
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
.table tbody tr:last-child td {
|
.table tbody tr:last-child td {
|
||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Status Badges - Similar Style */
|
/* Status Badges */
|
||||||
.badge {
|
.badge {
|
||||||
padding: 6px 12px !important;
|
padding: 6px 12px !important;
|
||||||
border-radius: 20px !important;
|
border-radius: 20px !important;
|
||||||
@@ -205,26 +195,23 @@
|
|||||||
display: inline-block !important;
|
display: inline-block !important;
|
||||||
line-height: 1.2 !important;
|
line-height: 1.2 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.badge-active {
|
.badge-active {
|
||||||
background: linear-gradient(135deg, #d1fae5, #a7f3d0) !important;
|
background: linear-gradient(135deg, #d1fae5, #a7f3d0) !important;
|
||||||
color: #065f46 !important;
|
color: #065f46 !important;
|
||||||
border-color: #10b981 !important;
|
border-color: #10b981 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.badge-inactive {
|
.badge-inactive {
|
||||||
background: linear-gradient(135deg, #fecaca, #fca5a5) !important;
|
background: linear-gradient(135deg, #fecaca, #fca5a5) !important;
|
||||||
color: #991b1b !important;
|
color: #991b1b !important;
|
||||||
border-color: #ef4444 !important;
|
border-color: #ef4444 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.badge-pending {
|
.badge-pending {
|
||||||
background: linear-gradient(135deg, #fef3c7, #fde68a) !important;
|
background: linear-gradient(135deg, #fef3c7, #fde68a) !important;
|
||||||
color: #92400e !important;
|
color: #92400e !important;
|
||||||
border-color: #f59e0b !important;
|
border-color: #f59e0b !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Employee ID Badge - Similar to Shipment ID */
|
/* Employee ID Badge */
|
||||||
.employee-id-badge {
|
.employee-id-badge {
|
||||||
font-family: 'Courier New', monospace;
|
font-family: 'Courier New', monospace;
|
||||||
background: rgba(67, 97, 238, 0.1);
|
background: rgba(67, 97, 238, 0.1);
|
||||||
@@ -236,12 +223,11 @@
|
|||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Action Buttons - Similar Style */
|
/* Action Buttons */
|
||||||
.action-buttons {
|
.action-buttons {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-action {
|
.btn-action {
|
||||||
padding: 6px 12px;
|
padding: 6px 12px;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
@@ -255,30 +241,26 @@
|
|||||||
border: none;
|
border: none;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-edit {
|
.btn-edit {
|
||||||
background: linear-gradient(135deg, #4cc9f0, #4361ee);
|
background: linear-gradient(135deg, #4cc9f0, #4361ee);
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-edit:hover {
|
.btn-edit:hover {
|
||||||
background: linear-gradient(135deg, #38bdf8, #3a56d4);
|
background: linear-gradient(135deg, #38bdf8, #3a56d4);
|
||||||
transform: translateY(-2px);
|
transform: translateY(-2px);
|
||||||
box-shadow: 0 4px 12px rgba(76, 201, 240, 0.3);
|
box-shadow: 0 4px 12px rgba(76, 201, 240, 0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-delete {
|
.btn-delete {
|
||||||
background: linear-gradient(135deg, #f87171, #ef4444);
|
background: linear-gradient(135deg, #f87171, #ef4444);
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-delete:hover {
|
.btn-delete:hover {
|
||||||
background: linear-gradient(135deg, #ef4444, #dc2626);
|
background: linear-gradient(135deg, #ef4444, #dc2626);
|
||||||
transform: translateY(-2px);
|
transform: translateY(-2px);
|
||||||
box-shadow: 0 4px 12px rgba(239, 68, 68, 0.3);
|
box-shadow: 0 4px 12px rgba(239, 68, 68, 0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Success Message - Similar Style */
|
/* Success Message */
|
||||||
.alert-success {
|
.alert-success {
|
||||||
background: linear-gradient(135deg, #e6ffed, #d1f7e5);
|
background: linear-gradient(135deg, #e6ffed, #d1f7e5);
|
||||||
border: 1px solid #b6f0c6;
|
border: 1px solid #b6f0c6;
|
||||||
@@ -291,7 +273,6 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 0.75rem;
|
gap: 0.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.alert-success:before {
|
.alert-success:before {
|
||||||
content: '✓';
|
content: '✓';
|
||||||
background: var(--success);
|
background: var(--success);
|
||||||
@@ -311,7 +292,6 @@
|
|||||||
padding: 3rem 1rem;
|
padding: 3rem 1rem;
|
||||||
color: var(--gray);
|
color: var(--gray);
|
||||||
}
|
}
|
||||||
|
|
||||||
.empty-state:before {
|
.empty-state:before {
|
||||||
content: '👤';
|
content: '👤';
|
||||||
font-size: 3rem;
|
font-size: 3rem;
|
||||||
@@ -331,14 +311,13 @@
|
|||||||
border: 1px solid rgba(67, 97, 238, 0.2);
|
border: 1px solid rgba(67, 97, 238, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Stats Cards - Similar to Shipment Totals */
|
/* Stats Cards */
|
||||||
.stats-cards {
|
.stats-cards {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 20px;
|
gap: 20px;
|
||||||
margin-bottom: 30px;
|
margin-bottom: 30px;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.stat-card {
|
.stat-card {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
min-width: 200px;
|
min-width: 200px;
|
||||||
@@ -351,12 +330,10 @@
|
|||||||
gap: 15px;
|
gap: 15px;
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.stat-card:hover {
|
.stat-card:hover {
|
||||||
transform: translateY(-3px);
|
transform: translateY(-3px);
|
||||||
box-shadow: var(--hover-shadow);
|
box-shadow: var(--hover-shadow);
|
||||||
}
|
}
|
||||||
|
|
||||||
.stat-icon {
|
.stat-icon {
|
||||||
width: 50px;
|
width: 50px;
|
||||||
height: 50px;
|
height: 50px;
|
||||||
@@ -366,24 +343,20 @@
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.stat-icon.total {
|
.stat-icon.total {
|
||||||
background: linear-gradient(135deg, #e6f3ff, #c2d9ff);
|
background: linear-gradient(135deg, #e6f3ff, #c2d9ff);
|
||||||
color: var(--primary);
|
color: var(--primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.stat-icon.active {
|
.stat-icon.active {
|
||||||
background: linear-gradient(135deg, #d1fae5, #a7f3d0);
|
background: linear-gradient(135deg, #d1fae5, #a7f3d0);
|
||||||
color: #10b981;
|
color: #10b981;
|
||||||
}
|
}
|
||||||
|
|
||||||
.stat-content h3 {
|
.stat-content h3 {
|
||||||
font-size: 1.8rem;
|
font-size: 1.8rem;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
color: var(--dark);
|
color: var(--dark);
|
||||||
}
|
}
|
||||||
|
|
||||||
.stat-content p {
|
.stat-content p {
|
||||||
color: var(--gray);
|
color: var(--gray);
|
||||||
margin: 4px 0 0 0;
|
margin: 4px 0 0 0;
|
||||||
@@ -399,19 +372,16 @@
|
|||||||
padding: 12px 25px;
|
padding: 12px 25px;
|
||||||
border-top: 1px solid #eef3fb;
|
border-top: 1px solid #eef3fb;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pagination-info {
|
.pagination-info {
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
color: #9ba5bb;
|
color: #9ba5bb;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pagination-controls {
|
.pagination-controls {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pagination-btn {
|
.pagination-btn {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border: 1px solid #e3eaf6;
|
border: 1px solid #e3eaf6;
|
||||||
@@ -428,13 +398,11 @@
|
|||||||
min-width: 40px;
|
min-width: 40px;
|
||||||
height: 32px;
|
height: 32px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pagination-btn:hover:not(:disabled) {
|
.pagination-btn:hover:not(:disabled) {
|
||||||
background: #1a2951;
|
background: #1a2951;
|
||||||
color: white;
|
color: white;
|
||||||
border-color: #1a2951;
|
border-color: #1a2951;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pagination-btn:disabled {
|
.pagination-btn:disabled {
|
||||||
background: #f8fafc;
|
background: #f8fafc;
|
||||||
color: #cbd5e0;
|
color: #cbd5e0;
|
||||||
@@ -442,7 +410,6 @@
|
|||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
opacity: 0.6;
|
opacity: 0.6;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pagination-page-btn {
|
.pagination-page-btn {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border: 1px solid #e3eaf6;
|
border: 1px solid #e3eaf6;
|
||||||
@@ -456,25 +423,21 @@
|
|||||||
min-width: 36px;
|
min-width: 36px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pagination-page-btn:hover {
|
.pagination-page-btn:hover {
|
||||||
background: #1a2951;
|
background: #1a2951;
|
||||||
color: white;
|
color: white;
|
||||||
border-color: #1a2951;
|
border-color: #1a2951;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pagination-page-btn.active {
|
.pagination-page-btn.active {
|
||||||
background: #1a2951;
|
background: #1a2951;
|
||||||
color: white;
|
color: white;
|
||||||
border-color: #1a2951;
|
border-color: #1a2951;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pagination-pages {
|
.pagination-pages {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 4px;
|
gap: 4px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pagination-ellipsis {
|
.pagination-ellipsis {
|
||||||
color: #9ba5bb;
|
color: #9ba5bb;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
@@ -485,17 +448,14 @@
|
|||||||
.stats-cards {
|
.stats-cards {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.stat-card {
|
.stat-card {
|
||||||
min-width: 100%;
|
min-width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pagination-container {
|
.pagination-container {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pagination-controls {
|
.pagination-controls {
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
@@ -503,7 +463,6 @@
|
|||||||
</style>
|
</style>
|
||||||
|
|
||||||
<div class="container-fluid py-4">
|
<div class="container-fluid py-4">
|
||||||
|
|
||||||
@if(session('success'))
|
@if(session('success'))
|
||||||
<div class="alert-success">
|
<div class="alert-success">
|
||||||
{{ session('success') }}
|
{{ session('success') }}
|
||||||
|
|||||||
@@ -1,36 +0,0 @@
|
|||||||
<!-- <!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>Laravel Test Page</title>
|
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
||||||
|
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
||||||
<style>
|
|
||||||
/* Paste the provided CSS here */
|
|
||||||
</style>
|
|
||||||
|
|
||||||
</head>
|
|
||||||
<body class="bg-light">
|
|
||||||
|
|
||||||
<div class="container mt-5">
|
|
||||||
<div class="card shadow-lg border-0 rounded-4">
|
|
||||||
<div class="card-header bg-primary text-white">
|
|
||||||
<h4 class="mb-0">Laravel Gitea Test Page</h4>
|
|
||||||
</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<h5 class="text-success">✅ Laravel is running successfully!</h5>
|
|
||||||
<p class="mt-3">If you see this page, your Blade views and routing work perfectly.</p>
|
|
||||||
|
|
||||||
<ul class="list-group mt-4">
|
|
||||||
<li class="list-group-item"><strong>PHP Version:</strong> {{ PHP_VERSION }}</li>
|
|
||||||
<li class="list-group-item"><strong>Laravel Version:</strong> {{ app()->version() }}</li>
|
|
||||||
<li class="list-group-item"><strong>Environment:</strong> {{ app()->environment() }}</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<a href="{{ url('/') }}" class="btn btn-outline-primary mt-4">Go to Home testing done</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
</html> -->
|
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ use App\Http\Controllers\Admin\AdminChatController;
|
|||||||
use Illuminate\Support\Facades\Broadcast;
|
use Illuminate\Support\Facades\Broadcast;
|
||||||
use App\Http\Controllers\ContainerController;
|
use App\Http\Controllers\ContainerController;
|
||||||
|
|
||||||
|
|
||||||
// ---------------------------
|
// ---------------------------
|
||||||
// Public Front Page
|
// Public Front Page
|
||||||
// ---------------------------
|
// ---------------------------
|
||||||
@@ -50,7 +49,6 @@ Route::post('/broadcasting/auth', function (\Illuminate\Http\Request $request) {
|
|||||||
return response()->json(['message' => 'Unauthenticated'], 403);
|
return response()->json(['message' => 'Unauthenticated'], 403);
|
||||||
})->middleware('web');
|
})->middleware('web');
|
||||||
|
|
||||||
|
|
||||||
// ---------------------------
|
// ---------------------------
|
||||||
// ADMIN LOGIN ROUTES
|
// ADMIN LOGIN ROUTES
|
||||||
// ---------------------------
|
// ---------------------------
|
||||||
@@ -62,7 +60,6 @@ Route::prefix('admin')->group(function () {
|
|||||||
|
|
||||||
Broadcast::routes(['middleware' => ['web']]);
|
Broadcast::routes(['middleware' => ['web']]);
|
||||||
|
|
||||||
|
|
||||||
// ==========================================
|
// ==========================================
|
||||||
// PROTECTED ADMIN ROUTES (session protected)
|
// PROTECTED ADMIN ROUTES (session protected)
|
||||||
// ==========================================
|
// ==========================================
|
||||||
@@ -93,11 +90,6 @@ Route::prefix('admin')
|
|||||||
[\App\Http\Controllers\Admin\AdminOrderController::class, 'uploadExcelPreview']
|
[\App\Http\Controllers\Admin\AdminOrderController::class, 'uploadExcelPreview']
|
||||||
)->name('admin.orders.upload.excel.preview');
|
)->name('admin.orders.upload.excel.preview');
|
||||||
|
|
||||||
|
|
||||||
//---------------------------
|
|
||||||
// CONTAINER ROUTES
|
|
||||||
//---------------------------
|
|
||||||
// Index + list
|
|
||||||
//---------------------------
|
//---------------------------
|
||||||
// CONTAINER ROUTES
|
// CONTAINER ROUTES
|
||||||
//---------------------------
|
//---------------------------
|
||||||
@@ -121,8 +113,11 @@ Route::prefix('admin')
|
|||||||
|
|
||||||
Route::delete('/containers/{container}', [ContainerController::class, 'destroy'])
|
Route::delete('/containers/{container}', [ContainerController::class, 'destroy'])
|
||||||
->name('containers.destroy');
|
->name('containers.destroy');
|
||||||
|
Route::get('containers/{container}/download-pdf', [ContainerController::class, 'downloadPdf'])
|
||||||
|
->name('containers.download.pdf');
|
||||||
|
|
||||||
|
Route::get('/admin/containers/{container}/download-excel', [ContainerController::class, 'downloadExcel'])
|
||||||
|
->name('containers.download.excel');
|
||||||
|
|
||||||
// ---------------------------
|
// ---------------------------
|
||||||
// USER REQUESTS
|
// USER REQUESTS
|
||||||
@@ -149,7 +144,6 @@ Route::prefix('admin')
|
|||||||
[UserRequestController::class, 'rejectProfileUpdate']
|
[UserRequestController::class, 'rejectProfileUpdate']
|
||||||
)->name('admin.profile.reject');
|
)->name('admin.profile.reject');
|
||||||
|
|
||||||
|
|
||||||
// ---------------------------
|
// ---------------------------
|
||||||
// MARK LIST
|
// MARK LIST
|
||||||
// ---------------------------
|
// ---------------------------
|
||||||
@@ -159,19 +153,15 @@ Route::prefix('admin')
|
|||||||
Route::get('/mark-list/status/{id}', [AdminMarkListController::class, 'toggleStatus'])
|
Route::get('/mark-list/status/{id}', [AdminMarkListController::class, 'toggleStatus'])
|
||||||
->name('admin.marklist.toggle');
|
->name('admin.marklist.toggle');
|
||||||
|
|
||||||
|
|
||||||
// ---------------------------
|
// ---------------------------
|
||||||
// ORDERS (UPDATED)
|
// ORDERS (UPDATED)
|
||||||
// ---------------------------
|
// ---------------------------
|
||||||
// मुख्य Orders पेज (invoice + container listing)
|
|
||||||
Route::get('/orders', [AdminOrderController::class, 'index'])
|
Route::get('/orders', [AdminOrderController::class, 'index'])
|
||||||
->name('admin.orders');
|
->name('admin.orders');
|
||||||
|
|
||||||
// जुनं list route (असल्या वापरासाठी पण index ला point)
|
|
||||||
Route::get('/orders/list', [AdminOrderController::class, 'index'])
|
Route::get('/orders/list', [AdminOrderController::class, 'index'])
|
||||||
->name('admin.orders.index');
|
->name('admin.orders.index');
|
||||||
|
|
||||||
// Order show (old single order view)
|
|
||||||
Route::get('/orders/{id}', [AdminOrderController::class, 'show'])
|
Route::get('/orders/{id}', [AdminOrderController::class, 'show'])
|
||||||
->name('admin.orders.show');
|
->name('admin.orders.show');
|
||||||
|
|
||||||
@@ -193,7 +183,6 @@ Route::prefix('admin')
|
|||||||
Route::get('/orders/{id}/see', [AdminOrderController::class, 'see'])
|
Route::get('/orders/{id}/see', [AdminOrderController::class, 'see'])
|
||||||
->name('orders.see');
|
->name('orders.see');
|
||||||
|
|
||||||
|
|
||||||
// ---------------------------
|
// ---------------------------
|
||||||
// ORDERS (FIXED ROUTES)
|
// ORDERS (FIXED ROUTES)
|
||||||
// ---------------------------
|
// ---------------------------
|
||||||
@@ -212,7 +201,6 @@ Route::prefix('admin')
|
|||||||
Route::delete('/orders/{id}/delete', [AdminOrderController::class, 'destroy'])
|
Route::delete('/orders/{id}/delete', [AdminOrderController::class, 'destroy'])
|
||||||
->name('admin.orders.destroy');
|
->name('admin.orders.destroy');
|
||||||
|
|
||||||
|
|
||||||
// ---------------------------
|
// ---------------------------
|
||||||
// SHIPMENTS (FIXED ROUTES)
|
// SHIPMENTS (FIXED ROUTES)
|
||||||
// ---------------------------
|
// ---------------------------
|
||||||
@@ -240,7 +228,6 @@ Route::prefix('admin')
|
|||||||
Route::get('/shipment/dummy/{id}', [ShipmentController::class, 'dummy'])
|
Route::get('/shipment/dummy/{id}', [ShipmentController::class, 'dummy'])
|
||||||
->name('admin.shipments.dummy');
|
->name('admin.shipments.dummy');
|
||||||
|
|
||||||
|
|
||||||
// ---------------------------
|
// ---------------------------
|
||||||
// INVOICES
|
// INVOICES
|
||||||
// ---------------------------
|
// ---------------------------
|
||||||
@@ -271,9 +258,10 @@ Route::prefix('admin')
|
|||||||
Route::post('/admin/invoices/{invoice}/charge-group', [AdminInvoiceController::class, 'storeChargeGroup'])
|
Route::post('/admin/invoices/{invoice}/charge-group', [AdminInvoiceController::class, 'storeChargeGroup'])
|
||||||
->name('admin.invoices.charge-group.store');
|
->name('admin.invoices.charge-group.store');
|
||||||
|
|
||||||
Route::get('/admin/invoices/create', [InvoiceController::class, 'create'])
|
// जर create page वापरायचा असेल तर AdminInvoiceController मध्ये create() असावा.
|
||||||
->name('admin.invoices.create');
|
// नसेल तर हा route comment / delete कर.
|
||||||
|
// Route::get('/admin/invoices/create', [AdminInvoiceController::class, 'create'])
|
||||||
|
// ->name('admin.invoices.create');
|
||||||
|
|
||||||
// ---------------------------
|
// ---------------------------
|
||||||
// CUSTOMERS
|
// CUSTOMERS
|
||||||
@@ -293,7 +281,6 @@ Route::prefix('admin')
|
|||||||
Route::post('/customers/{id}/status', [AdminCustomerController::class, 'toggleStatus'])
|
Route::post('/customers/{id}/status', [AdminCustomerController::class, 'toggleStatus'])
|
||||||
->name('admin.customers.status');
|
->name('admin.customers.status');
|
||||||
|
|
||||||
|
|
||||||
// CHAT
|
// CHAT
|
||||||
Route::get('/chat-support', [AdminChatController::class, 'index'])
|
Route::get('/chat-support', [AdminChatController::class, 'index'])
|
||||||
->name('admin.chat_support');
|
->name('admin.chat_support');
|
||||||
@@ -305,7 +292,6 @@ Route::prefix('admin')
|
|||||||
->name('admin.chat.send');
|
->name('admin.chat.send');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
// ==========================================
|
// ==========================================
|
||||||
// ADMIN ACCOUNT (AJAX) ROUTES
|
// ADMIN ACCOUNT (AJAX) ROUTES
|
||||||
// ==========================================
|
// ==========================================
|
||||||
@@ -351,7 +337,6 @@ Route::prefix('admin/account')
|
|||||||
->name('remove.order.from.entry');
|
->name('remove.order.from.entry');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
// ---------------------------
|
// ---------------------------
|
||||||
// REPORTS DOWNLOAD ROUTES
|
// REPORTS DOWNLOAD ROUTES
|
||||||
// ---------------------------
|
// ---------------------------
|
||||||
@@ -373,7 +358,7 @@ Route::middleware(['auth:admin'])
|
|||||||
Route::resource('staff', AdminStaffController::class);
|
Route::resource('staff', AdminStaffController::class);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Extra admin prefix group (तसाच ठेवला)
|
// Extra admin prefix group
|
||||||
Route::prefix('admin')->middleware('auth:admin')->group(function () {
|
Route::prefix('admin')->middleware('auth:admin')->group(function () {
|
||||||
// ... your routes
|
// ... your routes
|
||||||
});
|
});
|
||||||
@@ -382,6 +367,14 @@ Route::post('/admin/broadcasting/auth', function () {
|
|||||||
return Broadcast::auth(request());
|
return Broadcast::auth(request());
|
||||||
})->middleware('auth:admin');
|
})->middleware('auth:admin');
|
||||||
|
|
||||||
Route::get('/admin/invoices/{invoice}/download', [InvoiceController::class, 'download'])
|
// INVOICE DOWNLOAD (AdminInvoiceController वापरून)
|
||||||
|
Route::get('/admin/invoices/{invoice}/download', [AdminInvoiceController::class, 'download'])
|
||||||
->name('admin.invoices.download');
|
->name('admin.invoices.download');
|
||||||
|
|
||||||
|
|
||||||
|
// CONTAINER POPUP VIEW
|
||||||
|
// In admin group
|
||||||
|
Route::get('/admin/containers/{container}/popup', [\App\Http\Controllers\ContainerController::class, 'popupPopup'])
|
||||||
|
->name('containers.popup');
|
||||||
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user