user roles, bug fixes
This commit is contained in:
@@ -16,7 +16,6 @@ use Illuminate\Support\Str;
|
||||
use Barryvdh\DomPDF\Facade\Pdf;
|
||||
|
||||
|
||||
|
||||
class ClientsController extends Controller
|
||||
{
|
||||
public function index(){
|
||||
@@ -890,7 +889,7 @@ class ClientsController extends Controller
|
||||
'invoice_amount' => 'required|numeric',
|
||||
'invoice_date' => 'required|date',
|
||||
'invoice_status' => 'required|string',
|
||||
'remarks' => 'sometimes|string'
|
||||
'remarks' => 'nullable|string'
|
||||
]);
|
||||
|
||||
// Find the record and update it
|
||||
|
||||
@@ -118,8 +118,12 @@ class NetworkOperatorsController extends Controller
|
||||
public function show($id)
|
||||
{
|
||||
$operator = NetworkOperator::findOrFail($id);
|
||||
|
||||
// Fetch all services for the dropdown
|
||||
$allServices = \App\Models\Service::orderBy('name')->get();
|
||||
|
||||
$staffMembers = \App\Models\StaffMember::orderBy('name')->get();
|
||||
return view('network_operators.show', compact('operator', 'staffMembers'));
|
||||
return view('network_operators.show', compact('operator', 'staffMembers', 'allServices'));
|
||||
}
|
||||
|
||||
public function storeConnection(Request $request, $operatorId)
|
||||
@@ -169,6 +173,42 @@ class NetworkOperatorsController extends Controller
|
||||
'connection' => $connection
|
||||
]);
|
||||
}
|
||||
public function storeNote(Request $request, $operatorId)
|
||||
{
|
||||
$request->validate([
|
||||
'notes_body' => 'required|string',
|
||||
'services' => 'nullable|string|max:200'
|
||||
]);
|
||||
|
||||
$serviceName = null;
|
||||
if ($request->filled('services')) {
|
||||
// This will find the service if it exists, or create a new one in the database if the user typed a new tag
|
||||
$service = \App\Models\Service::firstOrCreate([
|
||||
'name' => trim($request->services)
|
||||
]);
|
||||
$serviceName = $service->name;
|
||||
}
|
||||
|
||||
\App\Models\MnoNote::create([
|
||||
'mno_id' => $operatorId,
|
||||
'services' => $request->services ?? '',
|
||||
'user_id' => auth()->id(), // Automatically assign the logged-in user
|
||||
'notes_body' => $request->notes_body,
|
||||
]);
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => 'Note added successfully!'
|
||||
]);
|
||||
}
|
||||
|
||||
public function destroyNote($id)
|
||||
{
|
||||
$note = \App\Models\MnoNote::findOrFail($id);
|
||||
$note->delete();
|
||||
|
||||
return redirect()->back()->with('success', 'Note deleted successfully.');
|
||||
}
|
||||
|
||||
public function destroyConnection($id)
|
||||
{
|
||||
|
||||
@@ -45,11 +45,16 @@ class StaffMembersController extends Controller
|
||||
|
||||
// dd($staffMembers);
|
||||
// dd(\Auth::user()->designation);
|
||||
|
||||
$systemRoles = \App\Models\ErpSystemRole::orderBy('name')->get();
|
||||
|
||||
|
||||
$data = [
|
||||
'staffMembers' => $staffMembers,
|
||||
'departments' => $departments,
|
||||
'allstaffMembers' => $allstaffMembers,
|
||||
'countries' => $countries,
|
||||
'systemRoles' => $systemRoles
|
||||
];
|
||||
|
||||
|
||||
@@ -73,12 +78,17 @@ class StaffMembersController extends Controller
|
||||
$validated['profile_pic'] = $request->file('photo')->store('staff_profiles', 'public');
|
||||
}
|
||||
|
||||
// $user->roles()->syncWithoutDetaching([$roleId]); // Add without removing existing roles
|
||||
// $user->roles()->sync($roleIds); // Replace the user's role set
|
||||
|
||||
$validated['created_by'] = auth()->id() ?? 1;
|
||||
$validated['password'] = Hash::make('default_password');
|
||||
$validated['hire_date'] = now();
|
||||
|
||||
StaffMember::create($validated);
|
||||
|
||||
if ($request->has('roles')) {
|
||||
$staff->roles()->sync($request->roles);
|
||||
}
|
||||
return response()->json(['success' => true, 'message' => 'Staff member added successfully!']);
|
||||
}
|
||||
|
||||
@@ -107,6 +117,8 @@ class StaffMembersController extends Controller
|
||||
$validated['modified_by'] = auth()->id() ?? 1;
|
||||
|
||||
$staff->update($validated);
|
||||
$staff->roles()->sync($request->roles ?? []);
|
||||
|
||||
|
||||
return response()->json(['success' => true, 'message' => 'Staff member updated successfully!']);
|
||||
}
|
||||
@@ -152,7 +164,7 @@ class StaffMembersController extends Controller
|
||||
}
|
||||
public function showDetails($id)
|
||||
{
|
||||
$staff = StaffMember::with('dependents')->findOrFail($id);
|
||||
$staff = StaffMember::with(['dependents', 'roles'])->findOrFail($id);
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
|
||||
28
app/Http/Middleware/CheckRole.php
Normal file
28
app/Http/Middleware/CheckRole.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class CheckRole
|
||||
{
|
||||
public function handle(Request $request, Closure $next, ...$roles)
|
||||
{
|
||||
// Check if the user is logged in
|
||||
if (!auth()->check()) {
|
||||
return redirect('login');
|
||||
}
|
||||
|
||||
$user = auth()->user(); // Assuming StaffMember acts as your Authenticatable user
|
||||
|
||||
// Check if the user has any of the allowed roles
|
||||
foreach ($roles as $role) {
|
||||
if ($user->hasRole($role)) {
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
|
||||
abort(403, 'Unauthorized action.');
|
||||
}
|
||||
}
|
||||
17
app/Models/ErpSystemRole.php
Normal file
17
app/Models/ErpSystemRole.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
use App\Models\StaffMember;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class ErpSystemRole extends Model
|
||||
{
|
||||
protected $guarded = array('id');
|
||||
public $table = "system_roles";
|
||||
|
||||
|
||||
public function staffmembers(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(StaffMember::class, 'user_roles', 'system_role_id', 'staff_member_id');
|
||||
}
|
||||
}
|
||||
@@ -18,4 +18,13 @@ class Mnonote extends Model
|
||||
public function created_by_info(){
|
||||
return $this->hasOne('App\Models\StaffMember', 'id', 'user_id');
|
||||
}
|
||||
public function operator()
|
||||
{
|
||||
return $this->belongsTo(NetworkOperator::class, 'mno_id', 'id');
|
||||
}
|
||||
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'user_id', 'id');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,10 @@ class NetworkOperator extends Model
|
||||
{
|
||||
return $this->hasMany(NetworkOperatorDocument::class);
|
||||
}
|
||||
public function notes()
|
||||
{
|
||||
return $this->hasMany(\App\Models\Mnonote::class, 'mno_id', 'id')->latest();
|
||||
}
|
||||
/*
|
||||
public function setBuyingRateAttribute($value)
|
||||
{
|
||||
|
||||
@@ -6,6 +6,8 @@ use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use App\Traits\LogsActivity;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
use App\Models\ErpSystemRole;
|
||||
|
||||
class StaffMember extends Authenticatable
|
||||
{
|
||||
@@ -35,7 +37,22 @@ class StaffMember extends Authenticatable
|
||||
public function department(){
|
||||
return $this->hasOne('App\Models\Department', 'id', 'department_id');
|
||||
}
|
||||
// public function designation(){
|
||||
// return $this->hasOne('App\Models\Department', 'id', 'department_id');
|
||||
// }
|
||||
|
||||
public function roles(): BelongsToMany
|
||||
{
|
||||
// Parameter order: Related Model, Pivot Table Name, Foreign Key on Pivot, Related Key on Pivot
|
||||
return $this->belongsToMany(ErpSystemRole::class, 'user_roles', 'staff_member_id', 'system_role_id');
|
||||
}
|
||||
|
||||
public function hasRole(string $role): bool
|
||||
{
|
||||
return $this->roles()->where('name', $role)->exists();
|
||||
}
|
||||
|
||||
|
||||
// 4. Cast dates correctly
|
||||
protected $casts = [
|
||||
// 'dob' => 'date',
|
||||
|
||||
@@ -14,6 +14,9 @@ return Application::configure(basePath: dirname(__DIR__))
|
||||
)
|
||||
->withMiddleware(function (Middleware $middleware) {
|
||||
//
|
||||
$middleware->alias([
|
||||
'role' => \App\Http\Middleware\CheckRole::class,
|
||||
]);
|
||||
})
|
||||
->withExceptions(function (Exceptions $exceptions) {
|
||||
$exceptions->render(function (HttpException $e, Request $request) {
|
||||
|
||||
@@ -271,4 +271,88 @@ $(document).ready(function() {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
// ==========================================
|
||||
// 4. NOTES LOGIC
|
||||
// ==========================================
|
||||
|
||||
// Initialize Select2 with the 'tags' feature enabled
|
||||
$('#note_services').select2({
|
||||
placeholder: "Select or type a new service...",
|
||||
allowClear: true,
|
||||
width: '100%',
|
||||
dropdownParent: $('#addNoteModal'),
|
||||
tags: true // This allows users to type and submit custom entries
|
||||
});
|
||||
|
||||
$('#addNoteForm').on('submit', function(e) {
|
||||
// ... (keep your existing ajax submit logic here) ...
|
||||
});
|
||||
|
||||
$('#addNoteModal').on('hidden.bs.modal', function () {
|
||||
$('#addNoteForm')[0].reset();
|
||||
$('#noteModalAlert').html('');
|
||||
$('#addNoteForm').find('.is-invalid').removeClass('is-invalid');
|
||||
$('#addNoteForm').find('.invalid-feedback').remove();
|
||||
|
||||
// Clear the Select2 dropdown properly
|
||||
$('#note_services').val(null).trigger('change');
|
||||
});
|
||||
$('#addNoteForm').on('submit', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
let $form =$(this);
|
||||
let $submitBtn =$('#btnSubmitNote');
|
||||
let originalText = $submitBtn.html();
|
||||
let $alertBox =$('#noteModalAlert');
|
||||
|
||||
$submitBtn.html('<span class="spinner-border spinner-border-sm me-2"></span>Saving...').prop('disabled', true);
|
||||
$alertBox.html('');$form.find('.is-invalid').removeClass('is-invalid');
|
||||
|
||||
$.ajax({
|
||||
url: $form.attr('action'),
|
||||
type: 'POST',
|
||||
data: $form.serialize(),
|
||||
success: function(response) {
|
||||
if (response.success) {
|
||||
$('#addNoteModal').modal('hide');
|
||||
Swal.fire({
|
||||
icon: 'success',
|
||||
title: 'Saved!',
|
||||
text: response.message,
|
||||
confirmButtonColor: '#5c4df0'
|
||||
}).then(() => location.reload());
|
||||
}
|
||||
},
|
||||
error: function(xhr) {
|
||||
if (xhr.status === 422) {
|
||||
let errors = xhr.responseJSON.errors;
|
||||
$alertBox.html(`
|
||||
<div class="alert alert-danger d-flex align-items-center py-2 px-3 small" role="alert">
|
||||
<i class="bi bi-exclamation-triangle-fill me-2"></i>
|
||||
<div>Please fill in the required fields.</div>
|
||||
</div>
|
||||
`);
|
||||
$.each(errors, function(key, value) {
|
||||
let $input =$form.find('[name="' + key + '"]');
|
||||
if ($input.length) {
|
||||
$input.addClass('is-invalid');$input.parent().append('<div class="invalid-feedback d-block fw-medium small">' + value[0] + '</div>');
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
complete: function() {
|
||||
$submitBtn.html(originalText).prop('disabled', false);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$('#addNoteModal').on('hidden.bs.modal', function () {
|
||||
$('#addNoteForm')[0].reset();
|
||||
$('#noteModalAlert').html('');
|
||||
$('#addNoteForm').find('.is-invalid').removeClass('is-invalid');
|
||||
$('#addNoteForm').find('.invalid-feedback').remove();
|
||||
});
|
||||
});
|
||||
@@ -255,4 +255,42 @@
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Add Note Modal -->
|
||||
<div class="modal fade" id="addNoteModal" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header bg-light border-bottom-0">
|
||||
<h5 class="modal-title fw-bold">Add Internal Note</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<form id="addNoteForm" action="{{ route('mnos.notes.store', $operator->id) }}" method="POST">
|
||||
@csrf
|
||||
<div class="modal-body p-4">
|
||||
<div id="noteModalAlert"></div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label text-secondary fw-semibold small">Related Service / Topic (Optional)</label>
|
||||
<select name="services" id="note_services" class="form-select" style="width: 100%;">
|
||||
<option value=""></option>
|
||||
@foreach($allServices as $svc)
|
||||
<option value="{{ $svc->name }}">{{ $svc->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label text-secondary fw-semibold small">Note Detail *</label>
|
||||
<textarea name="notes_body" class="form-control" rows="4" required placeholder="Log call notes, updates, or instructions here..."></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer bg-light border-top-0">
|
||||
<button type="button" class="btn btn-outline-secondary btn-sm" data-bs-dismiss="modal">Cancel</button>
|
||||
<button type="submit" id="btnSubmitNote" class="btn text-white btn-sm fw-bold px-4" style="background-color: #5c4df0;">Save Note</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,7 +1,31 @@
|
||||
@extends('layouts.masterbeta')
|
||||
|
||||
@section('title', 'Cick ERP - ' . $operator->name)
|
||||
|
||||
@push('styles')
|
||||
<link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" />
|
||||
<style>
|
||||
/* Keep your existing styles here... */
|
||||
|
||||
/* Select2 Bootstrap 5 / Custom tweaks */
|
||||
.select2-container .select2-selection--single {
|
||||
height: 38px;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 0.375rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.select2-container--default .select2-selection--single .select2-selection__arrow {
|
||||
height: 36px;
|
||||
}
|
||||
|
||||
.network-badge { font-size: 0.65rem; padding: 0.2rem 0.4rem; border-radius: 4px; border: 1px solid #e2e8f0; font-weight: 600; display: inline-flex; align-items: center; gap: 3px; }
|
||||
.net-approved { background-color: rgba(16, 185, 129, 0.1); color: #10b981; border-color: rgba(16, 185, 129, 0.2); }
|
||||
.net-pending { background-color: rgba(245, 158, 11, 0.1); color: #f59e0b; border-color: rgba(245, 158, 11, 0.2); }
|
||||
.net-rejected { background-color: rgba(239, 68, 68, 0.1); color: #ef4444; border-color: rgba(239, 68, 68, 0.2); }
|
||||
|
||||
.sender-id-text { font-family: monospace; font-size: 1.1rem; letter-spacing: 1px; color: #111425; }
|
||||
</style>
|
||||
@endpush
|
||||
@section('breadcrumbs')
|
||||
<a href="{{ url('/') }}" class="text-secondary text-decoration-none"><i class="bi bi-house me-2"></i></a>
|
||||
<i class="bi bi-chevron-right text-secondary me-2" style="font-size: 0.8rem;"></i>
|
||||
@@ -303,6 +327,44 @@
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!-- Notes Section -->
|
||||
<div class="content-card p-4 mb-4">
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<h5 class="fw-bold mb-0 text-secondary" style="font-size: 0.9rem; text-transform: uppercase; letter-spacing: 0.5px;">
|
||||
<i class="bi bi-journal-text me-2 text-primary"></i> Internal Notes & Activity
|
||||
</h5>
|
||||
<button type="button" class="btn btn-sm text-white fw-bold" style="background-color: #5c4df0;" data-bs-toggle="modal" data-bs-target="#addNoteModal">
|
||||
<i class="bi bi-plus-lg me-1"></i> Add Note
|
||||
</button>
|
||||
</div>
|
||||
<hr class="text-muted opacity-25">
|
||||
|
||||
<div class="mt-3">
|
||||
@forelse($operator->notes as $note)
|
||||
<div class="border-bottom pb-3 mb-3">
|
||||
<div class="d-flex justify-content-between align-items-start">
|
||||
<div>
|
||||
<span class="fw-bold text-dark">{{ $note->user->name ?? 'System User' }}</span>
|
||||
<span class="text-secondary small ms-2"><i class="bi bi-clock me-1"></i>{{ $note->created_at->format('d M Y, h:i A') }}</span>
|
||||
@if($note->services)
|
||||
<span class="badge bg-light text-primary border ms-2">{{ $note->services }}</span>
|
||||
@endif
|
||||
</div>
|
||||
<form action="{{ route('mnos.notes.destroy', $note->id) }}" method="POST" class="d-inline">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<button type="submit" class="btn btn-sm btn-light text-danger border-0 p-1" onclick="return confirm('Delete this note?')">
|
||||
<i class="bi bi-trash"></i>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
<div class="text-dark mt-2" style="font-size: 0.9rem; white-space: pre-wrap;">{{ $note->notes_body }}</div>
|
||||
</div>
|
||||
@empty
|
||||
<div class="text-center text-secondary py-4">No notes added yet.</div>
|
||||
@endforelse
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -317,6 +379,7 @@
|
||||
const baseUrl = "{{ url('/') }}";
|
||||
const operatorId = "{{ $operator->id }}";
|
||||
</script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
|
||||
<script src="{{ asset('public/assets/js/mno-show.js') }}"></script>
|
||||
|
||||
|
||||
|
||||
@@ -149,7 +149,8 @@
|
||||
data-department_id="{{ $staff->department_id }}"
|
||||
data-location_country="{{ $staff->location_country }}"
|
||||
data-status="{{ $staff->status }}"
|
||||
data-pic="{{ $staff->profile_pic ? asset('public/storage/' . $staff->profile_pic) : '' }}">
|
||||
data-pic="{{ $staff->profile_pic ? asset('public/storage/' . $staff->profile_pic) : '' }}"
|
||||
data-roles="{{ $staff->roles->pluck('id')->toJson() }}">
|
||||
<i class="bi bi-pencil me-1"></i> Edit
|
||||
</button>
|
||||
|
||||
@@ -258,6 +259,16 @@
|
||||
<option value="INACTIVE">Inactive</option>
|
||||
</select>
|
||||
</div>
|
||||
<!-- Insert this directly after the Status col-md-4 block -->
|
||||
<div class="col-12 mt-3 pt-3 border-top">
|
||||
<label class="form-label text-secondary fw-semibold small">System Roles & Permissions</label>
|
||||
<select class="form-select" id="staffRoles" name="roles[]" multiple="multiple">
|
||||
@foreach($systemRoles as $role)
|
||||
<option value="{{ $role->id }}">{{ $role->label }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<div class="form-text text-muted" style="font-size: 0.75rem;">Leave blank for standard employee access.</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer bg-light">
|
||||
@@ -318,6 +329,19 @@
|
||||
<div class="text-secondary small fw-semibold">ANNUAL LEAVE BALANCE</div>
|
||||
<div class="fw-medium text-dark small"><span id="viewStaffLeaveBalance">0</span> Days</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="text-secondary small fw-semibold">ANNUAL LEAVE BALANCE</div>
|
||||
<div class="fw-medium text-dark small"><span id="viewStaffLeaveBalance">0</span> Days</div>
|
||||
</div>
|
||||
|
||||
<!-- NEW ROLES SECTION -->
|
||||
<div class="col-12 mt-3 pt-3 border-top">
|
||||
<div class="text-secondary small fw-semibold mb-2">SYSTEM ROLES</div>
|
||||
<div id="viewStaffRoles" class="d-flex flex-wrap gap-1">
|
||||
<span class="text-muted small">Loading...</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- END ROLES SECTION -->
|
||||
</div>
|
||||
|
||||
<!-- Emergency Contacts Table Section -->
|
||||
@@ -423,6 +447,12 @@
|
||||
width: '100%',
|
||||
dropdownParent: $('#staffModal')
|
||||
});
|
||||
$('#staffRoles').select2({
|
||||
placeholder: "Select applicable roles...",
|
||||
allowClear: true,
|
||||
width: '100%',
|
||||
dropdownParent: $('#staffModal')
|
||||
});
|
||||
|
||||
// Handle Photo Preview
|
||||
$('#staffPhoto').on('change', function(e) {
|
||||
@@ -441,7 +471,7 @@
|
||||
$('#staffId').val('');
|
||||
$('#avatarPreview').html('<i class="bi bi-person text-secondary"></i>');
|
||||
$('#staffCountry').val(null).trigger('change');
|
||||
|
||||
$('#staffRoles').val(null).trigger('change');
|
||||
$('#staffModalTitle').text('Add New Employee');
|
||||
$('#btnSubmitStaff').html('<i class="bi bi-save me-2"></i>Save Employee');
|
||||
staffModal.show();
|
||||
@@ -460,6 +490,7 @@
|
||||
$('#staffPhone').val(data.phone);
|
||||
$('#staffDept').val(data.department_id);
|
||||
$('#staffCountry').val(data.location_country).trigger('change');
|
||||
$('#staffRoles').val(data.roles).trigger('change');
|
||||
$('#staffStatus').val(data.status);
|
||||
|
||||
if (data.pic) {
|
||||
@@ -559,7 +590,7 @@
|
||||
$('#viewStaffLocation').text('');
|
||||
$('#viewStaffLeaveBalance').text('');
|
||||
$('#viewStaffDependentsList').html('<tr><td colspan="4" class="text-center text-secondary py-3">Loading...</td></tr>');
|
||||
|
||||
$('#viewStaffRoles').html('<span class="text-muted small">Loading...</span>');
|
||||
viewModal.show();
|
||||
|
||||
// Fetch staff details via AJAX
|
||||
@@ -590,6 +621,15 @@
|
||||
let initials = staff.name.split(' ').map(n => n[0]).join('').substring(0, 2).toUpperCase();
|
||||
$('#viewStaffAvatarContainer').html(`<div class="rounded-circle bg-light text-primary fw-bold d-flex align-items-center justify-content-center border" style="width: 70px; height: 70px; font-size: 1.5rem;">${initials}</div>`);
|
||||
}
|
||||
let rolesHtml = '';
|
||||
if (staff.roles && staff.roles.length > 0) {
|
||||
staff.roles.forEach(role => {
|
||||
rolesHtml += `<span class="badge bg-primary bg-opacity-10 text-primary border border-primary-subtle px-2 py-1">${role.label}</span>`;
|
||||
});
|
||||
} else {
|
||||
rolesHtml = `<span class="text-muted small fst-italic">Standard Employee Access</span>`;
|
||||
}
|
||||
$('#viewStaffRoles').html(rolesHtml);
|
||||
|
||||
// Dependents Table population
|
||||
let dependentsHtml = '';
|
||||
|
||||
@@ -159,6 +159,9 @@ Route::middleware(['auth'])->group(function () {
|
||||
Route::post('/vpns/download', [App\Http\Controllers\VpnConfigController::class, 'download'])->name('vpns.download');
|
||||
|
||||
|
||||
Route::post('/mnos/{operatorId}/notes', [App\Http\Controllers\NetworkOperatorsController::class, 'storeNote'])->name('mnos.notes.store');
|
||||
Route::delete('/mnos/notes/{id}', [App\Http\Controllers\NetworkOperatorsController::class, 'destroyNote'])->name('mnos.notes.destroy');
|
||||
|
||||
|
||||
Route::get('/activity-logs', [App\Http\Controllers\ActivityLogController::class, 'index'])->name('activity-logs.index');
|
||||
|
||||
|
||||
Reference in New Issue
Block a user