employee update
This commit is contained in:
@@ -55,6 +55,7 @@ class AdminStaffController extends Controller
|
|||||||
DB::beginTransaction();
|
DB::beginTransaction();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// 1️⃣ Create staff WITHOUT employee_id (ID not available yet)
|
||||||
$admin = Admin::create([
|
$admin = Admin::create([
|
||||||
'name' => $request->name,
|
'name' => $request->name,
|
||||||
'email' => $request->email,
|
'email' => $request->email,
|
||||||
@@ -69,23 +70,33 @@ class AdminStaffController extends Controller
|
|||||||
'status' => $request->status,
|
'status' => $request->status,
|
||||||
'additional_info' => $request->additional_info,
|
'additional_info' => $request->additional_info,
|
||||||
|
|
||||||
'username' => $request->username,
|
// username may be NULL here
|
||||||
|
'username' => $request->username ?: null,
|
||||||
'password' => Hash::make($request->password),
|
'password' => Hash::make($request->password),
|
||||||
'type' => 'staff',
|
'type' => 'staff',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Generate EMPLOYEE ID using admin ID (safe)
|
// 2️⃣ Generate EMPLOYEE ID
|
||||||
$employeeId = 'EMP' . str_pad($admin->id, 4, '0', STR_PAD_LEFT);
|
$employeeId = 'EMP' . str_pad($admin->id, 4, '0', STR_PAD_LEFT);
|
||||||
$admin->update(['employee_id' => $employeeId]);
|
|
||||||
|
|
||||||
// Assign permissions (if any)
|
// 3️⃣ Auto-generate username if left blank
|
||||||
|
$username = $request->username ?: strtolower($employeeId);
|
||||||
|
|
||||||
|
// 4️⃣ Update employee_id + username together
|
||||||
|
$admin->update([
|
||||||
|
'employee_id' => $employeeId,
|
||||||
|
'username' => $username,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 5️⃣ Assign permissions (if any)
|
||||||
if ($request->permissions) {
|
if ($request->permissions) {
|
||||||
$admin->givePermissionTo($request->permissions);
|
$admin->givePermissionTo($request->permissions);
|
||||||
}
|
}
|
||||||
|
|
||||||
DB::commit();
|
DB::commit();
|
||||||
|
|
||||||
return redirect()->route('admin.staff.index')
|
return redirect()
|
||||||
|
->route('admin.staff.index')
|
||||||
->with('success', 'Staff created successfully.');
|
->with('success', 'Staff created successfully.');
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
@@ -94,6 +105,7 @@ class AdminStaffController extends Controller
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function edit($id)
|
public function edit($id)
|
||||||
{
|
{
|
||||||
$staff = Admin::where('type', 'staff')->findOrFail($id);
|
$staff = Admin::where('type', 'staff')->findOrFail($id);
|
||||||
|
|||||||
@@ -15,7 +15,12 @@ class UserRequestController extends Controller
|
|||||||
public function index()
|
public function index()
|
||||||
{
|
{
|
||||||
$requests = CustomerRequest::orderBy('id', 'desc')->get();
|
$requests = CustomerRequest::orderBy('id', 'desc')->get();
|
||||||
return view('admin.requests', compact('requests'));
|
$pendingProfileUpdates = \App\Models\UpdateRequest::where('status', 'pending')->count();
|
||||||
|
|
||||||
|
return view('admin.requests', compact(
|
||||||
|
'requests',
|
||||||
|
'pendingProfileUpdates'
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Approve user request
|
// Approve user request
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ class Admin extends Authenticatable
|
|||||||
'name', 'email', 'password', 'username',
|
'name', 'email', 'password', 'username',
|
||||||
'phone', 'emergency_phone', 'address',
|
'phone', 'emergency_phone', 'address',
|
||||||
'role', 'department', 'designation', 'joining_date',
|
'role', 'department', 'designation', 'joining_date',
|
||||||
'status', 'additional_info', 'type', // admin/staff indicator
|
'status', 'additional_info', 'type','employee_id', // admin/staff indicator
|
||||||
];
|
];
|
||||||
|
|
||||||
protected $hidden = [
|
protected $hidden = [
|
||||||
|
|||||||
BIN
public/invoices/invoice-INV-2025-000016.pdf
Normal file
BIN
public/invoices/invoice-INV-2025-000016.pdf
Normal file
Binary file not shown.
Binary file not shown.
@@ -201,7 +201,7 @@
|
|||||||
background: #fff;
|
background: #fff;
|
||||||
padding: 10px 18px !important;
|
padding: 10px 18px !important;
|
||||||
position: relative;
|
position: relative;
|
||||||
height: 48px;
|
height: 65px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
|
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
|
||||||
}
|
}
|
||||||
@@ -221,6 +221,32 @@
|
|||||||
font-size: 1.06rem;
|
font-size: 1.06rem;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ================================
|
||||||
|
HEADER NOTIFICATION BADGE FIX
|
||||||
|
================================ */
|
||||||
|
|
||||||
|
/* Target ONLY badge inside bell icon */
|
||||||
|
header .bi-bell {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Override broken global badge styles */
|
||||||
|
header .bi-bell .badge {
|
||||||
|
width: 22px !important;
|
||||||
|
height: 22px !important;
|
||||||
|
min-width: 22px !important;
|
||||||
|
padding: 0 !important;
|
||||||
|
font-size: 12px !important;
|
||||||
|
line-height: 22px !important;
|
||||||
|
border-radius: 50% !important;
|
||||||
|
display: inline-flex !important;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
animation: none !important;
|
||||||
|
box-shadow: 0 0 0 2px #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
@@ -285,12 +311,12 @@
|
|||||||
</a>
|
</a>
|
||||||
@endcan
|
@endcan
|
||||||
|
|
||||||
{{-- Profile Update Requests --}}
|
<!-- {{-- Profile Update Requests --}}
|
||||||
@can('request.update_profile')
|
@can('request.update_profile')
|
||||||
<a href="{{ route('admin.profile.requests') }}">
|
<a href="{{ route('admin.profile.requests') }}">
|
||||||
<i class="bi bi-person-lines-fill"></i> Profile Update Requests
|
<i class="bi bi-person-lines-fill"></i> Profile Update Requests
|
||||||
</a>
|
</a>
|
||||||
@endcan
|
@endcan -->
|
||||||
|
|
||||||
{{-- Staff (NO PERMISSION REQUIRED) --}}
|
{{-- Staff (NO PERMISSION REQUIRED) --}}
|
||||||
<a href="{{ route('admin.staff.index') }}" class="{{ request()->routeIs('admin.staff.*') ? 'active' : '' }}">
|
<a href="{{ route('admin.staff.index') }}" class="{{ request()->routeIs('admin.staff.*') ? 'active' : '' }}">
|
||||||
|
|||||||
@@ -10,70 +10,144 @@
|
|||||||
$currentPage = request()->get('page', 1);
|
$currentPage = request()->get('page', 1);
|
||||||
$currentPage = max(1, (int)$currentPage);
|
$currentPage = max(1, (int)$currentPage);
|
||||||
$total = $requests->count();
|
$total = $requests->count();
|
||||||
$totalPages = ceil($total / $perPage);
|
|
||||||
$currentItems = $requests->slice(($currentPage - 1) * $perPage, $perPage);
|
$currentItems = $requests->slice(($currentPage - 1) * $perPage, $perPage);
|
||||||
@endphp
|
@endphp
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.old-value { color: #6b7280; font-weight: 600; }
|
/* ===== Card Wrapper ===== */
|
||||||
.new-value { color: #111827; font-weight: 700; }
|
.request-card {
|
||||||
.changed { background: #fef3c7; padding: 6px; border-radius: 6px; }
|
background: #ffffff;
|
||||||
.box { padding: 10px 14px; border-radius: 8px; background: #f8fafc; margin-bottom: 10px; }
|
border-radius: 14px;
|
||||||
.diff-label { font-size: 13px; font-weight: 700; }
|
padding: 18px;
|
||||||
.actions { display: flex; gap: 10px; }
|
margin-bottom: 18px;
|
||||||
|
box-shadow: 0 6px 20px rgba(0,0,0,0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===== Header Row ===== */
|
||||||
|
.request-header {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 60px 1.5fr 1fr 1.2fr 1fr;
|
||||||
|
gap: 14px;
|
||||||
|
align-items: center;
|
||||||
|
border-bottom: 1px solid #e5e7eb;
|
||||||
|
padding-bottom: 12px;
|
||||||
|
margin-bottom: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.request-header strong {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===== Badges ===== */
|
||||||
|
.badge {
|
||||||
|
padding: 6px 14px;
|
||||||
|
font-size: 12px;
|
||||||
|
border-radius: 999px;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-pending {
|
||||||
|
background: #fff7ed;
|
||||||
|
color: #c2410c;
|
||||||
|
border: 1px solid #fed7aa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-approved {
|
||||||
|
background: #ecfdf5;
|
||||||
|
color: #047857;
|
||||||
|
border: 1px solid #a7f3d0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-rejected {
|
||||||
|
background: #fef2f2;
|
||||||
|
color: #b91c1c;
|
||||||
|
border: 1px solid #fecaca;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===== Action Buttons ===== */
|
||||||
|
.actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions .btn {
|
||||||
|
padding: 6px 14px;
|
||||||
|
font-size: 13px;
|
||||||
|
border-radius: 999px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===== Detail Grid ===== */
|
||||||
|
.detail-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
gap: 14px;
|
||||||
|
margin-top: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===== Detail Box ===== */
|
||||||
|
.detail-box {
|
||||||
|
background: #f8fafc;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 12px 14px;
|
||||||
|
border: 1px solid #e2e8f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-box.changed {
|
||||||
|
background: linear-gradient(145deg, #fff7ed, #ffedd5);
|
||||||
|
border-left: 4px solid #f59e0b;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-label {
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #334155;
|
||||||
|
}
|
||||||
|
|
||||||
|
.old {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #64748b;
|
||||||
|
}
|
||||||
|
|
||||||
|
.new {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #020617;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===== Responsive ===== */
|
||||||
|
@media (max-width: 992px) {
|
||||||
|
.request-header {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<h4 class="fw-bold my-3">Profile Update Requests ({{ $total }})</h4>
|
<h4 class="fw-bold my-3">Profile Update Requests ({{ $total }})</h4>
|
||||||
|
|
||||||
<div class="card mb-4 shadow-sm">
|
|
||||||
<div class="card-body pb-1">
|
|
||||||
|
|
||||||
<div class="table-responsive custom-table-wrapper">
|
|
||||||
<table class="table align-middle mb-0 custom-table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>#</th>
|
|
||||||
<th>User</th>
|
|
||||||
<th>Requested Changes</th>
|
|
||||||
<th>Status</th>
|
|
||||||
<th>Requested At</th>
|
|
||||||
<th>Actions</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
|
|
||||||
<tbody>
|
|
||||||
@foreach($currentItems as $index => $req)
|
@foreach($currentItems as $index => $req)
|
||||||
@php
|
@php
|
||||||
$user = $req->user;
|
$user = $req->user;
|
||||||
// FIX: Convert string to array
|
|
||||||
$newData = is_array($req->data) ? $req->data : json_decode($req->data, true);
|
$newData = is_array($req->data) ? $req->data : json_decode($req->data, true);
|
||||||
@endphp
|
@endphp
|
||||||
|
|
||||||
<tr>
|
<div class="request-card">
|
||||||
<td><strong>{{ ($currentPage - 1) * $perPage + $index + 1 }}</strong></td>
|
|
||||||
|
|
||||||
<td>
|
<!-- HEADER -->
|
||||||
|
<div class="request-header">
|
||||||
|
<strong>#{{ ($currentPage - 1) * $perPage + $index + 1 }}</strong>
|
||||||
|
|
||||||
|
<div>
|
||||||
<strong>{{ $user->customer_name }}</strong><br>
|
<strong>{{ $user->customer_name }}</strong><br>
|
||||||
<small>{{ $user->email }}</small><br>
|
<small>{{ $user->email }}</small><br>
|
||||||
<small>ID: {{ $user->customer_id }}</small>
|
<small>ID: {{ $user->customer_id }}</small>
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
@foreach($newData as $key => $newValue)
|
|
||||||
@php
|
|
||||||
$oldValue = $user->$key ?? '—';
|
|
||||||
$changed = $oldValue != $newValue;
|
|
||||||
@endphp
|
|
||||||
|
|
||||||
<div class="box {{ $changed ? 'changed' : '' }}">
|
|
||||||
<span class="diff-label">{{ ucfirst(str_replace('_',' ', $key)) }}:</span><br>
|
|
||||||
<span class="old-value">Old: {{ $oldValue }}</span><br>
|
|
||||||
<span class="new-value">New: {{ $newValue ?? '—' }}</span>
|
|
||||||
</div>
|
</div>
|
||||||
@endforeach
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
<div>
|
||||||
@if($req->status == 'pending')
|
@if($req->status == 'pending')
|
||||||
<span class="badge badge-pending">Pending</span>
|
<span class="badge badge-pending">Pending</span>
|
||||||
@elseif($req->status == 'approved')
|
@elseif($req->status == 'approved')
|
||||||
@@ -81,31 +155,57 @@
|
|||||||
@else
|
@else
|
||||||
<span class="badge badge-rejected">Rejected</span>
|
<span class="badge badge-rejected">Rejected</span>
|
||||||
@endif
|
@endif
|
||||||
</td>
|
</div>
|
||||||
|
|
||||||
<td>{{ $req->created_at->format('d M Y, h:i A') }}</td>
|
<div>{{ $req->created_at->format('d M Y, h:i A') }}</div>
|
||||||
|
|
||||||
<td class="actions">
|
<div class="actions">
|
||||||
@if($req->status == 'pending')
|
@if($req->status == 'pending')
|
||||||
<a href="{{ route('admin.profile.approve', $req->id) }}" class="btn btn-success btn-sm">
|
<a href="{{ route('admin.profile.approve', $req->id) }}" class="btn btn-success btn-sm">
|
||||||
<i class="bi bi-check-circle"></i> Approve
|
✔ Approve
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<a href="{{ route('admin.profile.reject', $req->id) }}" class="btn btn-danger btn-sm">
|
<a href="{{ route('admin.profile.reject', $req->id) }}" class="btn btn-danger btn-sm">
|
||||||
<i class="bi bi-x-circle"></i> Reject
|
✖ Reject
|
||||||
</a>
|
</a>
|
||||||
@else
|
@else
|
||||||
<span class="text-muted">Completed</span>
|
<span class="text-muted">Completed</span>
|
||||||
@endif
|
@endif
|
||||||
</td>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</tr>
|
<!-- DETAILS ROW 1 -->
|
||||||
|
<div class="detail-grid">
|
||||||
|
@foreach(['customer_name','company_name','email'] as $field)
|
||||||
|
@php
|
||||||
|
$old = $user->$field ?? '—';
|
||||||
|
$new = $newData[$field] ?? $old;
|
||||||
|
@endphp
|
||||||
|
<div class="detail-box {{ $old != $new ? 'changed' : '' }}">
|
||||||
|
<div class="detail-label">{{ ucfirst(str_replace('_',' ', $field)) }}</div>
|
||||||
|
<div class="old">Old: {{ $old }}</div>
|
||||||
|
<div class="new">New: {{ $new }}</div>
|
||||||
|
</div>
|
||||||
|
@endforeach
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- DETAILS ROW 2 -->
|
||||||
|
<div class="detail-grid">
|
||||||
|
@foreach(['mobile_no','address','pincode'] as $field)
|
||||||
|
|
||||||
|
@php
|
||||||
|
$old = $user->$field ?? '—';
|
||||||
|
$new = $newData[$field] ?? $old;
|
||||||
|
@endphp
|
||||||
|
<div class="detail-box {{ $old != $new ? 'changed' : '' }}">
|
||||||
|
<div class="detail-label">{{ ucfirst(str_replace('_',' ', $field)) }}</div>
|
||||||
|
<div class="old">Old: {{ $old }}</div>
|
||||||
|
<div class="new">New: {{ $new }}</div>
|
||||||
|
</div>
|
||||||
@endforeach
|
@endforeach
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
@endforeach
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
@endsection
|
@endsection
|
||||||
|
|||||||
@@ -307,14 +307,55 @@
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/* ==============================================
|
||||||
|
PROFILE UPDATE REQUEST BUTTON BADGE FIX
|
||||||
|
============================================== */
|
||||||
|
|
||||||
|
/* Ensure button is positioning context */
|
||||||
|
a.btn.btn-primary.position-relative {
|
||||||
|
position: relative;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fix badge inside Profile Update Requests button */
|
||||||
|
a.btn.btn-primary.position-relative .badge {
|
||||||
|
width: 30px !important;
|
||||||
|
height: 30px !important;
|
||||||
|
min-width: 30px !important;
|
||||||
|
padding: 0 !important;
|
||||||
|
font-size: 14px !important;
|
||||||
|
line-height: 30px !important;
|
||||||
|
border-radius: 50% !important;
|
||||||
|
display: inline-flex !important;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
animation: none !important;
|
||||||
|
box-shadow: 0 0 0 2px #ffffff;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<!-- Counts -->
|
<!-- Counts -->
|
||||||
<div class="d-flex justify-content-between align-items-center mb-2 mt-3">
|
<div class="d-flex justify-content-between align-items-center mb-2 mt-3">
|
||||||
<h4 class="fw-bold mb-0">User Requests (Total: {{ $total }})</h4>
|
<h4 class="fw-bold mb-0">User Requests (Total: {{ $total }})</h4>
|
||||||
|
|
||||||
|
@can('request.update_profile')
|
||||||
|
<a href="{{ route('admin.profile.requests') }}" class="btn btn-primary position-relative">
|
||||||
|
<i class="bi bi-person-lines-fill me-1"></i>
|
||||||
|
Profile Update Requests
|
||||||
|
|
||||||
|
@if($pendingProfileUpdates > 0)
|
||||||
|
<span class="position-absolute top-0 start-100 translate-middle badge rounded-pill bg-danger">
|
||||||
|
{{ $pendingProfileUpdates }}
|
||||||
|
</span>
|
||||||
|
@endif
|
||||||
|
</a>
|
||||||
|
@endcan
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<!-- Search + Table -->
|
<!-- Search + Table -->
|
||||||
<div class="card mb-4 shadow-sm">
|
<div class="card mb-4 shadow-sm">
|
||||||
<div class="card-body pb-1">
|
<div class="card-body pb-1">
|
||||||
|
|||||||
@@ -849,8 +849,7 @@
|
|||||||
name="username"
|
name="username"
|
||||||
value="{{ old('username') }}"
|
value="{{ old('username') }}"
|
||||||
class="form-input"
|
class="form-input"
|
||||||
placeholder="Leave blank to generate automatically"
|
placeholder="Leave blank to generate automatically">
|
||||||
required>
|
|
||||||
<div class="helper-text">
|
<div class="helper-text">
|
||||||
<span>ℹ️</span>
|
<span>ℹ️</span>
|
||||||
If left blank, username will be generated from employee ID
|
If left blank, username will be generated from employee ID
|
||||||
|
|||||||
Reference in New Issue
Block a user