UI Update Customer Section
This commit is contained in:
134
app/Http/Controllers/Admin/AdminCustomerController.php
Normal file
134
app/Http/Controllers/Admin/AdminCustomerController.php
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Admin;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Support\Facades\Hash;
|
||||||
|
|
||||||
|
class AdminCustomerController extends Controller
|
||||||
|
{
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
// LIST CUSTOMERS (with search + status filter)
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
public function index(Request $request)
|
||||||
|
{
|
||||||
|
$search = $request->search;
|
||||||
|
$status = $request->status;
|
||||||
|
|
||||||
|
$query = User::with(['marks', 'orders'])->orderBy('id', 'desc');
|
||||||
|
|
||||||
|
// SEARCH FILTER
|
||||||
|
if (!empty($search)) {
|
||||||
|
$query->where(function ($q) use ($search) {
|
||||||
|
$q->where('customer_name', 'like', "%$search%")
|
||||||
|
->orWhere('email', 'like', "%$search%")
|
||||||
|
->orWhere('mobile_no', 'like', "%$search%")
|
||||||
|
->orWhere('customer_id', 'like', "%$search%");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// STATUS FILTER
|
||||||
|
if (!empty($status) && in_array($status, ['active', 'inactive'])) {
|
||||||
|
$query->where('status', $status);
|
||||||
|
}
|
||||||
|
|
||||||
|
$customers = $query->get();
|
||||||
|
|
||||||
|
return view('admin.customers', compact('customers', 'search', 'status'));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
// SHOW ADD CUSTOMER FORM
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
public function create()
|
||||||
|
{
|
||||||
|
return view('admin.customers_add');
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
// STORE NEW CUSTOMER
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
public function store(Request $request)
|
||||||
|
{
|
||||||
|
$request->validate([
|
||||||
|
'customer_name' => 'required|string|max:255',
|
||||||
|
'company_name' => 'nullable|string|max:255',
|
||||||
|
'designation' => 'nullable|string|max:255',
|
||||||
|
'email' => 'required|email|unique:users,email',
|
||||||
|
'mobile_no' => 'required|string|max:20',
|
||||||
|
'address' => 'nullable|string',
|
||||||
|
'pincode' => 'nullable|string|max:10',
|
||||||
|
'customer_type' => 'required|in:regular,premium',
|
||||||
|
'status' => 'required|in:active,inactive',
|
||||||
|
]);
|
||||||
|
|
||||||
|
// AUTO GENERATE CUSTOMER ID
|
||||||
|
$year = date('Y');
|
||||||
|
$prefix = "CID-$year-";
|
||||||
|
|
||||||
|
$lastCustomer = User::whereYear('created_at', $year)
|
||||||
|
->orderBy('id', 'DESC')
|
||||||
|
->first();
|
||||||
|
|
||||||
|
$next = $lastCustomer ? intval(substr($lastCustomer->customer_id, -6)) + 1 : 1;
|
||||||
|
$customerId = $prefix . str_pad($next, 6, '0', STR_PAD_LEFT);
|
||||||
|
|
||||||
|
// CREATE CUSTOMER
|
||||||
|
User::create([
|
||||||
|
'customer_id' => $customerId,
|
||||||
|
'customer_name' => $request->customer_name,
|
||||||
|
'company_name' => $request->company_name,
|
||||||
|
'designation' => $request->designation,
|
||||||
|
'email' => $request->email,
|
||||||
|
'mobile_no' => $request->mobile_no,
|
||||||
|
'address' => $request->address,
|
||||||
|
'pincode' => $request->pincode,
|
||||||
|
'date' => date('Y-m-d'),
|
||||||
|
'customer_type' => $request->customer_type,
|
||||||
|
'status' => $request->status,
|
||||||
|
'password' => Hash::make('123456'), // DEFAULT PASSWORD
|
||||||
|
]);
|
||||||
|
|
||||||
|
return redirect()
|
||||||
|
->route('admin.customers.index')
|
||||||
|
->with('success', 'Customer added successfully!');
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
// VIEW CUSTOMER FULL DETAILS
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
public function view($id)
|
||||||
|
{
|
||||||
|
$customer = User::with(['marks', 'orders'])->findOrFail($id);
|
||||||
|
|
||||||
|
$totalOrders = $customer->orders->count();
|
||||||
|
$totalAmount = $customer->orders->sum('ttl_amount');
|
||||||
|
$recentOrders = $customer->orders()->latest()->take(5)->get();
|
||||||
|
|
||||||
|
return view('admin.customers_view', compact(
|
||||||
|
'customer',
|
||||||
|
'totalOrders',
|
||||||
|
'totalAmount',
|
||||||
|
'recentOrders'
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
// TOGGLE STATUS ACTIVE / INACTIVE
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
public function toggleStatus($id)
|
||||||
|
{
|
||||||
|
$customer = User::findOrFail($id);
|
||||||
|
|
||||||
|
$customer->status = $customer->status === 'active'
|
||||||
|
? 'inactive'
|
||||||
|
: 'active';
|
||||||
|
|
||||||
|
$customer->save();
|
||||||
|
|
||||||
|
return back()->with('success', 'Customer status updated.');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -15,7 +15,7 @@ class User extends Authenticatable implements JWTSubject
|
|||||||
* The attributes that are mass assignable.
|
* The attributes that are mass assignable.
|
||||||
*/
|
*/
|
||||||
protected $fillable = [
|
protected $fillable = [
|
||||||
'customer_id', // CID-2025-000001 format
|
'customer_id',
|
||||||
'customer_name',
|
'customer_name',
|
||||||
'company_name',
|
'company_name',
|
||||||
'designation',
|
'designation',
|
||||||
@@ -25,10 +25,15 @@ class User extends Authenticatable implements JWTSubject
|
|||||||
'pincode',
|
'pincode',
|
||||||
'date',
|
'date',
|
||||||
'password',
|
'password',
|
||||||
|
|
||||||
|
// newly added customer fields
|
||||||
|
'status', // active / inactive
|
||||||
|
'customer_type', // premium / regular
|
||||||
|
'profile_image', // optional image
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The attributes that should be hidden for arrays.
|
* Attributes that should be hidden.
|
||||||
*/
|
*/
|
||||||
protected $hidden = [
|
protected $hidden = [
|
||||||
'password',
|
'password',
|
||||||
@@ -36,7 +41,7 @@ class User extends Authenticatable implements JWTSubject
|
|||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The attributes that should be cast.
|
* Attribute casting.
|
||||||
*/
|
*/
|
||||||
protected function casts(): array
|
protected function casts(): array
|
||||||
{
|
{
|
||||||
@@ -47,7 +52,30 @@ class User extends Authenticatable implements JWTSubject
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* JWT Identifier.
|
* Relationship: User → MarkList (Many)
|
||||||
|
*/
|
||||||
|
public function marks()
|
||||||
|
{
|
||||||
|
return $this->hasMany(\App\Models\MarkList::class, 'customer_id', 'customer_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Relationship: User → Orders (Through MarkList)
|
||||||
|
*/
|
||||||
|
public function orders()
|
||||||
|
{
|
||||||
|
return $this->hasManyThrough(
|
||||||
|
\App\Models\Order::class,
|
||||||
|
\App\Models\MarkList::class,
|
||||||
|
'customer_id', // MarkList.customer_id
|
||||||
|
'mark_no', // Orders.mark_no
|
||||||
|
'customer_id', // Users.customer_id
|
||||||
|
'mark_no' // MarkList.mark_no
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JWT Identifier
|
||||||
*/
|
*/
|
||||||
public function getJWTIdentifier()
|
public function getJWTIdentifier()
|
||||||
{
|
{
|
||||||
@@ -55,7 +83,7 @@ class User extends Authenticatable implements JWTSubject
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* JWT Custom Claims.
|
* JWT Custom Claims
|
||||||
*/
|
*/
|
||||||
public function getJWTCustomClaims()
|
public function getJWTCustomClaims()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::table('users', function (Blueprint $table) {
|
||||||
|
|
||||||
|
// status: active / inactive
|
||||||
|
$table->enum('status', ['active', 'inactive'])->default('active')->after('pincode');
|
||||||
|
|
||||||
|
// premium / regular
|
||||||
|
$table->enum('customer_type', ['regular', 'premium'])->default('regular')->after('status');
|
||||||
|
|
||||||
|
// optional: profile image path
|
||||||
|
$table->string('profile_image')->nullable()->after('customer_type');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Schema::table('users', function (Blueprint $table) {
|
||||||
|
$table->dropColumn(['status', 'customer_type', 'profile_image']);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
BIN
public/invoices/invoice-INV-2025-000005.pdf
Normal file
BIN
public/invoices/invoice-INV-2025-000005.pdf
Normal file
Binary file not shown.
BIN
public/invoices/invoice-INV-2025-000006.pdf
Normal file
BIN
public/invoices/invoice-INV-2025-000006.pdf
Normal file
Binary file not shown.
BIN
public/invoices/invoice-INV-2025-000007.pdf
Normal file
BIN
public/invoices/invoice-INV-2025-000007.pdf
Normal file
Binary file not shown.
BIN
public/invoices/invoice-INV-2025-000008.pdf
Normal file
BIN
public/invoices/invoice-INV-2025-000008.pdf
Normal file
Binary file not shown.
@@ -1,12 +1,420 @@
|
|||||||
@extends('admin.layouts.app')
|
@extends('admin.layouts.app')
|
||||||
|
|
||||||
@section('page-title', 'Dashboard')
|
@section('page-title', 'Customers')
|
||||||
|
|
||||||
@section('content')
|
@section('content')
|
||||||
<div class="card shadow-sm">
|
|
||||||
<div class="card-body">
|
<style>
|
||||||
<h4>Welcome to the Admin customer page</h4>
|
.glass-card {
|
||||||
<p>Here you can manage all system modules.</p>
|
background: rgba(255, 255, 255, 0.95);
|
||||||
|
backdrop-filter: blur(20px);
|
||||||
|
border-radius: 15px;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||||
|
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-card {
|
||||||
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 15px;
|
||||||
|
color: white;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
transition: transform 0.3s ease;
|
||||||
|
min-height: 80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-card:hover {
|
||||||
|
transform: translateY(-3px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-count {
|
||||||
|
font-size: 1.4rem;
|
||||||
|
font-weight: 700;
|
||||||
|
margin-bottom: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-label {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
opacity: 0.9;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-icon {
|
||||||
|
position: absolute;
|
||||||
|
top: 12px;
|
||||||
|
right: 12px;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-container {
|
||||||
|
background: rgba(255, 255, 255, 0.9);
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 8px 15px;
|
||||||
|
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-input {
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
padding: 5px 10px;
|
||||||
|
width: 100%;
|
||||||
|
font-size: 14px;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-btn {
|
||||||
|
background: rgba(102, 126, 234, 0.1);
|
||||||
|
border: 2px solid transparent;
|
||||||
|
color: #667eea;
|
||||||
|
padding: 6px 15px;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-weight: 600;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
margin: 0 5px;
|
||||||
|
text-decoration: none;
|
||||||
|
display: inline-block;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-btn.active {
|
||||||
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
|
color: white;
|
||||||
|
border-color: #667eea;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-customer-btn {
|
||||||
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
|
border: none;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 8px 20px;
|
||||||
|
color: white;
|
||||||
|
font-weight: 600;
|
||||||
|
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4);
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
text-decoration: none;
|
||||||
|
display: inline-block;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-glass {
|
||||||
|
background: rgba(255, 255, 255, 0.9);
|
||||||
|
border-radius: 10px;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.05);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-header {
|
||||||
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
|
||||||
|
color: white !important;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
padding: 12px 15px !important;
|
||||||
|
position: relative;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Curved borders for table headers */
|
||||||
|
.table-header:first-child {
|
||||||
|
border-top-left-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-header:last-child {
|
||||||
|
border-top-right-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table > :not(caption) > * > * {
|
||||||
|
padding: 12px 15px;
|
||||||
|
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.customer-avatar {
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: white;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
box-shadow: 0 2px 8px rgba(102, 126, 234, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.premium-badge {
|
||||||
|
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
|
||||||
|
color: white;
|
||||||
|
padding: 3px 8px;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-size: 0.7rem;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.regular-badge {
|
||||||
|
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
|
||||||
|
color: white;
|
||||||
|
padding: 3px 8px;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-size: 0.7rem;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-badge {
|
||||||
|
padding: 4px 10px;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.active-status {
|
||||||
|
background: linear-gradient(135deg, #4cd964 0%, #5ac8fa 100%);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inactive-status {
|
||||||
|
background: linear-gradient(135deg, #ff6b6b 0%, #ee5a24 100%);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-btn {
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
border-radius: 6px;
|
||||||
|
border: none;
|
||||||
|
background: rgba(102, 126, 234, 0.1);
|
||||||
|
color: #667eea;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
margin: 0 2px;
|
||||||
|
text-decoration: none;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-btn:hover {
|
||||||
|
background: #667eea;
|
||||||
|
color: white;
|
||||||
|
transform: scale(1.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-row {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
||||||
|
gap: 12px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.customer-info-column {
|
||||||
|
min-width: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table tbody tr {
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table tbody tr:hover {
|
||||||
|
background: rgba(102, 126, 234, 0.03);
|
||||||
|
transform: translateX(5px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.customer-details {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
color: #6c757d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.customer-stats {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
color: #6c757d;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Enhanced table styling */
|
||||||
|
.table-container {
|
||||||
|
border-radius: 10px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-header-container {
|
||||||
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
|
border-radius: 10px 10px 0 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div class="container-fluid">
|
||||||
|
<!-- Header -->
|
||||||
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||||
|
<h4 style="color: #2c3e50; font-weight: 700;">Customer List</h4>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Stats Cards -->
|
||||||
|
<div class="stats-row">
|
||||||
|
<div class="stats-card">
|
||||||
|
<div class="stats-count">{{ $totalCustomers ?? '1,247' }}</div>
|
||||||
|
<div class="stats-label">Total Customers</div>
|
||||||
|
<i class="bi bi-people-fill stats-icon"></i>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="stats-card" style="background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);">
|
||||||
|
<div class="stats-count">{{ $newThisMonth ?? '342' }}</div>
|
||||||
|
<div class="stats-label">New This Month</div>
|
||||||
|
<i class="bi bi-person-plus stats-icon"></i>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="stats-card" style="background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);">
|
||||||
|
<div class="stats-count">{{ $activeThisMonth ?? '123' }}</div>
|
||||||
|
<div class="stats-label">Active This Month</div>
|
||||||
|
<i class="bi bi-activity stats-icon"></i>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="stats-card" style="background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%);">
|
||||||
|
<div class="stats-count">{{ $premiumCount ?? '23' }}</div>
|
||||||
|
<div class="stats-label">Premium Customers</div>
|
||||||
|
<i class="bi bi-award-fill stats-icon"></i>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Search and Filter Section -->
|
||||||
|
<div class="glass-card p-3 mb-3">
|
||||||
|
<div class="row align-items-center">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<form method="GET" action="{{ route('admin.customers.index') }}" class="d-flex align-items-center">
|
||||||
|
<div class="search-container">
|
||||||
|
<i class="bi bi-search text-muted me-2"></i>
|
||||||
|
<input type="text"
|
||||||
|
name="search"
|
||||||
|
value="{{ $search ?? '' }}"
|
||||||
|
class="search-input"
|
||||||
|
placeholder="Search Customer Name">
|
||||||
|
@if(!empty($status))
|
||||||
|
<input type="hidden" name="status" value="{{ $status }}">
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6 text-end">
|
||||||
|
<a href="{{ route('admin.customers.index', ['status'=>'active', 'search'=>$search ?? '']) }}"
|
||||||
|
class="filter-btn {{ ($status ?? '') == 'active' ? 'active' : '' }}">
|
||||||
|
Active
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a href="{{ route('admin.customers.index', ['status'=>'inactive', 'search'=>$search ?? '']) }}"
|
||||||
|
class="filter-btn {{ ($status ?? '') == 'inactive' ? 'active' : '' }}">
|
||||||
|
Inactive
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a href="{{ route('admin.customers.index') }}"
|
||||||
|
class="filter-btn {{ empty($status) ? 'active' : '' }}">
|
||||||
|
All
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a href="{{ route('admin.customers.add') }}" class="add-customer-btn ms-2">
|
||||||
|
<i class="bi bi-plus-circle me-1"></i>Add Customer
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Customer List Table -->
|
||||||
|
<div class="glass-card">
|
||||||
|
<div class="p-3 border-bottom">
|
||||||
|
<h5 class="mb-0" style="color: #2c3e50; font-weight: 600;">Customer List</h5>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="table-container">
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-hover align-middle mb-0">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="table-header">Customer Info</th>
|
||||||
|
<th class="table-header">Customer ID</th>
|
||||||
|
<th class="table-header">Create Date</th>
|
||||||
|
<th class="table-header">Status</th>
|
||||||
|
<th class="table-header" width="120">Actions</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@forelse($customers as $c)
|
||||||
|
<tr>
|
||||||
|
<!-- Customer Info Column -->
|
||||||
|
<td class="customer-info-column">
|
||||||
|
<div class="d-flex align-items-start">
|
||||||
|
<div class="customer-avatar me-3">
|
||||||
|
{{ strtoupper(substr($c->customer_name,0,1)) }}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="fw-bold">{{ $c->customer_name }}</div>
|
||||||
|
@if($c->customer_type == 'premium')
|
||||||
|
<span class="premium-badge">Premium Customer</span>
|
||||||
|
@else
|
||||||
|
<span class="regular-badge">Regular Customer</span>
|
||||||
|
@endif
|
||||||
|
<div class="customer-details mt-1">
|
||||||
|
{{ $c->email }}<br>
|
||||||
|
{{ $c->mobile_no }}
|
||||||
|
</div>
|
||||||
|
<div class="customer-stats mt-1">
|
||||||
|
{{ $c->orders->count() }} orders • ₹{{ number_format($c->orders->sum('ttl_amount'), 2) }} total
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<!-- Customer ID -->
|
||||||
|
<td>
|
||||||
|
<span class="fw-bold text-primary">{{ $c->customer_id }}</span>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<!-- Create Date -->
|
||||||
|
<td>
|
||||||
|
<span class="text-muted">{{ $c->created_at ? $c->created_at->format('d-m-Y') : '-' }}</span>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<!-- Status -->
|
||||||
|
<td>
|
||||||
|
@if($c->status === 'active')
|
||||||
|
<span class="status-badge active-status">Active</span>
|
||||||
|
@else
|
||||||
|
<span class="status-badge inactive-status">Inactive</span>
|
||||||
|
@endif
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<!-- Actions -->
|
||||||
|
<td>
|
||||||
|
<div class="d-flex">
|
||||||
|
<a href="{{ route('admin.customers.view', $c->id) }}"
|
||||||
|
class="action-btn" title="View">
|
||||||
|
<i class="bi bi-eye"></i>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<form action="{{ route('admin.customers.status', $c->id) }}"
|
||||||
|
method="POST" style="display:inline-block;">
|
||||||
|
@csrf
|
||||||
|
<button class="action-btn" title="Toggle Status" type="submit">
|
||||||
|
<i class="bi bi-power"></i>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
@empty
|
||||||
|
<tr>
|
||||||
|
<td colspan="5" class="text-center py-4">
|
||||||
|
<i class="bi bi-people display-4 text-muted d-block mb-2"></i>
|
||||||
|
<span class="text-muted">No customers found.</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
@endforelse
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
@endsection
|
@endsection
|
||||||
542
resources/views/admin/customers_add.blade.php
Normal file
542
resources/views/admin/customers_add.blade.php
Normal file
@@ -0,0 +1,542 @@
|
|||||||
|
@extends('admin.layouts.app')
|
||||||
|
|
||||||
|
@section('page-title', 'Add Customer')
|
||||||
|
|
||||||
|
@section('content')
|
||||||
|
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
--primary-gradient: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
|
--success-gradient: linear-gradient(135deg, #10b981 0%, #059669 100%);
|
||||||
|
--glass-bg: #ffffff;
|
||||||
|
--glass-border: rgba(255, 255, 255, 0.2);
|
||||||
|
--shadow-soft: 0 8px 32px rgba(0, 0, 0, 0.1);
|
||||||
|
--shadow-medium: 0 12px 48px rgba(0, 0, 0, 0.15);
|
||||||
|
--shadow-strong: 0 20px 60px rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Enhanced Card - No Blur - Original Width */
|
||||||
|
.card {
|
||||||
|
background: var(--glass-bg);
|
||||||
|
border-radius: 24px;
|
||||||
|
box-shadow: var(--shadow-strong);
|
||||||
|
border: 1px solid #e4e6ef;
|
||||||
|
animation: cardEntrance 0.8s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
/* Maintaining original width */
|
||||||
|
width: 100%;
|
||||||
|
max-width: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: linear-gradient(45deg,
|
||||||
|
rgba(102, 126, 234, 0.03) 0%,
|
||||||
|
rgba(118, 75, 162, 0.03) 50%,
|
||||||
|
rgba(16, 185, 129, 0.03) 100%);
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes cardEntrance {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(30px) scale(0.95);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0) scale(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Premium Card Header */
|
||||||
|
.card-header {
|
||||||
|
background: var(--primary-gradient);
|
||||||
|
color: white;
|
||||||
|
border-bottom: none;
|
||||||
|
padding: 25px 30px;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-header::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: -50%;
|
||||||
|
left: -50%;
|
||||||
|
width: 200%;
|
||||||
|
height: 200%;
|
||||||
|
background: linear-gradient(45deg,
|
||||||
|
transparent 0%,
|
||||||
|
rgba(255, 255, 255, 0.1) 50%,
|
||||||
|
transparent 100%);
|
||||||
|
animation: headerShimmer 6s infinite linear;
|
||||||
|
transform: rotate(45deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes headerShimmer {
|
||||||
|
0% { transform: translateX(-100%) rotate(45deg); }
|
||||||
|
100% { transform: translateX(100%) rotate(45deg); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-header h4 {
|
||||||
|
margin: 0;
|
||||||
|
font-weight: 800;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-header h4::before {
|
||||||
|
content: '✨';
|
||||||
|
font-size: 1.3rem;
|
||||||
|
filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.2));
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-body {
|
||||||
|
padding: 30px;
|
||||||
|
background: #f8fafc;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* World-Class Form Elements - No Blur */
|
||||||
|
.form-label {
|
||||||
|
font-weight: 700;
|
||||||
|
color: #1e293b;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-label::before {
|
||||||
|
content: '';
|
||||||
|
width: 3px;
|
||||||
|
height: 14px;
|
||||||
|
background: var(--primary-gradient);
|
||||||
|
border-radius: 2px;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-control, .form-select {
|
||||||
|
border: 2px solid #e2e8f0;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 12px 16px;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
background: #ffffff;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-control:focus, .form-select:focus {
|
||||||
|
border-color: #667eea;
|
||||||
|
box-shadow:
|
||||||
|
0 0 0 3px rgba(102, 126, 234, 0.15),
|
||||||
|
0 6px 20px rgba(102, 126, 234, 0.15),
|
||||||
|
inset 0 1px 0 rgba(255, 255, 255, 0.8);
|
||||||
|
background: #ffffff;
|
||||||
|
transform: translateY(-2px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-control:hover, .form-select:hover {
|
||||||
|
border-color: #cbd5e1;
|
||||||
|
transform: translateY(-1px);
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Enhanced Grid System - Original Layout */
|
||||||
|
.row.g-3 {
|
||||||
|
margin: -12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row.g-3 > [class*="col-"] {
|
||||||
|
padding: 12px;
|
||||||
|
animation: formElementEntrance 0.6s cubic-bezier(0.25, 0.46, 0.45, 0.94) both;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes formElementEntrance {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(20px);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Staggered Animation Delays */
|
||||||
|
.row.g-3 > [class*="col-"]:nth-child(1) { animation-delay: 0.1s; }
|
||||||
|
.row.g-3 > [class*="col-"]:nth-child(2) { animation-delay: 0.15s; }
|
||||||
|
.row.g-3 > [class*="col-"]:nth-child(3) { animation-delay: 0.2s; }
|
||||||
|
.row.g-3 > [class*="col-"]:nth-child(4) { animation-delay: 0.25s; }
|
||||||
|
.row.g-3 > [class*="col-"]:nth-child(5) { animation-delay: 0.3s; }
|
||||||
|
.row.g-3 > [class*="col-"]:nth-child(6) { animation-delay: 0.35s; }
|
||||||
|
|
||||||
|
/* Premium Textarea */
|
||||||
|
textarea.form-control {
|
||||||
|
resize: vertical;
|
||||||
|
min-height: 100px;
|
||||||
|
line-height: 1.5;
|
||||||
|
background: #ffffff;
|
||||||
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea.form-control:focus {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow:
|
||||||
|
0 0 0 3px rgba(102, 126, 234, 0.15),
|
||||||
|
0 8px 24px rgba(102, 126, 234, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* World-Class Button Design */
|
||||||
|
.btn-success {
|
||||||
|
background: var(--success-gradient);
|
||||||
|
border: none;
|
||||||
|
padding: 14px 35px;
|
||||||
|
border-radius: 12px;
|
||||||
|
color: white;
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 1rem;
|
||||||
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
box-shadow: var(--shadow-medium);
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-success::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: -100%;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: linear-gradient(90deg,
|
||||||
|
transparent,
|
||||||
|
rgba(255, 255, 255, 0.3),
|
||||||
|
transparent);
|
||||||
|
transition: left 0.5s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-success:hover::before {
|
||||||
|
left: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-success:hover {
|
||||||
|
transform: translateY(-3px);
|
||||||
|
box-shadow:
|
||||||
|
0 12px 30px rgba(16, 185, 129, 0.35),
|
||||||
|
0 0 0 1px rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-success:active {
|
||||||
|
transform: translateY(-1px);
|
||||||
|
transition: all 0.1s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Enhanced Select Styling */
|
||||||
|
.form-select {
|
||||||
|
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%23667eea' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M6 8l4 4 4-4'/%3e%3c/svg%3e");
|
||||||
|
background-position: right 16px center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: 16px;
|
||||||
|
padding-right: 45px;
|
||||||
|
cursor: pointer;
|
||||||
|
appearance: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Loading Animation */
|
||||||
|
.btn-success.loading {
|
||||||
|
pointer-events: none;
|
||||||
|
padding-right: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-success.loading::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
right: 16px;
|
||||||
|
top: 50%;
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
margin-top: -8px;
|
||||||
|
border: 2px solid transparent;
|
||||||
|
border-top: 2px solid white;
|
||||||
|
border-radius: 50%;
|
||||||
|
animation: spin 1s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes spin {
|
||||||
|
0% { transform: rotate(0deg); }
|
||||||
|
100% { transform: rotate(360deg); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Micro-interactions for Enhanced UX */
|
||||||
|
.form-control:valid {
|
||||||
|
border-left: 2px solid #10b981;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-control:invalid:not(:focus):not(:placeholder-shown) {
|
||||||
|
border-left: 2px solid #ef4444;
|
||||||
|
animation: shake 0.4s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes shake {
|
||||||
|
0%, 100% { transform: translateX(0); }
|
||||||
|
25% { transform: translateX(-3px); }
|
||||||
|
75% { transform: translateX(3px); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Secondary Button */
|
||||||
|
.btn-secondary {
|
||||||
|
background: #64748b;
|
||||||
|
border: none;
|
||||||
|
padding: 14px 30px;
|
||||||
|
border-radius: 12px;
|
||||||
|
color: white;
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 1rem;
|
||||||
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
box-shadow: var(--shadow-soft);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-secondary:hover {
|
||||||
|
background: #475569;
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: var(--shadow-medium);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Required Field Indicator */
|
||||||
|
.required-field::after {
|
||||||
|
content: '*';
|
||||||
|
color: #ef4444;
|
||||||
|
margin-left: 4px;
|
||||||
|
font-weight: 800;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Success Animation */
|
||||||
|
@keyframes successPulse {
|
||||||
|
0%, 100% { transform: scale(1); }
|
||||||
|
50% { transform: scale(1.02); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.success-animation {
|
||||||
|
animation: successPulse 0.5s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Input Hints */
|
||||||
|
.input-hint {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
color: #64748b;
|
||||||
|
margin-top: 4px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-hint::before {
|
||||||
|
content: '💡';
|
||||||
|
font-size: 0.7rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Performance optimized animations */
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
* {
|
||||||
|
animation-duration: 0.01ms !important;
|
||||||
|
animation-iteration-count: 1 !important;
|
||||||
|
transition-duration: 0.01ms !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div class="container py-4">
|
||||||
|
<!-- Maintaining original container structure -->
|
||||||
|
<div class="card shadow-sm" style="border: none; background: transparent; box-shadow: none !important;">
|
||||||
|
<div class="card" style="margin: 0;">
|
||||||
|
<!-- Premium Card Header -->
|
||||||
|
<div class="card-header">
|
||||||
|
<h4>
|
||||||
|
<i class="bi bi-person-plus-fill me-2"></i>
|
||||||
|
Add New Customer
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Card Body - Original Structure -->
|
||||||
|
<div class="card-body">
|
||||||
|
<form action="{{ route('admin.customers.store') }}" method="POST" id="customerForm">
|
||||||
|
@csrf
|
||||||
|
|
||||||
|
<div class="row g-3">
|
||||||
|
<!-- Customer Name -->
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label class="form-label required-field">Customer Name</label>
|
||||||
|
<input type="text"
|
||||||
|
name="customer_name"
|
||||||
|
class="form-control"
|
||||||
|
placeholder="Enter full name"
|
||||||
|
required
|
||||||
|
pattern="[A-Za-z\s]{2,}">
|
||||||
|
<div class="input-hint">Minimum 2 characters, letters only</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Company Name -->
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label class="form-label">Company Name</label>
|
||||||
|
<input type="text"
|
||||||
|
name="company_name"
|
||||||
|
class="form-control"
|
||||||
|
placeholder="Enter company name">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Designation -->
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label class="form-label">Designation</label>
|
||||||
|
<input type="text"
|
||||||
|
name="designation"
|
||||||
|
class="form-control"
|
||||||
|
placeholder="Enter job title">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Email -->
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label class="form-label required-field">Email Address</label>
|
||||||
|
<input type="email"
|
||||||
|
name="email"
|
||||||
|
class="form-control"
|
||||||
|
placeholder="Enter email address"
|
||||||
|
required
|
||||||
|
pattern="[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}$">
|
||||||
|
<div class="input-hint">Valid email format required</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Mobile -->
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label class="form-label required-field">Mobile Number</label>
|
||||||
|
<input type="tel"
|
||||||
|
name="mobile_no"
|
||||||
|
class="form-control"
|
||||||
|
placeholder="Enter mobile number"
|
||||||
|
required
|
||||||
|
pattern="[0-9]{10}">
|
||||||
|
<div class="input-hint">10 digits without spaces</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Pincode -->
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label class="form-label">Pincode</label>
|
||||||
|
<input type="text"
|
||||||
|
name="pincode"
|
||||||
|
class="form-control"
|
||||||
|
placeholder="Enter pincode"
|
||||||
|
pattern="[0-9]{6}">
|
||||||
|
<div class="input-hint">6-digit pincode</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Address -->
|
||||||
|
<div class="col-12">
|
||||||
|
<label class="form-label">Address</label>
|
||||||
|
<textarea name="address"
|
||||||
|
class="form-control"
|
||||||
|
rows="3"
|
||||||
|
placeholder="Enter complete address"></textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Customer Type -->
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label class="form-label required-field">Customer Type</label>
|
||||||
|
<select name="customer_type" class="form-select" required>
|
||||||
|
<option value="regular">Regular</option>
|
||||||
|
<option value="premium">Premium</option>
|
||||||
|
</select>
|
||||||
|
<div class="input-hint">Premium customers get special benefits</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Status -->
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label class="form-label required-field">Status</label>
|
||||||
|
<select name="status" class="form-select" required>
|
||||||
|
<option value="active">Active</option>
|
||||||
|
<option value="inactive">Inactive</option>
|
||||||
|
</select>
|
||||||
|
<div class="input-hint">Active customers can place orders</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Form Actions - Original Layout -->
|
||||||
|
<div class="text-end mt-4">
|
||||||
|
<a href="{{ route('admin.customers.index') }}" class="btn btn-secondary me-3">
|
||||||
|
<i class="bi bi-arrow-left me-2"></i>
|
||||||
|
Cancel
|
||||||
|
</a>
|
||||||
|
<button type="submit" class="btn-success" id="submitBtn">
|
||||||
|
<i class="bi bi-person-plus me-2"></i>
|
||||||
|
Create Customer
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
const form = document.getElementById('customerForm');
|
||||||
|
const submitBtn = document.getElementById('submitBtn');
|
||||||
|
|
||||||
|
form.addEventListener('submit', function(e) {
|
||||||
|
// Add loading state
|
||||||
|
submitBtn.classList.add('loading');
|
||||||
|
submitBtn.innerHTML = '<i class="bi bi-hourglass-split me-2"></i>Creating...';
|
||||||
|
|
||||||
|
// Simulate form processing
|
||||||
|
setTimeout(() => {
|
||||||
|
submitBtn.classList.remove('loading');
|
||||||
|
submitBtn.classList.add('success-animation');
|
||||||
|
submitBtn.innerHTML = '<i class="bi bi-check-circle me-2"></i>Customer Created!';
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
submitBtn.classList.remove('success-animation');
|
||||||
|
submitBtn.innerHTML = '<i class="bi bi-person-plus me-2"></i>Create Customer';
|
||||||
|
}, 1500);
|
||||||
|
}, 1000);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Real-time validation
|
||||||
|
const inputs = form.querySelectorAll('input[required]');
|
||||||
|
inputs.forEach(input => {
|
||||||
|
input.addEventListener('blur', function() {
|
||||||
|
if (this.value && this.checkValidity()) {
|
||||||
|
this.style.borderLeft = '2px solid #10b981';
|
||||||
|
} else if (this.value && !this.checkValidity()) {
|
||||||
|
this.style.borderLeft = '2px solid #ef4444';
|
||||||
|
} else {
|
||||||
|
this.style.borderLeft = '';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Enhanced input interactions
|
||||||
|
const formControls = form.querySelectorAll('.form-control, .form-select');
|
||||||
|
formControls.forEach(control => {
|
||||||
|
control.addEventListener('focus', function() {
|
||||||
|
this.style.transform = 'translateY(-2px)';
|
||||||
|
});
|
||||||
|
|
||||||
|
control.addEventListener('blur', function() {
|
||||||
|
this.style.transform = 'translateY(0)';
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
@endsection
|
||||||
784
resources/views/admin/customers_view.blade.php
Normal file
784
resources/views/admin/customers_view.blade.php
Normal file
@@ -0,0 +1,784 @@
|
|||||||
|
@extends('admin.layouts.app')
|
||||||
|
|
||||||
|
@section('page-title', 'Customer Details')
|
||||||
|
|
||||||
|
@section('content')
|
||||||
|
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
--primary-gradient: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
|
--success-gradient: linear-gradient(135deg, #10b981 0%, #059669 100%);
|
||||||
|
--warning-gradient: linear-gradient(135deg, #f59e0b 0%, #d97706 100%);
|
||||||
|
--info-gradient: linear-gradient(135deg, #3b82f6 0%, #1d4ed8 100%);
|
||||||
|
--glass-bg: rgba(255, 255, 255, 0.95);
|
||||||
|
--shadow-strong: 0 20px 60px rgba(0, 0, 0, 0.15);
|
||||||
|
--shadow-medium: 0 12px 40px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Main Card Animations */
|
||||||
|
.customer-card {
|
||||||
|
background: var(--glass-bg);
|
||||||
|
border-radius: 24px;
|
||||||
|
box-shadow: var(--shadow-strong);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||||
|
animation: cardEntrance 0.8s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.customer-card::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: linear-gradient(45deg,
|
||||||
|
rgba(102, 126, 234, 0.03) 0%,
|
||||||
|
rgba(118, 75, 162, 0.03) 50%,
|
||||||
|
rgba(16, 185, 129, 0.03) 100%);
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes cardEntrance {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(30px) scale(0.95);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0) scale(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Header Section */
|
||||||
|
.page-header {
|
||||||
|
background: var(--primary-gradient);
|
||||||
|
border-radius: 20px;
|
||||||
|
padding: 30px;
|
||||||
|
color: white;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
animation: headerSlide 0.6s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes headerSlide {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateX(-30px);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-header::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: -50%;
|
||||||
|
left: -50%;
|
||||||
|
width: 200%;
|
||||||
|
height: 200%;
|
||||||
|
background: linear-gradient(45deg,
|
||||||
|
transparent 0%,
|
||||||
|
rgba(255, 255, 255, 0.1) 50%,
|
||||||
|
transparent 100%);
|
||||||
|
animation: headerShimmer 8s infinite linear;
|
||||||
|
transform: rotate(45deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes headerShimmer {
|
||||||
|
0% { transform: translateX(-100%) rotate(45deg); }
|
||||||
|
100% { transform: translateX(100%) rotate(45deg); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Customer Profile Section */
|
||||||
|
.profile-header {
|
||||||
|
padding: 40px 35px 30px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar-container {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.customer-avatar {
|
||||||
|
width: 100px;
|
||||||
|
height: 100px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: var(--primary-gradient);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: white;
|
||||||
|
font-weight: 800;
|
||||||
|
font-size: 2.5rem;
|
||||||
|
box-shadow: 0 12px 30px rgba(102, 126, 234, 0.4);
|
||||||
|
border: 4px solid white;
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
animation: avatarPulse 2s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes avatarPulse {
|
||||||
|
0%, 100% { transform: scale(1); }
|
||||||
|
50% { transform: scale(1.05); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar-status {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 8px;
|
||||||
|
right: 8px;
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: 3px solid white;
|
||||||
|
z-index: 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-active { background: var(--success-gradient); }
|
||||||
|
.status-inactive { background: #ef4444; }
|
||||||
|
|
||||||
|
/* Info Cards */
|
||||||
|
.info-card {
|
||||||
|
background: white;
|
||||||
|
border-radius: 20px;
|
||||||
|
padding: 25px;
|
||||||
|
box-shadow: var(--shadow-medium);
|
||||||
|
border: 1px solid #f1f5f9;
|
||||||
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
height: 100%;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-card::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 5px;
|
||||||
|
height: 100%;
|
||||||
|
background: var(--primary-gradient);
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-card:hover {
|
||||||
|
transform: translateY(-5px);
|
||||||
|
box-shadow: 0 25px 50px rgba(0, 0, 0, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-card h6 {
|
||||||
|
color: #64748b;
|
||||||
|
font-weight: 700;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-card h6::before {
|
||||||
|
content: '';
|
||||||
|
width: 3px;
|
||||||
|
height: 12px;
|
||||||
|
background: var(--primary-gradient);
|
||||||
|
border-radius: 2px;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Stats Cards */
|
||||||
|
.stats-card {
|
||||||
|
background: white;
|
||||||
|
border-radius: 20px;
|
||||||
|
padding: 25px;
|
||||||
|
text-align: center;
|
||||||
|
box-shadow: var(--shadow-medium);
|
||||||
|
border: 1px solid #f1f5f9;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-card::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 4px;
|
||||||
|
background: var(--primary-gradient);
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-card:hover {
|
||||||
|
transform: translateY(-5px);
|
||||||
|
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.12);
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-card.orders { --card-color: #3b82f6; }
|
||||||
|
.stats-card.amount { --card-color: #10b981; }
|
||||||
|
.stats-card.marks { --card-color: #8b5cf6; }
|
||||||
|
|
||||||
|
.stats-icon {
|
||||||
|
width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
border-radius: 15px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin: 0 auto 15px;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
background: linear-gradient(135deg, var(--card-color), var(--card-color));
|
||||||
|
color: white;
|
||||||
|
box-shadow: 0 8px 20px rgba(59, 130, 246, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-card.orders .stats-icon { background: var(--info-gradient); }
|
||||||
|
.stats-card.amount .stats-icon { background: var(--success-gradient); }
|
||||||
|
.stats-card.marks .stats-icon { background: var(--primary-gradient); }
|
||||||
|
|
||||||
|
.stats-value {
|
||||||
|
font-size: 2.2rem;
|
||||||
|
font-weight: 800;
|
||||||
|
color: #1e293b;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-label {
|
||||||
|
color: #64748b;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mark Numbers List */
|
||||||
|
.marks-section {
|
||||||
|
background: white;
|
||||||
|
border-radius: 20px;
|
||||||
|
box-shadow: var(--shadow-medium);
|
||||||
|
border: 1px solid #f1f5f9;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-header {
|
||||||
|
background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);
|
||||||
|
padding: 25px 30px;
|
||||||
|
border-bottom: 1px solid #e2e8f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-header h5 {
|
||||||
|
margin: 0;
|
||||||
|
color: #1e293b;
|
||||||
|
font-weight: 800;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-header h5::before {
|
||||||
|
content: '';
|
||||||
|
width: 4px;
|
||||||
|
height: 20px;
|
||||||
|
background: var(--primary-gradient);
|
||||||
|
border-radius: 2px;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mark-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 20px 30px;
|
||||||
|
border-bottom: 1px solid #f1f5f9;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
animation: itemEntrance 0.5s ease-out both;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes itemEntrance {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateX(-20px);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mark-item:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mark-item:hover {
|
||||||
|
background: #f8fafc;
|
||||||
|
transform: translateX(5px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mark-badge {
|
||||||
|
background: var(--primary-gradient);
|
||||||
|
color: white;
|
||||||
|
padding: 8px 16px;
|
||||||
|
border-radius: 12px;
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
margin-right: 15px;
|
||||||
|
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mark-route {
|
||||||
|
color: #64748b;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Buttons - FIXED POSITIONING */
|
||||||
|
.btn-back {
|
||||||
|
background: rgba(255, 255, 255, 0.2);
|
||||||
|
border: 2px solid rgba(255, 255, 255, 0.3);
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 12px 25px;
|
||||||
|
color: white;
|
||||||
|
font-weight: 700;
|
||||||
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
text-decoration: none;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-back::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: -100%;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: linear-gradient(90deg,
|
||||||
|
transparent,
|
||||||
|
rgba(255, 255, 255, 0.2),
|
||||||
|
transparent);
|
||||||
|
transition: left 0.6s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-back:hover::before {
|
||||||
|
left: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-back:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.3);
|
||||||
|
border-color: rgba(255, 255, 255, 0.5);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
color: white;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-premium {
|
||||||
|
background: var(--primary-gradient);
|
||||||
|
border: none;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 12px 25px;
|
||||||
|
color: white;
|
||||||
|
font-weight: 700;
|
||||||
|
box-shadow: 0 8px 25px rgba(102, 126, 234, 0.4);
|
||||||
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-premium::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: -100%;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: linear-gradient(90deg,
|
||||||
|
transparent,
|
||||||
|
rgba(255, 255, 255, 0.2),
|
||||||
|
transparent);
|
||||||
|
transition: left 0.6s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-premium:hover::before {
|
||||||
|
left: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-premium:hover {
|
||||||
|
transform: translateY(-3px);
|
||||||
|
box-shadow: 0 12px 35px rgba(102, 126, 234, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-outline-secondary {
|
||||||
|
border: 2px solid #64748b;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 12px 25px;
|
||||||
|
color: #64748b;
|
||||||
|
font-weight: 700;
|
||||||
|
background: transparent;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
text-decoration: none;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-outline-secondary:hover {
|
||||||
|
border-color: #475569;
|
||||||
|
color: #475569;
|
||||||
|
background: rgba(100, 116, 139, 0.05);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Badges */
|
||||||
|
.type-badge {
|
||||||
|
padding: 8px 16px;
|
||||||
|
border-radius: 12px;
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-premium {
|
||||||
|
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
|
||||||
|
color: white;
|
||||||
|
box-shadow: 0 4px 15px rgba(240, 147, 251, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-regular {
|
||||||
|
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
|
||||||
|
color: white;
|
||||||
|
box-shadow: 0 4px 15px rgba(79, 172, 254, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-badge {
|
||||||
|
padding: 8px 16px;
|
||||||
|
border-radius: 12px;
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-active {
|
||||||
|
background: var(--success-gradient);
|
||||||
|
color: white;
|
||||||
|
box-shadow: 0 4px 15px rgba(16, 185, 129, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-inactive {
|
||||||
|
background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%);
|
||||||
|
color: white;
|
||||||
|
box-shadow: 0 4px 15px rgba(239, 68, 68, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Animations */
|
||||||
|
.animate-fade-in {
|
||||||
|
animation: fadeInUp 0.6s ease-out both;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadeInUp {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(20px);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.animation-delay-1 { animation-delay: 0.1s; }
|
||||||
|
.animation-delay-2 { animation-delay: 0.2s; }
|
||||||
|
.animation-delay-3 { animation-delay: 0.3s; }
|
||||||
|
.animation-delay-4 { animation-delay: 0.4s; }
|
||||||
|
|
||||||
|
/* Header Button Container - FIXED */
|
||||||
|
.header-actions {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.profile-header {
|
||||||
|
padding: 25px 20px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.customer-avatar {
|
||||||
|
width: 80px;
|
||||||
|
height: 80px;
|
||||||
|
font-size: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-header {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-card {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-value {
|
||||||
|
font-size: 1.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-actions {
|
||||||
|
justify-content: center;
|
||||||
|
margin-top: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-header .row {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 576px) {
|
||||||
|
.btn-back, .btn-outline-secondary {
|
||||||
|
padding: 10px 20px;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-actions {
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-actions a {
|
||||||
|
width: 100%;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div class="container py-4">
|
||||||
|
|
||||||
|
{{-- HEADER - FIXED BUTTON POSITION --}}
|
||||||
|
<div class="page-header animate-fade-in">
|
||||||
|
<div class="row align-items-center">
|
||||||
|
<div class="col-md-8">
|
||||||
|
<h1 class="fw-bold mb-2">Customer Details</h1>
|
||||||
|
<p class="mb-0 opacity-90">Complete customer information and analytics</p>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<div class="header-actions">
|
||||||
|
<a href="{{ route('admin.customers.index') }}" class="btn-back">
|
||||||
|
<i class="bi bi-arrow-left"></i>
|
||||||
|
Back to List
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{-- CUSTOMER PROFILE CARD --}}
|
||||||
|
<div class="customer-card mb-4">
|
||||||
|
<div class="profile-header">
|
||||||
|
<div class="row align-items-center">
|
||||||
|
<div class="col-auto">
|
||||||
|
<div class="avatar-container">
|
||||||
|
<div class="customer-avatar">
|
||||||
|
{{ strtoupper(substr($customer->customer_name,0,1)) }}
|
||||||
|
</div>
|
||||||
|
<div class="avatar-status {{ $customer->status == 'active' ? 'status-active' : 'status-inactive' }}"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<div class="d-flex align-items-center mb-2">
|
||||||
|
<h2 class="fw-bold mb-0 me-3">{{ $customer->customer_name }}</h2>
|
||||||
|
@if($customer->customer_type == 'premium')
|
||||||
|
<span class="type-badge badge-premium">
|
||||||
|
<i class="bi bi-award me-1"></i>Premium
|
||||||
|
</span>
|
||||||
|
@else
|
||||||
|
<span class="type-badge badge-regular">
|
||||||
|
<i class="bi bi-person me-1"></i>Regular
|
||||||
|
</span>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
<p class="text-muted mb-2">
|
||||||
|
<i class="bi bi-building me-2"></i>
|
||||||
|
{{ $customer->company_name ?? 'No company specified' }}
|
||||||
|
</p>
|
||||||
|
<div class="d-flex align-items-center gap-3">
|
||||||
|
<span class="status-badge {{ $customer->status == 'active' ? 'badge-active' : 'badge-inactive' }}">
|
||||||
|
<i class="bi bi-circle-fill me-1" style="font-size: 0.6rem;"></i>
|
||||||
|
{{ ucfirst($customer->status) }}
|
||||||
|
</span>
|
||||||
|
<span class="text-muted">
|
||||||
|
<i class="bi bi-calendar me-1"></i>
|
||||||
|
Joined {{ $customer->created_at ? $customer->created_at->format('M d, Y') : 'N/A' }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{-- CUSTOMER INFORMATION --}}
|
||||||
|
<div class="row g-4 p-4">
|
||||||
|
{{-- Contact Information --}}
|
||||||
|
<div class="col-md-6 animate-fade-in animation-delay-1">
|
||||||
|
<div class="info-card">
|
||||||
|
<h6><i class="bi bi-telephone me-2"></i>Contact Information</h6>
|
||||||
|
<div class="mb-3">
|
||||||
|
<div class="d-flex align-items-center mb-2">
|
||||||
|
<i class="bi bi-envelope text-primary me-2"></i>
|
||||||
|
<strong class="me-2">Email:</strong>
|
||||||
|
<span>{{ $customer->email }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex align-items-center mb-2">
|
||||||
|
<i class="bi bi-phone text-primary me-2"></i>
|
||||||
|
<strong class="me-2">Mobile:</strong>
|
||||||
|
<span>{{ $customer->mobile_no }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex align-items-center mb-2">
|
||||||
|
<i class="bi bi-geo-alt text-primary me-2"></i>
|
||||||
|
<strong class="me-2">Address:</strong>
|
||||||
|
<span>{{ $customer->address ?? 'N/A' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex align-items-center">
|
||||||
|
<i class="bi bi-pin-map text-primary me-2"></i>
|
||||||
|
<strong class="me-2">Pincode:</strong>
|
||||||
|
<span>{{ $customer->pincode ?? 'N/A' }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{-- Account Information --}}
|
||||||
|
<div class="col-md-6 animate-fade-in animation-delay-2">
|
||||||
|
<div class="info-card">
|
||||||
|
<h6><i class="bi bi-person-badge me-2"></i>Account Information</h6>
|
||||||
|
<div class="mb-3">
|
||||||
|
<div class="d-flex align-items-center mb-2">
|
||||||
|
<i class="bi bi-id-card text-primary me-2"></i>
|
||||||
|
<strong class="me-2">Customer ID:</strong>
|
||||||
|
<code class="bg-light px-2 py-1 rounded">{{ $customer->customer_id }}</code>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex align-items-center mb-2">
|
||||||
|
<i class="bi bi-calendar-check text-primary me-2"></i>
|
||||||
|
<strong class="me-2">Registered On:</strong>
|
||||||
|
<span>{{ $customer->created_at ? $customer->created_at->format('d M, Y') : '-' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex align-items-center">
|
||||||
|
<i class="bi bi-briefcase text-primary me-2"></i>
|
||||||
|
<strong class="me-2">Designation:</strong>
|
||||||
|
<span>{{ $customer->designation ?? 'N/A' }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{-- STATISTICS --}}
|
||||||
|
<div class="row g-4 mb-4">
|
||||||
|
{{-- Total Orders --}}
|
||||||
|
<div class="col-md-4 animate-fade-in animation-delay-1">
|
||||||
|
<div class="stats-card orders">
|
||||||
|
<div class="stats-icon">
|
||||||
|
<i class="bi bi-cart-check"></i>
|
||||||
|
</div>
|
||||||
|
<div class="stats-value">{{ $totalOrders }}</div>
|
||||||
|
<div class="stats-label">Total Orders</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{-- Total Amount --}}
|
||||||
|
<div class="col-md-4 animate-fade-in animation-delay-2">
|
||||||
|
<div class="stats-card amount">
|
||||||
|
<div class="stats-icon">
|
||||||
|
<i class="bi bi-currency-rupee"></i>
|
||||||
|
</div>
|
||||||
|
<div class="stats-value">₹{{ number_format($totalAmount, 2) }}</div>
|
||||||
|
<div class="stats-label">Total Amount Spent</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{-- Mark Count --}}
|
||||||
|
<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">{{ $customer->marks->count() }}</div>
|
||||||
|
<div class="stats-label">Mark Numbers</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{-- MARK NUMBERS SECTION --}}
|
||||||
|
<div class="marks-section animate-fade-in animation-delay-4">
|
||||||
|
<div class="section-header">
|
||||||
|
<h5>
|
||||||
|
<i class="bi bi-hash"></i>
|
||||||
|
Customer Mark Numbers
|
||||||
|
<span class="badge bg-primary ms-2">{{ $customer->marks->count() }}</span>
|
||||||
|
</h5>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="section-body">
|
||||||
|
@if($customer->marks->count() == 0)
|
||||||
|
<div class="text-center py-5">
|
||||||
|
<i class="bi bi-inbox display-4 text-muted mb-3"></i>
|
||||||
|
<p class="text-muted mb-0">No mark numbers found for this customer.</p>
|
||||||
|
</div>
|
||||||
|
@else
|
||||||
|
@foreach($customer->marks as $index => $mark)
|
||||||
|
<div class="mark-item" style="animation-delay: {{ $index * 0.1 }}s">
|
||||||
|
<div class="mark-badge">
|
||||||
|
<i class="bi bi-tag me-1"></i>{{ $mark->mark_no }}
|
||||||
|
</div>
|
||||||
|
<div class="mark-route">
|
||||||
|
<i class="bi bi-arrow-right me-1"></i>
|
||||||
|
{{ $mark->origin }} → {{ $mark->destination }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endforeach
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
// Add hover effects to interactive elements
|
||||||
|
const interactiveElements = document.querySelectorAll('.info-card, .stats-card, .mark-item');
|
||||||
|
|
||||||
|
interactiveElements.forEach(element => {
|
||||||
|
element.addEventListener('mouseenter', function() {
|
||||||
|
this.style.transform = this.classList.contains('mark-item') ? 'translateX(5px)' : 'translateY(-5px)';
|
||||||
|
});
|
||||||
|
|
||||||
|
element.addEventListener('mouseleave', function() {
|
||||||
|
this.style.transform = 'translateY(0)';
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add loading animation to stats cards
|
||||||
|
const statsValues = document.querySelectorAll('.stats-value');
|
||||||
|
statsValues.forEach(value => {
|
||||||
|
const originalText = value.textContent;
|
||||||
|
value.textContent = '0';
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
value.textContent = originalText;
|
||||||
|
value.style.transform = 'scale(1.1)';
|
||||||
|
setTimeout(() => {
|
||||||
|
value.style.transform = 'scale(1)';
|
||||||
|
}, 300);
|
||||||
|
}, 500);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
@endsection
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -173,7 +173,7 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
min-width: 1100px;
|
min-width: 1100px;
|
||||||
border-collapse: separate;
|
border-collapse: separate;
|
||||||
border-spacing: 0;
|
border-spacing: 0 8px; /* Add gap between rows */
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -189,7 +189,6 @@
|
|||||||
border-top-right-radius: 17px;
|
border-top-right-radius: 17px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.table thead th {
|
.table thead th {
|
||||||
background: transparent !important;
|
background: transparent !important;
|
||||||
border: none;
|
border: none;
|
||||||
@@ -197,7 +196,7 @@
|
|||||||
color: #343535;
|
color: #343535;
|
||||||
letter-spacing: 0.02em;
|
letter-spacing: 0.02em;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
padding: 12px 8px;
|
padding: 16px 12px;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
position: relative;
|
position: relative;
|
||||||
border-bottom: 2px solid #e8e2cf;
|
border-bottom: 2px solid #e8e2cf;
|
||||||
@@ -206,50 +205,58 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.table thead th:first-child {
|
.table thead th:first-child {
|
||||||
padding-left: 20px;
|
padding-left: 25px;
|
||||||
}
|
}
|
||||||
.table thead th:last-child {
|
.table thead th:last-child {
|
||||||
padding-right: 20px;
|
padding-right: 25px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Soft blue background for ALL table rows */
|
||||||
.table-striped tbody tr {
|
.table-striped tbody tr {
|
||||||
background: #fff;
|
background: #f0f8ff !important; /* Soft blue background for all rows */
|
||||||
transition: all 0.2s ease;
|
transition: all 0.2s ease;
|
||||||
border-bottom: 1px solid #f1f3f4;
|
border-radius: 12px; /* Rounded corners for each row */
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05); /* Subtle shadow for separation */
|
||||||
}
|
}
|
||||||
|
|
||||||
.table-striped tbody tr:hover {
|
.table-striped tbody tr:hover {
|
||||||
background: #f8fafc !important;
|
background: #e6f3ff !important; /* Slightly darker blue on hover */
|
||||||
}
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); /* Enhanced shadow on hover */
|
||||||
|
transform: translateY(-1px); /* Lift effect on hover */
|
||||||
.table-striped tbody tr:nth-of-type(odd) {
|
|
||||||
background: #f9fafc;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Remove striped pattern - all rows same soft blue */
|
||||||
|
.table-striped tbody tr:nth-of-type(odd),
|
||||||
.table-striped tbody tr:nth-of-type(even) {
|
.table-striped tbody tr:nth-of-type(even) {
|
||||||
background: #ffffff;
|
background: #f0f8ff !important; /* Same soft blue for all rows */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Center all table cells */
|
/* Center all table cells with proper spacing */
|
||||||
.table td {
|
.table td {
|
||||||
padding: 10px 8px;
|
padding: 16px 12px; /* Increased vertical padding */
|
||||||
border: none;
|
border: none;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
color: #4a5568;
|
color: #4a5568;
|
||||||
border-bottom: 1px solid #f1f3f4;
|
|
||||||
text-align: center !important; /* Center all cell content */
|
text-align: center !important; /* Center all cell content */
|
||||||
|
border-top: 1px solid transparent;
|
||||||
|
border-bottom: 1px solid transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* First and last cell rounded corners */
|
||||||
.table td:first-child {
|
.table td:first-child {
|
||||||
padding-left: 20px;
|
padding-left: 25px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: #2d3748;
|
color: #2d3748;
|
||||||
|
border-top-left-radius: 12px;
|
||||||
|
border-bottom-left-radius: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.table td:last-child {
|
.table td:last-child {
|
||||||
padding-right: 20px;
|
padding-right: 25px;
|
||||||
|
border-top-right-radius: 12px;
|
||||||
|
border-bottom-right-radius: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Invoice Number with Curved Boxes */
|
/* Invoice Number with Curved Boxes */
|
||||||
@@ -257,13 +264,13 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center; /* Center the invoice number */
|
justify-content: center; /* Center the invoice number */
|
||||||
gap: 8px;
|
gap: 12px;
|
||||||
padding: 6px 0;
|
padding: 8px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.invoice-icon {
|
.invoice-icon {
|
||||||
width: 32px;
|
width: 36px;
|
||||||
height: 32px;
|
height: 36px;
|
||||||
border-radius: 8px; /* Curved border radius */
|
border-radius: 8px; /* Curved border radius */
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -324,31 +331,67 @@
|
|||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Badge Styling - Centered */
|
/* Badge Styling - Centered with custom backgrounds and icons */
|
||||||
.badge {
|
.badge {
|
||||||
font-size: 11px;
|
font-size: 11px !important;
|
||||||
font-weight: 600;
|
font-weight: 600 !important;
|
||||||
padding: 4px 10px;
|
padding: 6px 12px 6px 8px !important;
|
||||||
border-radius: 8px;
|
border-radius: 20px !important;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
letter-spacing: 0.3px;
|
letter-spacing: 0.3px;
|
||||||
display: inline-block;
|
display: inline-flex !important;
|
||||||
text-align: center;
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background-size: cover !important;
|
||||||
|
background-position: center !important;
|
||||||
|
color: #fff !important;
|
||||||
|
text-shadow: 0 1px 2px rgba(0,0,0,0.3);
|
||||||
|
border: 2px solid transparent !important;
|
||||||
|
min-width: 40px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
line-height: 1.2;
|
||||||
|
gap: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bg-success {
|
/* Status icons */
|
||||||
background: linear-gradient(135deg, #18ce77 60%, #08ac52 110%) !important;
|
.status-icon {
|
||||||
color: #fff !important;
|
font-size: 12px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bg-warning {
|
/* Custom status badge backgrounds with icons */
|
||||||
background: linear-gradient(135deg, #ffb23c 53%, #e17800 110%) !important;
|
.badge-paid {
|
||||||
color: #fff !important;
|
background: url('/images/status-bg-paid.png') !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bg-danger {
|
.badge-pending {
|
||||||
background: linear-gradient(135deg, #ff5a4e 65%, #d90010 110%) !important;
|
background: url('/images/status-bg-pending.png') !important;
|
||||||
color: #fff !important;
|
}
|
||||||
|
|
||||||
|
.badge-overdue {
|
||||||
|
background: url('/images/status-bg-overdue.png') !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fallback colors if images don't load - ALL WITH SAME SIZE */
|
||||||
|
.badge.badge-paid {
|
||||||
|
background: linear-gradient(135deg, #d1fae5, #a7f3d0) !important;
|
||||||
|
color: #065f46 !important;
|
||||||
|
border-color: #10b981 !important;
|
||||||
|
width: 98px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge.badge-pending {
|
||||||
|
background: linear-gradient(135deg, #fef3c7, #fde68a) !important;
|
||||||
|
color: #d97706 !important;
|
||||||
|
border-color: #f59e0b !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge.badge-overdue {
|
||||||
|
background: linear-gradient(135deg, #e9d5ff, #c4b5fd) !important;
|
||||||
|
color: #6b21a8 !important;
|
||||||
|
border-color: #8b5cf6 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Action Button - Centered */
|
/* Action Button - Centered */
|
||||||
@@ -356,7 +399,7 @@
|
|||||||
background: linear-gradient(135deg, #3492f8 55%, #1256cc 110%);
|
background: linear-gradient(135deg, #3492f8 55%, #1256cc 110%);
|
||||||
border: none;
|
border: none;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
padding: 5px 10px;
|
padding: 6px 12px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
@@ -461,102 +504,33 @@
|
|||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Responsive Design */
|
/* Date Range Picker Styles */
|
||||||
@media (max-width: 1200px) {
|
.date-range-container {
|
||||||
.invoice-tools-row {
|
display: flex;
|
||||||
flex-direction: column;
|
align-items: center;
|
||||||
align-items: stretch;
|
gap: 8px;
|
||||||
gap: 15px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-box {
|
.date-input {
|
||||||
max-width: 100%;
|
background: white;
|
||||||
min-width: auto;
|
border: 1px solid #e2e8f0;
|
||||||
}
|
border-radius: 8px;
|
||||||
|
padding: 8px 12px;
|
||||||
.filter-group {
|
|
||||||
justify-content: space-between;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
.invoice-management-bar {
|
|
||||||
padding: 12px 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.invoice-tools-row {
|
|
||||||
padding: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.table th, .table td {
|
|
||||||
font-size: 12px;
|
|
||||||
padding: 8px 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.badge {
|
|
||||||
font-size: 10px;
|
|
||||||
padding: 3px 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-primary {
|
|
||||||
padding: 4px 8px;
|
|
||||||
font-size: 11px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.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 {
|
|
||||||
width: 28px;
|
|
||||||
height: 28px;
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Empty State - Centered */
|
|
||||||
.text-muted {
|
|
||||||
color: #8a9bb9 !important;
|
|
||||||
font-style: italic;
|
|
||||||
padding: 30px !important;
|
|
||||||
text-align: center;
|
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
|
color: #334155;
|
||||||
|
outline: none;
|
||||||
|
min-width: 130px;
|
||||||
|
box-shadow: 0 2px 5px rgba(0,0,0,0.04);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Card Styling */
|
.date-input:focus {
|
||||||
.card {
|
border-color: #3b82f6;
|
||||||
border-radius: 0 0 17px 17px;
|
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
|
||||||
border: none;
|
|
||||||
margin-bottom: 0 !important;
|
|
||||||
box-shadow: none;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-header {
|
.date-separator {
|
||||||
background: #f8f9fa !important;
|
color: #64748b;
|
||||||
border-bottom: 1px solid #e9ecef;
|
font-weight: 500;
|
||||||
border-radius: 0 !important;
|
|
||||||
padding: 15px 25px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-header h4 {
|
|
||||||
margin: 0;
|
|
||||||
color: #2451af;
|
|
||||||
font-weight: 700;
|
|
||||||
font-size: 1.3rem;
|
|
||||||
text-align: center;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Stats Summary - Centered */
|
/* Stats Summary - Centered */
|
||||||
@@ -622,6 +596,262 @@
|
|||||||
.table tbody tr td .btn-primary {
|
.table tbody tr td .btn-primary {
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Empty State - Centered */
|
||||||
|
.text-muted {
|
||||||
|
color: #8a9bb9 !important;
|
||||||
|
font-style: italic;
|
||||||
|
padding: 30px !important;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Card Styling */
|
||||||
|
.card {
|
||||||
|
border-radius: 0 0 17px 17px;
|
||||||
|
border: none;
|
||||||
|
margin-bottom: 0 !important;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-header {
|
||||||
|
background: #f8f9fa !important;
|
||||||
|
border-bottom: 1px solid #e9ecef;
|
||||||
|
border-radius: 0 !important;
|
||||||
|
padding: 15px 25px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-header h4 {
|
||||||
|
margin: 0;
|
||||||
|
color: #2451af;
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 1.3rem;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mobile Responsive Styles */
|
||||||
|
.mobile-invoice-card {
|
||||||
|
display: none;
|
||||||
|
background: #f0f8ff;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 15px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
||||||
|
border-left: 4px solid #667eea;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-invoice-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
padding-bottom: 8px;
|
||||||
|
border-bottom: 1px solid #e2e8f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-invoice-number {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-invoice-icon {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
border-radius: 8px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: white;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-invoice-number-text {
|
||||||
|
font-weight: 600;
|
||||||
|
color: #2469d6;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-invoice-status {
|
||||||
|
font-size: 10px !important;
|
||||||
|
padding: 4px 8px 4px 6px !important;
|
||||||
|
min-width: 70px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-invoice-details {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: 10px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-detail-item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-detail-label {
|
||||||
|
font-size: 11px;
|
||||||
|
color: #718096;
|
||||||
|
margin-bottom: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-detail-value {
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #2d3748;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-invoice-actions {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-action-btn {
|
||||||
|
flex: 1;
|
||||||
|
text-align: center;
|
||||||
|
padding: 6px 10px;
|
||||||
|
font-size: 12px;
|
||||||
|
border-radius: 6px;
|
||||||
|
text-decoration: none;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-action-btn.view {
|
||||||
|
background: #f0f8ff;
|
||||||
|
color: #2469d6;
|
||||||
|
border: 1px solid #2469d6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-action-btn.edit {
|
||||||
|
background: linear-gradient(135deg, #3492f8 55%, #1256cc 110%);
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive Design */
|
||||||
|
@media (max-width: 1200px) {
|
||||||
|
.invoice-tools-row {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
gap: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-box {
|
||||||
|
max-width: 100%;
|
||||||
|
min-width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-group {
|
||||||
|
justify-content: space-between;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 992px) {
|
||||||
|
.stats-summary {
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.invoice-management-bar {
|
||||||
|
padding: 12px 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.invoice-tools-row {
|
||||||
|
padding: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table th, .table td {
|
||||||
|
font-size: 12px;
|
||||||
|
padding: 12px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge {
|
||||||
|
font-size: 10px !important;
|
||||||
|
padding: 5px 10px 5px 6px !important;
|
||||||
|
min-width: 70px;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-icon {
|
||||||
|
font-size: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary {
|
||||||
|
padding: 4px 8px;
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.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 {
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Show mobile cards and hide table on small screens */
|
||||||
|
.desktop-table {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-invoice-card {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.date-range-container {
|
||||||
|
flex-direction: column;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.date-input {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-summary {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
margin: 15px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 576px) {
|
||||||
|
.invoice-management-title {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-header h4 {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-invoice-details {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-invoice-actions {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<div class="container-fluid py-3">
|
<div class="container-fluid py-3">
|
||||||
@@ -650,19 +880,12 @@
|
|||||||
<option value="overdue">Overdue</option>
|
<option value="overdue">Overdue</option>
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<select class="filter-select" id="dateFilter">
|
<!-- Date Range Picker -->
|
||||||
<option value="">All Dates</option>
|
<div class="date-range-container">
|
||||||
<option value="today">Today</option>
|
<input type="date" class="date-input" id="startDate">
|
||||||
<option value="week">This Week</option>
|
<span class="date-separator">to</span>
|
||||||
<option value="month">This Month</option>
|
<input type="date" class="date-input" id="endDate">
|
||||||
</select>
|
</div>
|
||||||
|
|
||||||
<select class="filter-select" id="amountFilter">
|
|
||||||
<option value="">Any Amount</option>
|
|
||||||
<option value="0-1000">₹0 - ₹1,000</option>
|
|
||||||
<option value="1000-5000">₹1,000 - ₹5,000</option>
|
|
||||||
<option value="5000+">₹5,000+</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Create Invoice Button -->
|
<!-- Create Invoice Button -->
|
||||||
@@ -692,8 +915,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- ALL INVOICES TABLE -->
|
<!-- ALL INVOICES - Desktop Table -->
|
||||||
<div class="card shadow-sm">
|
<div class="card shadow-sm desktop-table">
|
||||||
<div class="card-header bg-light d-flex justify-content-between align-items-center compact-view">
|
<div class="card-header bg-light d-flex justify-content-between align-items-center compact-view">
|
||||||
<h4 class="mb-0">All Invoices</h4>
|
<h4 class="mb-0">All Invoices</h4>
|
||||||
<span class="text-muted" style="font-size: 12px;">
|
<span class="text-muted" style="font-size: 12px;">
|
||||||
@@ -747,10 +970,14 @@
|
|||||||
<td class="amount-cell">₹{{ number_format($invoice->final_amount_with_gst, 2) }}</td>
|
<td class="amount-cell">₹{{ number_format($invoice->final_amount_with_gst, 2) }}</td>
|
||||||
|
|
||||||
<td>
|
<td>
|
||||||
<span class="badge
|
<span class="badge badge-{{ $invoice->status }}">
|
||||||
@if($invoice->status=='paid') bg-success
|
@if($invoice->status == 'paid')
|
||||||
@elseif($invoice->status=='overdue') bg-danger
|
<i class="bi bi-check-circle-fill status-icon"></i>
|
||||||
@else bg-warning @endif">
|
@elseif($invoice->status == 'pending')
|
||||||
|
<i class="bi bi-clock-fill status-icon"></i>
|
||||||
|
@elseif($invoice->status == 'overdue')
|
||||||
|
<i class="bi bi-exclamation-triangle-fill status-icon"></i>
|
||||||
|
@endif
|
||||||
{{ ucfirst($invoice->status) }}
|
{{ ucfirst($invoice->status) }}
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
@@ -775,6 +1002,75 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- ALL INVOICES - Mobile Cards -->
|
||||||
|
<div class="mobile-invoices-container">
|
||||||
|
@php
|
||||||
|
$totalInvoices = $invoices->count();
|
||||||
|
$sortedInvoices = $invoices->sortByDesc('created_at'); // Latest first
|
||||||
|
@endphp
|
||||||
|
|
||||||
|
@forelse($sortedInvoices as $i => $invoice)
|
||||||
|
<div class="mobile-invoice-card" data-invoice-id="{{ $invoice->id }}">
|
||||||
|
<div class="mobile-invoice-header">
|
||||||
|
<div class="mobile-invoice-number">
|
||||||
|
<div class="mobile-invoice-icon invoice-icon-{{ (($totalInvoices - $i) % 8) + 1 }}">
|
||||||
|
<i class="bi bi-file-earmark-text"></i>
|
||||||
|
</div>
|
||||||
|
<span class="mobile-invoice-number-text">{{ $invoice->invoice_number }}</span>
|
||||||
|
</div>
|
||||||
|
<span class="badge badge-{{ $invoice->status }} mobile-invoice-status">
|
||||||
|
@if($invoice->status == 'paid')
|
||||||
|
<i class="bi bi-check-circle-fill status-icon"></i>
|
||||||
|
@elseif($invoice->status == 'pending')
|
||||||
|
<i class="bi bi-clock-fill status-icon"></i>
|
||||||
|
@elseif($invoice->status == 'overdue')
|
||||||
|
<i class="bi bi-exclamation-triangle-fill status-icon"></i>
|
||||||
|
@endif
|
||||||
|
{{ ucfirst($invoice->status) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mobile-invoice-details">
|
||||||
|
<div class="mobile-detail-item">
|
||||||
|
<span class="mobile-detail-label">Customer</span>
|
||||||
|
<span class="mobile-detail-value">{{ $invoice->customer_name }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="mobile-detail-item">
|
||||||
|
<span class="mobile-detail-label">Amount</span>
|
||||||
|
<span class="mobile-detail-value">₹{{ number_format($invoice->final_amount, 2) }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="mobile-detail-item">
|
||||||
|
<span class="mobile-detail-label">GST</span>
|
||||||
|
<span class="mobile-detail-value">{{ $invoice->gst_percent }}%</span>
|
||||||
|
</div>
|
||||||
|
<div class="mobile-detail-item">
|
||||||
|
<span class="mobile-detail-label">Total</span>
|
||||||
|
<span class="mobile-detail-value">₹{{ number_format($invoice->final_amount_with_gst, 2) }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="mobile-detail-item">
|
||||||
|
<span class="mobile-detail-label">Invoice Date</span>
|
||||||
|
<span class="mobile-detail-value">{{ $invoice->invoice_date }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="mobile-detail-item">
|
||||||
|
<span class="mobile-detail-label">Due Date</span>
|
||||||
|
<span class="mobile-detail-value">{{ $invoice->due_date }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mobile-invoice-actions">
|
||||||
|
<a href="#" class="mobile-action-btn view open-invoice-popup" data-id="{{ $invoice->id }}">
|
||||||
|
<i class="bi bi-eye"></i> View
|
||||||
|
</a>
|
||||||
|
<a href="{{ route('admin.invoices.edit', $invoice->id) }}" class="mobile-action-btn edit">
|
||||||
|
<i class="bi bi-pencil"></i> Edit
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@empty
|
||||||
|
<div class="text-muted text-center py-4">No invoices found</div>
|
||||||
|
@endforelse
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -827,20 +1123,26 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Search functionality
|
// Search and filter functionality for both desktop and mobile
|
||||||
const searchInput = document.getElementById('invoiceSearch');
|
const searchInput = document.getElementById('invoiceSearch');
|
||||||
const statusFilter = document.getElementById('statusFilter');
|
const statusFilter = document.getElementById('statusFilter');
|
||||||
const dateFilter = document.getElementById('dateFilter');
|
const startDateInput = document.getElementById('startDate');
|
||||||
const amountFilter = document.getElementById('amountFilter');
|
const endDateInput = document.getElementById('endDate');
|
||||||
|
|
||||||
|
// Desktop table elements
|
||||||
const table = document.getElementById('invoicesTable');
|
const table = document.getElementById('invoicesTable');
|
||||||
const tableRows = table.getElementsByTagName('tbody')[0].getElementsByTagName('tr');
|
const tableRows = table ? table.getElementsByTagName('tbody')[0].getElementsByTagName('tr') : [];
|
||||||
|
|
||||||
|
// Mobile card elements
|
||||||
|
const mobileCards = document.querySelectorAll('.mobile-invoice-card');
|
||||||
|
|
||||||
function filterInvoices() {
|
function filterInvoices() {
|
||||||
const searchTerm = searchInput.value.toLowerCase();
|
const searchTerm = searchInput.value.toLowerCase();
|
||||||
const statusValue = statusFilter.value;
|
const statusValue = statusFilter.value;
|
||||||
const dateValue = dateFilter.value;
|
const startDate = startDateInput.value;
|
||||||
const amountValue = amountFilter.value;
|
const endDate = endDateInput.value;
|
||||||
|
|
||||||
|
// Filter desktop table rows
|
||||||
for (let row of tableRows) {
|
for (let row of tableRows) {
|
||||||
const cells = row.getElementsByTagName('td');
|
const cells = row.getElementsByTagName('td');
|
||||||
if (cells.length < 9) continue;
|
if (cells.length < 9) continue;
|
||||||
@@ -848,25 +1150,55 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
const invoiceNumber = cells[1].textContent.toLowerCase();
|
const invoiceNumber = cells[1].textContent.toLowerCase();
|
||||||
const customerName = cells[2].textContent.toLowerCase();
|
const customerName = cells[2].textContent.toLowerCase();
|
||||||
const status = cells[6].textContent.toLowerCase();
|
const status = cells[6].textContent.toLowerCase();
|
||||||
const amount = parseFloat(cells[3].textContent.replace('₹', '').replace(',', ''));
|
const invoiceDate = cells[7].textContent;
|
||||||
|
|
||||||
const matchesSearch = invoiceNumber.includes(searchTerm) || customerName.includes(searchTerm);
|
const matchesSearch = invoiceNumber.includes(searchTerm) || customerName.includes(searchTerm);
|
||||||
const matchesStatus = !statusValue || status.includes(statusValue);
|
const matchesStatus = !statusValue || status.includes(statusValue);
|
||||||
const matchesAmount = !amountValue || (
|
|
||||||
amountValue === '0-1000' ? amount <= 1000 :
|
|
||||||
amountValue === '1000-5000' ? amount > 1000 && amount <= 5000 :
|
|
||||||
amountValue === '5000+' ? amount > 5000 : true
|
|
||||||
);
|
|
||||||
|
|
||||||
row.style.display = matchesSearch && matchesStatus && matchesAmount ? '' : 'none';
|
// Date filtering
|
||||||
|
let matchesDate = true;
|
||||||
|
if (startDate || endDate) {
|
||||||
|
const cellDate = new Date(invoiceDate);
|
||||||
|
const start = startDate ? new Date(startDate) : null;
|
||||||
|
const end = endDate ? new Date(endDate) : null;
|
||||||
|
|
||||||
|
if (start && cellDate < start) matchesDate = false;
|
||||||
|
if (end && cellDate > end) matchesDate = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
row.style.display = matchesSearch && matchesStatus && matchesDate ? '' : 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter mobile cards
|
||||||
|
for (let card of mobileCards) {
|
||||||
|
const invoiceNumber = card.querySelector('.mobile-invoice-number-text').textContent.toLowerCase();
|
||||||
|
const customerName = card.querySelector('.mobile-detail-item:nth-child(1) .mobile-detail-value').textContent.toLowerCase();
|
||||||
|
const status = card.querySelector('.badge').textContent.toLowerCase();
|
||||||
|
const invoiceDate = card.querySelector('.mobile-detail-item:nth-child(5) .mobile-detail-value').textContent;
|
||||||
|
|
||||||
|
const matchesSearch = invoiceNumber.includes(searchTerm) || customerName.includes(searchTerm);
|
||||||
|
const matchesStatus = !statusValue || status.includes(statusValue);
|
||||||
|
|
||||||
|
// Date filtering
|
||||||
|
let matchesDate = true;
|
||||||
|
if (startDate || endDate) {
|
||||||
|
const cellDate = new Date(invoiceDate);
|
||||||
|
const start = startDate ? new Date(startDate) : null;
|
||||||
|
const end = endDate ? new Date(endDate) : null;
|
||||||
|
|
||||||
|
if (start && cellDate < start) matchesDate = false;
|
||||||
|
if (end && cellDate > end) matchesDate = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
card.style.display = matchesSearch && matchesStatus && matchesDate ? '' : 'none';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add event listeners for filtering
|
// Add event listeners for filtering
|
||||||
searchInput.addEventListener('input', filterInvoices);
|
searchInput.addEventListener('input', filterInvoices);
|
||||||
statusFilter.addEventListener('change', filterInvoices);
|
statusFilter.addEventListener('change', filterInvoices);
|
||||||
dateFilter.addEventListener('change', filterInvoices);
|
startDateInput.addEventListener('change', filterInvoices);
|
||||||
amountFilter.addEventListener('change', filterInvoices);
|
endDateInput.addEventListener('change', filterInvoices);
|
||||||
|
|
||||||
// Add hover effects to table rows
|
// Add hover effects to table rows
|
||||||
for (let row of tableRows) {
|
for (let row of tableRows) {
|
||||||
|
|||||||
@@ -189,7 +189,12 @@
|
|||||||
<i class="bi bi-receipt"></i> Invoice
|
<i class="bi bi-receipt"></i> Invoice
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<a href="{{ route('admin.customers') }}" class="{{ request()->routeIs('admin.customers') ? 'active' : '' }}"><i class="bi bi-people"></i> Customers</a>
|
<a href="{{ route('admin.customers.index') }}"
|
||||||
|
class="{{ request()->routeIs('admin.customers.index') ? 'active' : '' }}">
|
||||||
|
<i class="bi bi-people"></i> Customers
|
||||||
|
</a>
|
||||||
|
|
||||||
|
|
||||||
<a href="{{ route('admin.reports') }}" class="{{ request()->routeIs('admin.reports') ? 'active' : '' }}"><i class="bi bi-graph-up"></i> Reports</a>
|
<a href="{{ route('admin.reports') }}" class="{{ request()->routeIs('admin.reports') ? 'active' : '' }}"><i class="bi bi-graph-up"></i> Reports</a>
|
||||||
<a href="{{ route('admin.chat_support') }}" class="{{ request()->routeIs('admin.chat_support') ? 'active' : '' }}"><i class="bi bi-chat-dots"></i> Chat Support</a>
|
<a href="{{ route('admin.chat_support') }}" class="{{ request()->routeIs('admin.chat_support') ? 'active' : '' }}"><i class="bi bi-chat-dots"></i> Chat Support</a>
|
||||||
<!-- <a href="{{ route('admin.orders.index') }}"
|
<!-- <a href="{{ route('admin.orders.index') }}"
|
||||||
|
|||||||
@@ -6,194 +6,267 @@
|
|||||||
<style>
|
<style>
|
||||||
body {
|
body {
|
||||||
font-family: 'Segoe UI', 'Roboto', Arial, sans-serif;
|
font-family: 'Segoe UI', 'Roboto', Arial, sans-serif;
|
||||||
font-size: 14px;
|
background: #F7FBFC;
|
||||||
background: #f6f8fb;
|
color: #1A222B;
|
||||||
color: #2f3440;
|
margin: 0;
|
||||||
letter-spacing: 0.02em;
|
padding: 0;
|
||||||
|
font-size: 15px;
|
||||||
}
|
}
|
||||||
.header-box {
|
.container {
|
||||||
text-align: center;
|
max-width: 850px;
|
||||||
border-bottom: 3px solid #1b2430;
|
margin: 24px auto 0 auto;
|
||||||
background: linear-gradient(to right, #0766ad 0%, #85c6ee 100%);
|
background: #fff;
|
||||||
padding: 26px 0 18px 0;
|
border-radius: 13px;
|
||||||
margin-bottom: 28px;
|
box-shadow: 0 2px 14px rgba(40,105,160,0.08);
|
||||||
border-radius: 0 0 15px 15px;
|
padding: 35px 32px 18px 32px;
|
||||||
box-shadow: 0 2px 12px rgba(35,82,124,0.08);
|
}
|
||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: flex-start;
|
||||||
|
border-bottom: 2px solid #E6EBF0;
|
||||||
|
padding-bottom: 13px;
|
||||||
|
}
|
||||||
|
.logo-company {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
}
|
}
|
||||||
.logo {
|
.logo {
|
||||||
width: 120px;
|
height: 50px;
|
||||||
margin-bottom: 12px;
|
margin-right: 13px;
|
||||||
filter: drop-shadow(0 2px 5px #0001);
|
|
||||||
}
|
}
|
||||||
.title {
|
.company-details {
|
||||||
font-size: 28px;
|
margin-top: 0;
|
||||||
font-weight: bold;
|
|
||||||
color: #1a4c8b;
|
|
||||||
letter-spacing: 2px;
|
|
||||||
margin-bottom: 5px;
|
|
||||||
text-shadow: 0 2px 9px #90caf944;
|
|
||||||
}
|
|
||||||
.subtitle {
|
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
margin-top: 3px;
|
|
||||||
color: #376f9e;
|
|
||||||
font-weight: 500;
|
|
||||||
letter-spacing: 1px;
|
|
||||||
}
|
}
|
||||||
.section-title {
|
.company-title {
|
||||||
font-size: 19px;
|
font-size: 21px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
margin-top: 28px;
|
margin-bottom: 2px;
|
||||||
margin-bottom: 10px;
|
|
||||||
color: #0766ad;
|
|
||||||
text-transform: uppercase;
|
|
||||||
letter-spacing: 1px;
|
|
||||||
border-left: 4px solid #0766ad;
|
|
||||||
padding-left: 12px;
|
|
||||||
}
|
}
|
||||||
.info-box {
|
.company-sub {
|
||||||
border: 1px solid #d3e3fd;
|
font-size: 16px;
|
||||||
padding: 13px 18px;
|
margin-bottom: 8px;
|
||||||
line-height: 1.8;
|
font-weight: 500;
|
||||||
background: #ecf7ff;
|
|
||||||
border-radius: 10px;
|
|
||||||
box-shadow: 0 2px 8px rgba(100,143,176,0.05);
|
|
||||||
margin-bottom: 7px;
|
|
||||||
}
|
}
|
||||||
|
.invoice-details {
|
||||||
|
text-align: right;
|
||||||
|
min-width: 220px;
|
||||||
|
}
|
||||||
|
.invoice-title {
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 23px;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
margin-bottom: 2px;
|
||||||
|
}
|
||||||
|
.paid-label {
|
||||||
|
margin-top: 8px;
|
||||||
|
margin-bottom: 2px;
|
||||||
|
}
|
||||||
|
.paid-tag {
|
||||||
|
background: #23BF47;
|
||||||
|
color: #fff;
|
||||||
|
font-weight: bold;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 4px 16px 4px 22px;
|
||||||
|
font-size: 17px;
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.paid-tag:before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
left: 7px;
|
||||||
|
top: 7px;
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
.paid-date {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #23BF47;
|
||||||
|
margin-top: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bill-section {
|
||||||
|
background: #F3F7FB;
|
||||||
|
border-radius: 11px;
|
||||||
|
padding: 20px 18px 13px 18px;
|
||||||
|
margin: 28px 0 16px 0;
|
||||||
|
box-shadow: 0 0px 0px #0000;
|
||||||
|
}
|
||||||
|
.bill-title {
|
||||||
|
font-size: 17px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #23355D;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
letter-spacing: 0.3px;
|
||||||
|
}
|
||||||
|
.bill-details {
|
||||||
|
font-size: 15px;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
table {
|
table {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
margin-top: 15px;
|
margin-top: 9px;
|
||||||
|
margin-bottom: 13px;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
box-shadow: 0 2px 18px rgba(91,146,196,0.05);
|
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
th {
|
th {
|
||||||
background: linear-gradient(90deg, #e3f1fb 80%, #f0f6ff 100%);
|
background: #F6F7F9;
|
||||||
padding: 10px 7px;
|
padding: 10px 0;
|
||||||
font-size: 14px;
|
font-size: 15px;
|
||||||
font-weight: 700;
|
color: #6781A6;
|
||||||
color: #085d99;
|
font-weight: bold;
|
||||||
border: 1px solid #b1cbe1;
|
border: none;
|
||||||
text-align: center;
|
text-align: left;
|
||||||
}
|
}
|
||||||
td {
|
td {
|
||||||
padding: 8px 7px;
|
padding: 7px 0;
|
||||||
border: 1px solid #dae5ec;
|
color: #222;
|
||||||
color: #395471;
|
font-size: 15px;
|
||||||
font-size: 13px;
|
border: none;
|
||||||
background: #fafcff;
|
text-align: left;
|
||||||
|
}
|
||||||
|
tbody tr:not(:last-child) td {
|
||||||
|
border-bottom: 1px solid #E6EBF0;
|
||||||
|
}
|
||||||
|
tbody tr:last-child td {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
.totals-row td {
|
||||||
|
font-weight: bold;
|
||||||
|
color: #23355D;
|
||||||
|
}
|
||||||
|
.gst-row td {
|
||||||
|
font-weight: 500;
|
||||||
|
color: #23BF47;
|
||||||
|
}
|
||||||
|
.total-row td {
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 17px;
|
||||||
|
color: #222;
|
||||||
|
}
|
||||||
|
.payment-info {
|
||||||
|
margin-top: 24px;
|
||||||
|
margin-bottom: 9px;
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
.ref-number {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #6781A6;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
margin-top: 2px;
|
||||||
|
}
|
||||||
|
.footer {
|
||||||
|
border-top: 1.2px solid #E6EBF0;
|
||||||
|
margin-top: 25px;
|
||||||
|
padding-top: 12px;
|
||||||
|
font-size: 16px;
|
||||||
|
color: #888;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
tbody tr:nth-child(even) td {
|
.footer strong {
|
||||||
background: #f3fafd;
|
color: #222;
|
||||||
}
|
font-weight: 500;
|
||||||
tbody tr:hover td {
|
|
||||||
background: #e2f1fa;
|
|
||||||
transition: background 0.2s;
|
|
||||||
}
|
|
||||||
.totals-box {
|
|
||||||
margin-top: 22px;
|
|
||||||
margin-bottom: 16px;
|
|
||||||
padding: 14px 22px;
|
|
||||||
border: 1.5px solid #b1cbe1;
|
|
||||||
background: linear-gradient(90deg, #eaf6fb 70%, #f8fbfe 100%);
|
|
||||||
font-size: 16px;
|
|
||||||
line-height: 2.1;
|
|
||||||
border-radius: 11px;
|
|
||||||
box-shadow: 0 2px 10px rgba(120,160,200,0.08);
|
|
||||||
}
|
|
||||||
.totals-box strong {
|
|
||||||
color: #0766ad;
|
|
||||||
font-size: 15.5px;
|
|
||||||
}
|
|
||||||
@media (max-width: 680px) {
|
|
||||||
.header-box, .info-box, .totals-box {
|
|
||||||
padding: 10px;
|
|
||||||
}
|
|
||||||
.section-title {
|
|
||||||
font-size: 15.5px;
|
|
||||||
padding-left: 7px;
|
|
||||||
margin-bottom: 7px;
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
th, td {
|
|
||||||
font-size: 12px;
|
|
||||||
padding: 5px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<!-- HEADER -->
|
<div class="container">
|
||||||
<div class="header-box">
|
<!-- Header Section -->
|
||||||
<img src="{{ public_path('images/kent_logo2.png') }}" alt="logo" class="logo">
|
<div class="header">
|
||||||
<div class="title">KENT LOGISTICS</div>
|
<div class="logo-company">
|
||||||
<div class="subtitle">Official Invoice</div>
|
<img src="{{ public_path('images/kent_logo2.png') }}" class="logo">
|
||||||
|
<div class="company-details">
|
||||||
|
<div class="company-title">{{ $invoice->company_name ?? 'Kent International Pvt. Ltd.' }}</div>
|
||||||
|
<div class="company-sub"></div>
|
||||||
|
{{ $invoice->company_address ?? '123 Business Park, Sector 5' }}<br>
|
||||||
|
{{ $invoice->company_city ?? 'Gurugram, Haryana 122001' }}<br>
|
||||||
|
{{ $invoice->company_country ?? 'India' }}<br>
|
||||||
|
GST: {{ $invoice->company_gst ?? 'GST123456789' }}<br>
|
||||||
|
Email: {{ $invoice->company_email ?? 'billing@kent.com' }}<br>
|
||||||
|
Phone: {{ $invoice->company_phone ?? '+91 124 123 4567' }}
|
||||||
</div>
|
</div>
|
||||||
<!-- INVOICE INFO -->
|
|
||||||
<div class="info-box">
|
|
||||||
<strong>Invoice No:</strong> {{ $invoice->invoice_number }} <br>
|
|
||||||
<strong>Invoice Date:</strong> {{ $invoice->invoice_date }} <br>
|
|
||||||
<strong>Due Date:</strong> {{ $invoice->due_date }} <br>
|
|
||||||
<strong>Status:</strong> {{ ucfirst($invoice->status) }}
|
|
||||||
</div>
|
</div>
|
||||||
<!-- CUSTOMER DETAILS -->
|
<div class="invoice-details">
|
||||||
<div class="section-title">Customer Details</div>
|
<div class="invoice-title">INVOICE</div>
|
||||||
<div class="info-box">
|
Invoice #: {{ $invoice->invoice_number }}<br>
|
||||||
|
Issue Date: {{ date('d M Y', strtotime($invoice->invoice_date)) }}<br>
|
||||||
|
Due Date: {{ date('d M Y', strtotime($invoice->due_date)) }}
|
||||||
|
@if(strtolower($invoice->status) == 'paid')
|
||||||
|
<div class="paid-label">
|
||||||
|
<span class="paid-tag">Paid</span>
|
||||||
|
</div>
|
||||||
|
<div class="paid-date">
|
||||||
|
Paid on: {{ date('d M Y', strtotime($invoice->paid_date ?? now())) }}
|
||||||
|
</div>
|
||||||
|
@else
|
||||||
|
<div class="paid-date" style="color:#d00;">Status: {{ ucfirst($invoice->status) }}</div>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- Bill To Section -->
|
||||||
|
<div class="bill-section">
|
||||||
|
<div class="bill-title">Bill To</div>
|
||||||
|
<div class="bill-details">
|
||||||
<strong>{{ $invoice->customer_name }}</strong><br>
|
<strong>{{ $invoice->customer_name }}</strong><br>
|
||||||
{{ $invoice->company_name }} <br>
|
{{ $invoice->customer_address }}<br>
|
||||||
{{ $invoice->customer_mobile }} <br>
|
GST: {{ $invoice->customer_gst ?? '-' }}<br>
|
||||||
{{ $invoice->customer_email }} <br>
|
Email: {{ $invoice->customer_email }}<br>
|
||||||
{{ $invoice->customer_address }}
|
Phone: {{ $invoice->customer_mobile }}
|
||||||
</div>
|
</div>
|
||||||
<!-- ITEMS TABLE -->
|
</div>
|
||||||
<div class="section-title">Invoice Items</div>
|
<!-- Items Table -->
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>#</th>
|
|
||||||
<th>Description</th>
|
<th>Description</th>
|
||||||
<th>CTN</th>
|
<th>Qty</th>
|
||||||
<th>QTY</th>
|
<th>Rate (₹)</th>
|
||||||
<th>TTL/QTY</th>
|
<th>Amount (₹)</th>
|
||||||
<th>Unit</th>
|
|
||||||
<th>Price</th>
|
|
||||||
<th>TTL Amount</th>
|
|
||||||
<th>CBM</th>
|
|
||||||
<th>TTL CBM</th>
|
|
||||||
<th>KG</th>
|
|
||||||
<th>TTL KG</th>
|
|
||||||
<th>Shop No</th>
|
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@foreach($invoice->items as $i => $item)
|
@foreach($invoice->items as $item)
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ $i + 1 }}</td>
|
|
||||||
<td>{{ $item->description }}</td>
|
<td>{{ $item->description }}</td>
|
||||||
<td>{{ $item->ctn }}</td>
|
|
||||||
<td>{{ $item->qty }}</td>
|
<td>{{ $item->qty }}</td>
|
||||||
<td>{{ $item->ttl_qty }}</td>
|
<td>{{ number_format($item->price, 0) }}</td>
|
||||||
<td>{{ $item->unit }}</td>
|
<td>{{ number_format($item->ttl_amount, 0) }}</td>
|
||||||
<td>{{ number_format($item->price, 2) }}</td>
|
|
||||||
<td>{{ number_format($item->ttl_amount, 2) }}</td>
|
|
||||||
<td>{{ $item->cbm }}</td>
|
|
||||||
<td>{{ $item->ttl_cbm }}</td>
|
|
||||||
<td>{{ $item->kg }}</td>
|
|
||||||
<td>{{ $item->ttl_kg }}</td>
|
|
||||||
<td>{{ $item->shop_no }}</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
@endforeach
|
@endforeach
|
||||||
|
<tr class="totals-row">
|
||||||
|
<td colspan="3" style="text-align:right;">Subtotal:</td>
|
||||||
|
<td>{{ number_format($invoice->subtotal, 0) }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr class="gst-row">
|
||||||
|
<td colspan="3" style="text-align:right;">GST ({{ $invoice->gst_percent }}%):</td>
|
||||||
|
<td>{{ number_format($invoice->gst_amount, 0) }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr class="total-row">
|
||||||
|
<td colspan="3" style="text-align:right;">Total:</td>
|
||||||
|
<td>{{ number_format($invoice->final_amount_with_gst, 0) }}</td>
|
||||||
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<!-- TOTALS -->
|
<!-- Payment Info & Reference -->
|
||||||
<div class="section-title">Totals</div>
|
<div class="payment-info">
|
||||||
<div class="totals-box">
|
<strong>Payment Method:</strong> {{ $invoice->payment_method ?? 'Bank Transfer' }}
|
||||||
<strong>Amount:</strong> ₹{{ number_format($invoice->final_amount, 2) }} <br>
|
</div>
|
||||||
<strong>GST ({{ $invoice->gst_percent }}%):</strong> ₹{{ number_format($invoice->gst_amount, 2) }} <br>
|
<div class="ref-number">
|
||||||
<strong>Total With GST:</strong> <strong>₹{{ number_format($invoice->final_amount_with_gst, 2) }}</strong>
|
Reference Number: {{ $invoice->reference_no ?? "REF123456789" }}
|
||||||
|
</div>
|
||||||
|
<!-- Footer -->
|
||||||
|
<div class="footer">
|
||||||
|
Thank you for your business!<br>
|
||||||
|
For any queries, please contact us at <strong>{{ $invoice->company_email ?? 'billing@kent.com' }}</strong>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -9,6 +9,25 @@
|
|||||||
overflow-x: hidden !important;
|
overflow-x: hidden !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.table thead th,
|
||||||
|
.table tbody td {
|
||||||
|
white-space: nowrap !important;
|
||||||
|
padding: 8px 4px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table th:nth-child(10), /* if Date is column 10 */
|
||||||
|
.table td:nth-child(10) {
|
||||||
|
max-width: 110px !important;
|
||||||
|
width: 110px !important;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table {
|
||||||
|
table-layout: fixed !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
.table, .custom-table-modal, .shipment-details-table {
|
.table, .custom-table-modal, .shipment-details-table {
|
||||||
min-width: unset !important;
|
min-width: unset !important;
|
||||||
width: 100% !important;
|
width: 100% !important;
|
||||||
@@ -226,62 +245,79 @@
|
|||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* UPDATED: Status Badge Styles */
|
/* UPDATED: Status Badge Styles - ALL SAME SIZE */
|
||||||
.badge {
|
.badge {
|
||||||
padding: 8px 16px;
|
padding: 7px 17px !important;
|
||||||
border-radius: 20px;
|
border-radius: 20px !important;
|
||||||
font-weight: 600;
|
font-weight: 600 !important;
|
||||||
font-size: 0.8rem;
|
font-size: 13px !important;
|
||||||
border: 2px solid transparent;
|
border: 2px solid transparent !important;
|
||||||
|
min-width: 40px !important;
|
||||||
|
text-align: center !important;
|
||||||
|
display: inline-block !important;
|
||||||
|
line-height: 1.2 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Pending Status */
|
/* Pending Status - SAME SIZE */
|
||||||
.badge-pending {
|
.badge-pending {
|
||||||
background: linear-gradient(135deg, #fef3c7, #fde68a) !important;
|
background: linear-gradient(135deg, #fef3c7, #fde68a) !important;
|
||||||
color: #d97706 !important;
|
color: #d97706 !important;
|
||||||
border-color: #f59e0b;
|
border-color: #f59e0b !important;
|
||||||
|
width: 110px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* In Transit Status */
|
/* In Transit Status - SAME SIZE */
|
||||||
.badge-in_transit {
|
.badge-in_transit {
|
||||||
background: linear-gradient(135deg, #dbeafe, #93c5fd) !important;
|
background: linear-gradient(135deg, #dbeafe, #93c5fd) !important;
|
||||||
color: #1e40af !important;
|
color: #1e40af !important;
|
||||||
border-color: #3b82f6;
|
border-color: #3b82f6 !important;
|
||||||
|
width: 110px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Dispatched Status */
|
/* Dispatched Status - SAME SIZE */
|
||||||
.badge-dispatched {
|
.badge-dispatched {
|
||||||
background: linear-gradient(135deg, #e9d5ff, #c4b5fd) !important;
|
background: linear-gradient(135deg, #e9d5ff, #c4b5fd) !important;
|
||||||
color: #6b21a8 !important;
|
color: #6b21a8 !important;
|
||||||
border-color: #8b5cf6;
|
border-color: #8b5cf6 !important;
|
||||||
|
width: 110px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Delivered Status */
|
/* Delivered Status - SAME SIZE */
|
||||||
.badge-delivered {
|
.badge-delivered {
|
||||||
background: linear-gradient(135deg, #d1fae5, #a7f3d0) !important;
|
background: linear-gradient(135deg, #d1fae5, #a7f3d0) !important;
|
||||||
color: #065f46 !important;
|
color: #065f46 !important;
|
||||||
border-color: #10b981;
|
border-color: #10b981 !important;
|
||||||
|
width: 110px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Default badge styles */
|
/* Default badge styles - SAME SIZE */
|
||||||
.badge.bg-info {
|
.badge.bg-info {
|
||||||
background: linear-gradient(135deg, #4cc9f0, #4361ee) !important;
|
background: linear-gradient(135deg, #4cc9f0, #4361ee) !important;
|
||||||
color: white;
|
color: white !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.badge.bg-success {
|
.badge.bg-success {
|
||||||
background: linear-gradient(135deg, #4ade80, #22c55e) !important;
|
background: linear-gradient(135deg, #4ade80, #22c55e) !important;
|
||||||
color: white;
|
color: white !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.badge.bg-warning {
|
.badge.bg-warning {
|
||||||
background: linear-gradient(135deg, #fbbf24, #f59e0b) !important;
|
background: linear-gradient(135deg, #fbbf24, #f59e0b) !important;
|
||||||
color: white;
|
color: white !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.badge.bg-danger {
|
.badge.bg-danger {
|
||||||
background: linear-gradient(135deg, #f87171, #ef4444) !important;
|
background: linear-gradient(135deg, #f87171, #ef4444) !important;
|
||||||
color: white;
|
color: white !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Light badges for quantity, kg, cbm */
|
||||||
|
.badge.bg-light {
|
||||||
|
background: #f8f9fa !important;
|
||||||
|
color: #212529 !important;
|
||||||
|
border: 1px solid #dee2e6 !important;
|
||||||
|
min-width: 80px !important;
|
||||||
|
padding: 6px 12px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* NEW: Action Button Styles */
|
/* NEW: Action Button Styles */
|
||||||
@@ -851,6 +887,36 @@
|
|||||||
.loading {
|
.loading {
|
||||||
animation: pulse 1.5s infinite;
|
animation: pulse 1.5s infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Status Filter Styles */
|
||||||
|
.status-filter-container {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-filter-select {
|
||||||
|
cursor: pointer;
|
||||||
|
appearance: none;
|
||||||
|
-webkit-appearance: none;
|
||||||
|
-moz-appearance: none;
|
||||||
|
background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' 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 12px center;
|
||||||
|
background-size: 16px;
|
||||||
|
padding-right: 40px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Shipment row styling for filtering */
|
||||||
|
.shipment-row {
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shipment-row.hidden {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shipment-row.visible {
|
||||||
|
display: table-row;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<div class="container-fluid py-4">
|
<div class="container-fluid py-4">
|
||||||
@@ -877,18 +943,21 @@
|
|||||||
<!-- ============================= -->
|
<!-- ============================= -->
|
||||||
<div class="search-shipment-bar">
|
<div class="search-shipment-bar">
|
||||||
<span class="search-icon">🔍</span>
|
<span class="search-icon">🔍</span>
|
||||||
<input type="text" placeholder="Search Shipments...">
|
<input type="text" id="searchInput" placeholder="Search Shipments...">
|
||||||
<select>
|
<div class="status-filter-container">
|
||||||
<option>All Status</option>
|
<select id="statusFilter" class="status-filter-select">
|
||||||
<option>Pending</option>
|
<option value="all">All Status</option>
|
||||||
<option>In Transit</option>
|
<option value="pending">Pending</option>
|
||||||
<option>Delivered</option>
|
<option value="in_transit">In Transit</option>
|
||||||
|
<option value="dispatched">Dispatched</option>
|
||||||
|
<option value="delivered">Delivered</option>
|
||||||
</select>
|
</select>
|
||||||
<select>
|
</div>
|
||||||
<option>All Carriers</option>
|
<select id="carrierFilter">
|
||||||
<option>FedEx</option>
|
<option value="all">All Carriers</option>
|
||||||
<option>UPS</option>
|
<option value="fedex">FedEx</option>
|
||||||
<option>DHL</option>
|
<option value="ups">UPS</option>
|
||||||
|
<option value="dhl">DHL</option>
|
||||||
</select>
|
</select>
|
||||||
<button type="button" class="btn-add-shipment" data-bs-toggle="modal" data-bs-target="#createShipmentModal">
|
<button type="button" class="btn-add-shipment" data-bs-toggle="modal" data-bs-target="#createShipmentModal">
|
||||||
<span class="truck-icon">🚚</span>
|
<span class="truck-icon">🚚</span>
|
||||||
@@ -1017,10 +1086,14 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
|
||||||
<tbody>
|
<tbody id="shipmentsTableBody">
|
||||||
|
@php
|
||||||
|
$totalShipments = count($shipments);
|
||||||
|
@endphp
|
||||||
@forelse($shipments as $ship)
|
@forelse($shipments as $ship)
|
||||||
<tr>
|
<tr class="shipment-row" data-status="{{ $ship->status }}" data-shipment-id="{{ $ship->shipment_id }}">
|
||||||
<td class="fw-bold">{{ $loop->iteration }}</td>
|
{{-- REVERSE INDEX: सर्वात वरच्या shipment ला सर्वात मोठा क्रमांक --}}
|
||||||
|
<td class="fw-bold">{{ $totalShipments - $loop->index }}</td>
|
||||||
<td>
|
<td>
|
||||||
<a href="#" class="text-primary fw-bold"
|
<a href="#" class="text-primary fw-bold"
|
||||||
onclick="openShipmentDetails({{ $ship->id }})">
|
onclick="openShipmentDetails({{ $ship->id }})">
|
||||||
@@ -1112,6 +1185,72 @@
|
|||||||
<!-- MODAL LOAD SCRIPT (AJAX) -->
|
<!-- MODAL LOAD SCRIPT (AJAX) -->
|
||||||
<!-- ========================= -->
|
<!-- ========================= -->
|
||||||
<script>
|
<script>
|
||||||
|
// Status Filter Functionality
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
const statusFilter = document.getElementById('statusFilter');
|
||||||
|
const searchInput = document.getElementById('searchInput');
|
||||||
|
const carrierFilter = document.getElementById('carrierFilter');
|
||||||
|
const shipmentRows = document.querySelectorAll('.shipment-row');
|
||||||
|
|
||||||
|
// Function to filter shipments
|
||||||
|
function filterShipments() {
|
||||||
|
const selectedStatus = statusFilter.value;
|
||||||
|
const searchTerm = searchInput.value.toLowerCase();
|
||||||
|
const selectedCarrier = carrierFilter.value;
|
||||||
|
|
||||||
|
shipmentRows.forEach(row => {
|
||||||
|
const status = row.getAttribute('data-status');
|
||||||
|
const shipmentId = row.getAttribute('data-shipment-id').toLowerCase();
|
||||||
|
const origin = row.cells[2].textContent.toLowerCase();
|
||||||
|
const destination = row.cells[3].textContent.toLowerCase();
|
||||||
|
|
||||||
|
let statusMatch = selectedStatus === 'all' || status === selectedStatus;
|
||||||
|
let searchMatch = searchTerm === '' ||
|
||||||
|
shipmentId.includes(searchTerm) ||
|
||||||
|
origin.includes(searchTerm) ||
|
||||||
|
destination.includes(searchTerm);
|
||||||
|
let carrierMatch = selectedCarrier === 'all'; // You can add carrier data attribute if needed
|
||||||
|
|
||||||
|
if (statusMatch && searchMatch && carrierMatch) {
|
||||||
|
row.classList.remove('hidden');
|
||||||
|
row.classList.add('visible');
|
||||||
|
} else {
|
||||||
|
row.classList.add('hidden');
|
||||||
|
row.classList.remove('visible');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Show message if no results
|
||||||
|
const visibleRows = document.querySelectorAll('.shipment-row:not(.hidden)');
|
||||||
|
const noResultsRow = document.querySelector('.shipment-row[colspan]');
|
||||||
|
|
||||||
|
if (visibleRows.length === 0 && !noResultsRow) {
|
||||||
|
// Add no results message
|
||||||
|
const tbody = document.getElementById('shipmentsTableBody');
|
||||||
|
const noResultsHtml = `
|
||||||
|
<tr>
|
||||||
|
<td colspan="11" class="text-center py-5 text-muted">
|
||||||
|
<i class="bi bi-search display-4 d-block mb-3"></i>
|
||||||
|
No shipments found matching your criteria
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
`;
|
||||||
|
tbody.innerHTML = noResultsHtml;
|
||||||
|
} else if (visibleRows.length > 0 && noResultsRow) {
|
||||||
|
// Remove no results message
|
||||||
|
noResultsRow.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Event listeners for filters
|
||||||
|
statusFilter.addEventListener('change', filterShipments);
|
||||||
|
searchInput.addEventListener('input', filterShipments);
|
||||||
|
carrierFilter.addEventListener('change', filterShipments);
|
||||||
|
|
||||||
|
// Initialize filter on page load
|
||||||
|
filterShipments();
|
||||||
|
});
|
||||||
|
|
||||||
function openShipmentDetails(id) {
|
function openShipmentDetails(id) {
|
||||||
let modal = new bootstrap.Modal(document.getElementById('shipmentDetailsModal'));
|
let modal = new bootstrap.Modal(document.getElementById('shipmentDetailsModal'));
|
||||||
let content = document.getElementById('shipmentDetailsContent');
|
let content = document.getElementById('shipmentDetailsContent');
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ use App\Http\Controllers\Admin\AdminMarkListController;
|
|||||||
use App\Http\Controllers\Admin\AdminOrderController;
|
use App\Http\Controllers\Admin\AdminOrderController;
|
||||||
use App\Http\Controllers\Admin\ShipmentController;
|
use App\Http\Controllers\Admin\ShipmentController;
|
||||||
use App\Http\Controllers\Admin\AdminInvoiceController;
|
use App\Http\Controllers\Admin\AdminInvoiceController;
|
||||||
|
use App\Http\Controllers\Admin\AdminCustomerController;
|
||||||
|
|
||||||
// -------------------------
|
// -------------------------
|
||||||
// Default Front Page
|
// Default Front Page
|
||||||
@@ -33,7 +34,7 @@ Route::prefix('admin')->middleware('auth:admin')->group(function () {
|
|||||||
Route::get('/dashboard', [AdminOrderController::class, 'index'])->name('admin.dashboard');
|
Route::get('/dashboard', [AdminOrderController::class, 'index'])->name('admin.dashboard');
|
||||||
//Route::get('/shipments', fn() => view('admin.shipments'))->name('admin.shipments');
|
//Route::get('/shipments', fn() => view('admin.shipments'))->name('admin.shipments');
|
||||||
//Route::get('/invoice', fn() => view('admin.invoice'))->name('admin.invoice');
|
//Route::get('/invoice', fn() => view('admin.invoice'))->name('admin.invoice');
|
||||||
Route::get('/customers', fn() => view('admin.customers'))->name('admin.customers');
|
//Route::get('/customers', fn() => view('admin.customers'))->name('admin.customers');
|
||||||
Route::get('/reports', fn() => view('admin.reports'))->name('admin.reports');
|
Route::get('/reports', fn() => view('admin.reports'))->name('admin.reports');
|
||||||
Route::get('/chat-support', fn() => view('admin.chat_support'))->name('admin.chat_support');
|
Route::get('/chat-support', fn() => view('admin.chat_support'))->name('admin.chat_support');
|
||||||
|
|
||||||
@@ -116,7 +117,21 @@ Route::prefix('admin')->middleware('auth:admin')->group(function () {
|
|||||||
//Add New Invoice
|
//Add New Invoice
|
||||||
Route::get('/admin/invoices/create', [InvoiceController::class, 'create'])->name('admin.invoices.create');
|
Route::get('/admin/invoices/create', [InvoiceController::class, 'create'])->name('admin.invoices.create');
|
||||||
|
|
||||||
|
// customer
|
||||||
|
Route::get('/customers', [AdminCustomerController::class, 'index'])
|
||||||
|
->name('admin.customers.index');
|
||||||
|
|
||||||
|
Route::get('/customers/{id}/view', [AdminCustomerController::class, 'view'])
|
||||||
|
->name('admin.customers.view');
|
||||||
|
|
||||||
|
Route::post('/customers/{id}/status', [AdminCustomerController::class, 'toggleStatus'])
|
||||||
|
->name('admin.customers.status');
|
||||||
|
|
||||||
|
|
||||||
|
Route::get('/customers/add', [AdminCustomerController::class, 'create'])
|
||||||
|
->name('admin.customers.add');
|
||||||
|
|
||||||
|
Route::post('/customers/store', [AdminCustomerController::class, 'store'])
|
||||||
|
->name('admin.customers.store');
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user