diff --git a/app/Http/Controllers/ClientsController.php b/app/Http/Controllers/ClientsController.php index 38a9733..e5c054d 100644 --- a/app/Http/Controllers/ClientsController.php +++ b/app/Http/Controllers/ClientsController.php @@ -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 diff --git a/app/Http/Controllers/NetworkOperatorsController.php b/app/Http/Controllers/NetworkOperatorsController.php index b675bb1..1dbe6cb 100644 --- a/app/Http/Controllers/NetworkOperatorsController.php +++ b/app/Http/Controllers/NetworkOperatorsController.php @@ -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) { diff --git a/app/Http/Controllers/StaffMembersController.php b/app/Http/Controllers/StaffMembersController.php index ff64c52..3c97b90 100644 --- a/app/Http/Controllers/StaffMembersController.php +++ b/app/Http/Controllers/StaffMembersController.php @@ -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, diff --git a/app/Http/Middleware/CheckRole.php b/app/Http/Middleware/CheckRole.php new file mode 100644 index 0000000..5fedf7c --- /dev/null +++ b/app/Http/Middleware/CheckRole.php @@ -0,0 +1,28 @@ +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.'); + } +} \ No newline at end of file diff --git a/app/Models/ErpSystemRole.php b/app/Models/ErpSystemRole.php new file mode 100644 index 0000000..35fb16b --- /dev/null +++ b/app/Models/ErpSystemRole.php @@ -0,0 +1,17 @@ +belongsToMany(StaffMember::class, 'user_roles', 'system_role_id', 'staff_member_id'); + } +} diff --git a/app/Models/Mnonote.php b/app/Models/Mnonote.php index caf6dd7..7d04d06 100644 --- a/app/Models/Mnonote.php +++ b/app/Models/Mnonote.php @@ -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'); + } } diff --git a/app/Models/NetworkOperator.php b/app/Models/NetworkOperator.php index 29eeb87..b7a0b1f 100755 --- a/app/Models/NetworkOperator.php +++ b/app/Models/NetworkOperator.php @@ -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) { diff --git a/app/Models/StaffMember.php b/app/Models/StaffMember.php index cbc07a8..27bda70 100644 --- a/app/Models/StaffMember.php +++ b/app/Models/StaffMember.php @@ -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', diff --git a/bootstrap/app.php b/bootstrap/app.php index bdb67b2..e4ee53c 100644 --- a/bootstrap/app.php +++ b/bootstrap/app.php @@ -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) { diff --git a/public/assets/js/mno-show.js b/public/assets/js/mno-show.js index 4e67236..16f3aac 100644 --- a/public/assets/js/mno-show.js +++ b/public/assets/js/mno-show.js @@ -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('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(` +