From 757f90840441ab0b25acc8bec05cc68ece617d7f Mon Sep 17 00:00:00 2001 From: Kwesi Banson Jnr Date: Fri, 24 Apr 2026 12:09:36 +0000 Subject: [PATCH] added User management --- accounts.md | 1 + .../Controllers/ClientUsersController.php | 103 +++++ .../Controllers/ClientsLoginController.php | 4 +- .../Controllers/ClientsTrafficController.php | 2 +- app/Http/Middleware/RoleMiddleware.php | 24 ++ app/Models/ClientSession.php | 1 + bootstrap/app.php | 2 + config/permission.php | 206 ++++++++++ ..._04_23_164547_create_permission_tables.php | 134 ++++++ info.md | 24 ++ public/assets/js/traffic-mgt.js | 125 ++++++ public/assets/js/usermgt.js | 381 ++++++++---------- ...ex-test.blade.php => index-main.blade.php} | 160 +------- resources/views/client-users/index.blade.php | 327 +++++++++++++++ resources/views/layouts/master.blade.php | 17 +- routes/web.php | 11 +- 16 files changed, 1156 insertions(+), 366 deletions(-) create mode 100644 app/Http/Controllers/ClientUsersController.php create mode 100644 app/Http/Middleware/RoleMiddleware.php create mode 100644 config/permission.php create mode 100644 database/migrations/2026_04_23_164547_create_permission_tables.php create mode 100644 public/assets/js/traffic-mgt.js rename resources/views/client-traffic/{index-test.blade.php => index-main.blade.php} (56%) create mode 100644 resources/views/client-users/index.blade.php diff --git a/accounts.md b/accounts.md index ce90077..0bac4ae 100644 --- a/accounts.md +++ b/accounts.md @@ -13,6 +13,7 @@ - https://smsportal.clickmlapps.com/ - info@sunking.com - 847SIcgO9sUX + ### add - change pagination to 1000 from 20 - remove delivery receipt diff --git a/app/Http/Controllers/ClientUsersController.php b/app/Http/Controllers/ClientUsersController.php new file mode 100644 index 0000000..fb81c4d --- /dev/null +++ b/app/Http/Controllers/ClientUsersController.php @@ -0,0 +1,103 @@ + 'Users', + ]; + return view('client-users.index', $data); + + } + public function index() + { + $data = [ + 'page_title' => 'Users', + ]; + return view('client-users.index', $data); + } + + // Read: Fetch data for the jQuery table + // public function fetch() + // { + // $sessions = Models\ClientSession::orderBy('id', 'desc')->paginate(5); + // return response()->json(['sessions' => $sessions]); + // } + public function fetch(Request $request) + { + $query = Models\ClientSession::query(); + + // Check if the search parameter has a value + if ($request->has('search') && !empty($request->search)) { + $search = $request->search; + $query->where(function($q) use ($search) { + $q->where('email', 'LIKE', "%{$search}%") + ->orWhere('role', 'LIKE', "%{$search}%"); + }); + } + $sessions = $query->orderBy('id', 'desc')->paginate(5); + + return response()->json(['sessions' => $sessions]); + } + + + // Create: Store a new record + public function store(Request $request) + { + $request->validate([ + 'email' => 'required|email|unique:client_sessions,email', + 'role' => 'required|string', + 'password' => 'required|string' + ]); + + $session = Models\ClientSession::create([ + 'email' => $request->email, + 'role' => $request->role, + 'password' => Hash::make($request->password) + ]); + + return response()->json(['status' => 'success', 'message' => 'User created successfully!']); + } + + public function edit($id) + { + $session = Models\ClientSession::findOrFail($id); + return response()->json(['session' => $session]); + } + + public function update(Request $request, $id) + { + $request->validate([ + 'email' => 'required|email|unique:client_sessions,email,' . $id, + 'role' => 'required|string', + 'password' => 'required|string' + ]); + + $session = Models\ClientSession::findOrFail($id); + $session->update([ + 'email' => $request->email, + 'role' => $request->role, + 'password' => Hash::make($request->password) + ]); + + return response()->json(['status' => 'success', 'message' => 'User updated successfully!']); + } + + // Delete: Remove record + public function destroy($id) + { + Models\ClientSession::findOrFail($id)->delete(); + return response()->json(['status' => 'success', 'message' => 'User deleted successfully!']); + } +} diff --git a/app/Http/Controllers/ClientsLoginController.php b/app/Http/Controllers/ClientsLoginController.php index 93f70b1..08c463a 100644 --- a/app/Http/Controllers/ClientsLoginController.php +++ b/app/Http/Controllers/ClientsLoginController.php @@ -83,7 +83,6 @@ class ClientsLoginController extends Controller $logged_in = ''; $client = Models\ClientSession::where('email', $request->email)->first(); - // dd($client); if ($client == false) { return redirect()->back()->withErrors(['Invalid credentials']); } @@ -92,13 +91,14 @@ class ClientsLoginController extends Controller $result_arr = json_decode($result, true); $logged_in = $result_arr; - + // dd($logged_in); $request->session()->regenerate(true); $request->session()->put('current_user.user_id', $logged_in['id']); $request->session()->put('current_user.org_id', $logged_in['id']); $request->session()->put('current_user.name', $logged_in['name']); $request->session()->put('current_user.email', $logged_in['email']); + $request->session()->put('current_user.role', $client['role']); $request->session()->put('current_user.phoneNumber', $logged_in['phoneNumber']); $request->session()->put('current_user.status', $logged_in['status']); $request->session()->put('current_user.createdAt', $logged_in['createdAt']); diff --git a/app/Http/Controllers/ClientsTrafficController.php b/app/Http/Controllers/ClientsTrafficController.php index f0b6e61..b8ac6ee 100644 --- a/app/Http/Controllers/ClientsTrafficController.php +++ b/app/Http/Controllers/ClientsTrafficController.php @@ -68,7 +68,7 @@ class ClientsTrafficController extends Controller 'sms_units_arr' => $sms_units_arr, 'balance_arr' => $balance_arr ]; - return view('client-traffic.index-test', $data); + return view('client-traffic.index-main', $data); } public function indexTabulator(Request $request){ $client = new Client(); diff --git a/app/Http/Middleware/RoleMiddleware.php b/app/Http/Middleware/RoleMiddleware.php new file mode 100644 index 0000000..21ec3dd --- /dev/null +++ b/app/Http/Middleware/RoleMiddleware.php @@ -0,0 +1,24 @@ +withMiddleware(function (Middleware $middleware) { $middleware->alias([ 'checksession' => CheckClientSession::class, + 'checkrole' => RoleMiddleware::class, ]); }) ->withExceptions(function (Exceptions $exceptions) { diff --git a/config/permission.php b/config/permission.php new file mode 100644 index 0000000..85b8832 --- /dev/null +++ b/config/permission.php @@ -0,0 +1,206 @@ + [ + + /* + * When using the "HasPermissions" trait from this package, we need to know which + * Eloquent model should be used to retrieve your permissions. Of course, it + * is often just the "Permission" model but you may use whatever you like. + * + * The model you want to use as a Permission model needs to implement the + * `Spatie\Permission\Contracts\Permission` contract. + */ + + 'permission' => Permission::class, + + /* + * When using the "HasRoles" trait from this package, we need to know which + * Eloquent model should be used to retrieve your roles. Of course, it + * is often just the "Role" model but you may use whatever you like. + * + * The model you want to use as a Role model needs to implement the + * `Spatie\Permission\Contracts\Role` contract. + */ + + 'role' => Role::class, + + ], + + 'table_names' => [ + + /* + * When using the "HasRoles" trait from this package, we need to know which + * table should be used to retrieve your roles. We have chosen a basic + * default value but you may easily change it to any table you like. + */ + + 'roles' => 'roles', + + /* + * When using the "HasPermissions" trait from this package, we need to know which + * table should be used to retrieve your permissions. We have chosen a basic + * default value but you may easily change it to any table you like. + */ + + 'permissions' => 'permissions', + + /* + * When using the "HasPermissions" trait from this package, we need to know which + * table should be used to retrieve your models permissions. We have chosen a + * basic default value but you may easily change it to any table you like. + */ + + 'model_has_permissions' => 'model_has_permissions', + + /* + * When using the "HasRoles" trait from this package, we need to know which + * table should be used to retrieve your models roles. We have chosen a + * basic default value but you may easily change it to any table you like. + */ + + 'model_has_roles' => 'model_has_roles', + + /* + * When using the "HasRoles" trait from this package, we need to know which + * table should be used to retrieve your roles permissions. We have chosen a + * basic default value but you may easily change it to any table you like. + */ + + 'role_has_permissions' => 'role_has_permissions', + ], + + 'column_names' => [ + /* + * Change this if you want to name the related pivots other than defaults + */ + 'role_pivot_key' => null, // default 'role_id', + 'permission_pivot_key' => null, // default 'permission_id', + + /* + * Change this if you want to name the related model primary key other than + * `model_id`. + * + * For example, this would be nice if your primary keys are all UUIDs. In + * that case, name this `model_uuid`. + */ + + 'model_morph_key' => 'model_id', + + /* + * Change this if you want to use the teams feature and your related model's + * foreign key is other than `team_id`. + */ + + 'team_foreign_key' => 'team_id', + ], + + /* + * When set to true, the method for checking permissions will be registered on the gate. + * Set this to false if you want to implement custom logic for checking permissions. + */ + + 'register_permission_check_method' => true, + + /* + * When set to true, Laravel\Octane\Events\OperationTerminated event listener will be registered + * this will refresh permissions on every TickTerminated, TaskTerminated and RequestTerminated + * NOTE: This should not be needed in most cases, but an Octane/Vapor combination benefited from it. + */ + 'register_octane_reset_listener' => false, + + /* + * Events will fire when a role or permission is assigned/unassigned: + * \Spatie\Permission\Events\RoleAttached + * \Spatie\Permission\Events\RoleDetached + * \Spatie\Permission\Events\PermissionAttached + * \Spatie\Permission\Events\PermissionDetached + * + * To enable, set to true, and then create listeners to watch these events. + */ + 'events_enabled' => false, + + /* + * Teams Feature. + * When set to true the package implements teams using the 'team_foreign_key'. + * If you want the migrations to register the 'team_foreign_key', you must + * set this to true before doing the migration. + * If you already did the migration then you must make a new migration to also + * add 'team_foreign_key' to 'roles', 'model_has_roles', and 'model_has_permissions' + * (view the latest version of this package's migration file) + */ + + 'teams' => false, + + /* + * The class to use to resolve the permissions team id + */ + 'team_resolver' => DefaultTeamResolver::class, + + /* + * Passport Client Credentials Grant + * When set to true the package will use Passports Client to check permissions + */ + + 'use_passport_client_credentials' => false, + + /* + * When set to true, the required permission names are added to exception messages. + * This could be considered an information leak in some contexts, so the default + * setting is false here for optimum safety. + */ + + 'display_permission_in_exception' => false, + + /* + * When set to true, the required role names are added to exception messages. + * This could be considered an information leak in some contexts, so the default + * setting is false here for optimum safety. + */ + + 'display_role_in_exception' => false, + + /* + * By default wildcard permission lookups are disabled. + * See documentation to understand supported syntax. + */ + + 'enable_wildcard_permission' => false, + + /* + * The class to use for interpreting wildcard permissions. + * If you need to modify delimiters, override the class and specify its name here. + */ + // 'wildcard_permission' => Spatie\Permission\WildcardPermission::class, + + /* Cache-specific settings */ + + 'cache' => [ + + /* + * By default all permissions are cached for 24 hours to speed up performance. + * When permissions or roles are updated the cache is flushed automatically. + */ + + 'expiration_time' => DateInterval::createFromDateString('24 hours'), + + /* + * The cache key used to store all permissions. + */ + + 'key' => 'spatie.permission.cache', + + /* + * You may optionally indicate a specific cache driver to use for permission and + * role caching using any of the `store` drivers listed in the cache.php config + * file. Using 'default' here means to use the `default` set in cache.php. + */ + + 'store' => 'default', + ], +]; diff --git a/database/migrations/2026_04_23_164547_create_permission_tables.php b/database/migrations/2026_04_23_164547_create_permission_tables.php new file mode 100644 index 0000000..66ce1f9 --- /dev/null +++ b/database/migrations/2026_04_23_164547_create_permission_tables.php @@ -0,0 +1,134 @@ +engine('InnoDB'); + $table->bigIncrements('id'); // permission id + $table->string('name'); // For MyISAM use string('name', 225); // (or 166 for InnoDB with Redundant/Compact row format) + $table->string('guard_name'); // For MyISAM use string('guard_name', 25); + $table->timestamps(); + + $table->unique(['name', 'guard_name']); + }); + + Schema::create($tableNames['roles'], static function (Blueprint $table) use ($teams, $columnNames) { + // $table->engine('InnoDB'); + $table->bigIncrements('id'); // role id + if ($teams || config('permission.testing')) { // permission.testing is a fix for sqlite testing + $table->unsignedBigInteger($columnNames['team_foreign_key'])->nullable(); + $table->index($columnNames['team_foreign_key'], 'roles_team_foreign_key_index'); + } + $table->string('name'); // For MyISAM use string('name', 225); // (or 166 for InnoDB with Redundant/Compact row format) + $table->string('guard_name'); // For MyISAM use string('guard_name', 25); + $table->timestamps(); + if ($teams || config('permission.testing')) { + $table->unique([$columnNames['team_foreign_key'], 'name', 'guard_name']); + } else { + $table->unique(['name', 'guard_name']); + } + }); + + Schema::create($tableNames['model_has_permissions'], static function (Blueprint $table) use ($tableNames, $columnNames, $pivotPermission, $teams) { + $table->unsignedBigInteger($pivotPermission); + + $table->string('model_type'); + $table->unsignedBigInteger($columnNames['model_morph_key']); + $table->index([$columnNames['model_morph_key'], 'model_type'], 'model_has_permissions_model_id_model_type_index'); + + $table->foreign($pivotPermission) + ->references('id') // permission id + ->on($tableNames['permissions']) + ->onDelete('cascade'); + if ($teams) { + $table->unsignedBigInteger($columnNames['team_foreign_key']); + $table->index($columnNames['team_foreign_key'], 'model_has_permissions_team_foreign_key_index'); + + $table->primary([$columnNames['team_foreign_key'], $pivotPermission, $columnNames['model_morph_key'], 'model_type'], + 'model_has_permissions_permission_model_type_primary'); + } else { + $table->primary([$pivotPermission, $columnNames['model_morph_key'], 'model_type'], + 'model_has_permissions_permission_model_type_primary'); + } + + }); + + Schema::create($tableNames['model_has_roles'], static function (Blueprint $table) use ($tableNames, $columnNames, $pivotRole, $teams) { + $table->unsignedBigInteger($pivotRole); + + $table->string('model_type'); + $table->unsignedBigInteger($columnNames['model_morph_key']); + $table->index([$columnNames['model_morph_key'], 'model_type'], 'model_has_roles_model_id_model_type_index'); + + $table->foreign($pivotRole) + ->references('id') // role id + ->on($tableNames['roles']) + ->onDelete('cascade'); + if ($teams) { + $table->unsignedBigInteger($columnNames['team_foreign_key']); + $table->index($columnNames['team_foreign_key'], 'model_has_roles_team_foreign_key_index'); + + $table->primary([$columnNames['team_foreign_key'], $pivotRole, $columnNames['model_morph_key'], 'model_type'], + 'model_has_roles_role_model_type_primary'); + } else { + $table->primary([$pivotRole, $columnNames['model_morph_key'], 'model_type'], + 'model_has_roles_role_model_type_primary'); + } + }); + + Schema::create($tableNames['role_has_permissions'], static function (Blueprint $table) use ($tableNames, $pivotRole, $pivotPermission) { + $table->unsignedBigInteger($pivotPermission); + $table->unsignedBigInteger($pivotRole); + + $table->foreign($pivotPermission) + ->references('id') // permission id + ->on($tableNames['permissions']) + ->onDelete('cascade'); + + $table->foreign($pivotRole) + ->references('id') // role id + ->on($tableNames['roles']) + ->onDelete('cascade'); + + $table->primary([$pivotPermission, $pivotRole], 'role_has_permissions_permission_id_role_id_primary'); + }); + + app('cache') + ->store(config('permission.cache.store') != 'default' ? config('permission.cache.store') : null) + ->forget(config('permission.cache.key')); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + $tableNames = config('permission.table_names'); + + throw_if(empty($tableNames), Exception::class, 'Error: config/permission.php not found and defaults could not be merged. Please publish the package configuration before proceeding, or drop the tables manually.'); + + Schema::drop($tableNames['role_has_permissions']); + Schema::drop($tableNames['model_has_roles']); + Schema::drop($tableNames['model_has_permissions']); + Schema::drop($tableNames['roles']); + Schema::drop($tableNames['permissions']); + } +}; diff --git a/info.md b/info.md index 42dcd3b..bc0919f 100644 --- a/info.md +++ b/info.md @@ -9,3 +9,27 @@ Sam added that we can provide functionality for the App to be useable by multipl Victor will be able to share feedback on the endpoint change by 24th April. They have Mozambique and Madagascar offices that we could potentially onboard and Victor will loop us in but mentioned they would require the dashboard functionality +User Management +Ability to have multiple user logins with different roles. The roles will have different functions and accessibility as shown below; + +Administrator +Overall permissions with access to the following; + - SMS messages + - Finance related views ie Current account balance, SMS charges + - Create additional users + - View/dowload reports + - SMS sents + - SMS usage balance over a period +Finance + - Current balance + - SMS usage over a period + - Consumption/Usage reports + +Customer Care + - SMS messages +Kindly provide a way to view or extract a report for the following + - Periodic SMS usage (Financial) + - Periodic SMS sent + + +administrator, finance, customercare \ No newline at end of file diff --git a/public/assets/js/traffic-mgt.js b/public/assets/js/traffic-mgt.js new file mode 100644 index 0000000..3cd3849 --- /dev/null +++ b/public/assets/js/traffic-mgt.js @@ -0,0 +1,125 @@ + const token = document.querySelector('meta[name="csrf-token"]').getAttribute('content'); + const startDateElement = document.getElementById('startDate'); + const endDateElement = document.getElementById('endDate'); + + const startDatepicker = new Datepicker(startDateElement, { + autohide: true, + format: 'yyyy-mm-dd' + }); + + + const endDatepicker = new Datepicker(endDateElement, { + autohide: true, + format: 'yyyy-mm-dd' + }); + + function sendDailySmsUnits() { + document.getElementById('loadingOverlay').style.display = 'flex'; + const endpoint = "{{ route('client.dailysmsunits') }}"; + const startDate = startDateElement.value; + const endDate = endDateElement.value; + fetch(endpoint, { + method: 'POST', + headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'X-CSRF-TOKEN': token }, + body: 'start_date=' + encodeURIComponent(startDate) + '&end_date=' + encodeURIComponent(endDate) + }) + .then(response => response.json()) + .then(data => { + // document.getElementById('loadingSpinner').style.display = 'none'; + document.getElementById('loadingOverlay').style.display = 'none'; + const theReportRange = document.getElementById('reportRange'); + const theSmsUnitsValue = document.getElementById('smsUnitsValue'); + theReportRange.innerHTML = data.reportDate; + theSmsUnitsValue.innerHTML = "SMS Units : " + data.smsUnits + "| Charge : " + data.clientChargeTotal.toFixed(2); + + }) + .catch(error => console.error('Error:', error)); + } + endDateElement.addEventListener('changeDate', sendDailySmsUnits); + + + function statusDesign (cell, formatterParams){ + var value = cell.getValue(); + if (value !== null) { + if(value.includes('SENT')){ + return "" + value + ""; + } + else{ + return "" + value + ""; + } + } + } + var table = new Tabulator("#message-table", { + ajaxURL: base_url = "client-traffic-tabulator", // "https://smsportal.clickmlapps.com/client-traffic-tabulator/", + ajaxConfig: { + method: "GET", + headers: { + "Content-type": "application/json; charset=utf-8", + }, + }, + ajaxParams: {size: 1000}, + pagination: "remote", + paginationSize: 20, + paginationDataSent: { + "page": "page", + "size": "size" + }, + paginationDataReceived: { + "last_page": "totalPages", + "data": "content", + "current_page": "number", + "total": "totalElements" + }, + ajaxResponse: function(url, params, response) { + return response.content; + }, + columns: [ + {title: "Sender", field: "from", width:150, headerFilter:"input"}, + {title: "Msisdn", field: "to", width:150, headerFilter:"input"}, + {title:"Message", field:"message", width:650, formatter:"textarea", headerFilter:"input"}, + { + title:"Date Created", + field:"createdAt", + width:200, + formatter:"datetime", + formatterParams:{ + inputFormat:"iso", + outputFormat:"yyyy-MM-dd", + invalidPlaceholder:"(invalid date)" + }, + headerFilter:function(cell, onRendered, success, cancel){ + // Create native date input + var input = document.createElement("input"); + input.type = "date"; + + input.addEventListener("change", function(){ + console.log(input.value); + success(input.value); // pass value to Tabulator filter + }); + + return input; + }, + headerFilterFunc:function(headerValue, rowValue){ + if(!headerValue){ return true; } // no filter + if(!rowValue){ return false; } + + // Extract just the date portion from ISO timestamp + const rowDate = new Date(rowValue); + const formatted = rowDate.toISOString().split("T")[0]; // yyyy-MM-dd + + return formatted === headerValue; + } + } + ], + }); + + document.getElementById("download-pdf").addEventListener("click", function(){ + table.download("pdf", "messages.pdf", { + orientation:"portrait", // portrait or landscape + title:"Messages Export", // document title + }); + }); + + document.getElementById("download-xlsx").addEventListener("click", function(){ + table.download("xlsx", "messages.xlsx", {sheetName:"Messages"}); + }); \ No newline at end of file diff --git a/public/assets/js/usermgt.js b/public/assets/js/usermgt.js index 5def830..509e972 100644 --- a/public/assets/js/usermgt.js +++ b/public/assets/js/usermgt.js @@ -1,217 +1,192 @@ -$(document).ready(function(){ -// $('.editUserBtn').click(function(evnt){ +$(document).ready(function() { + $.ajaxSetup({ + headers: { 'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content') } + }); + + const sessionModal = new bootstrap.Modal(document.getElementById('sessionModal')); + + let currentPage = 1; + let searchQuery = ''; + let searchTimer; -// }); - // - console.log('foo bar'); - $('#editAllowedApps').select2({ - // width: "resolve", - dropdownParent: $('#editUserModal'), - placeholder : "Select options, multiple allowed" + fetchSessions(currentPage, searchQuery); - }); + function fetchSessions(page, search = '') { + $.ajax({ + type: "GET", + url: base_url + `/fetch-client-users?page=${page}&search=${encodeURIComponent(search)}`, + dataType: "json", + success: function(response) { + $('#clientUsersTableBody').html(""); + + if (response.sessions.data && response.sessions.data.length > 0) { + $.each(response.sessions.data, function(key, item) { + $('#clientUsersTableBody').append(` + + ${item.id} + ${item.email} + ${item.role} + + + + + + `); + }); - $('#allowedApps').select2({ - // width: "resolve", - dropdownParent: $('#addUserModal'), - placeholder : "Select options, multiple allowed" - }); + let from = response.sessions.from; + let to = response.sessions.to; + let total = response.sessions.total; + $('#paginationCounter').html(`Showing ${from} to ${to} of ${total} results`); + } else { + $('#clientUsersTableBody').html("No users found"); + $('#paginationCounter').html('Showing 0 to 0 of 0 results'); + } + + renderPagination(response); + } + }); + } - $('#inputPermissions').select2({ - // width: "resolve", - dropdownParent: $('#editUserModal'), - placeholder : "Select options, multiple allowed" - }); + $('#searchInput').on('keyup', function() { + clearTimeout(searchTimer); + searchQuery = $(this).val(); + + // Wait 500ms after the user stops typing to trigger the request + searchTimer = setTimeout(function() { + currentPage = 1; // Reset to page 1 for a brand new search + fetchSessions(currentPage, searchQuery); + }, 500); + }); - $('.editUserBtn').click(function(evnt){ - evnt.preventDefault(); - var selectedUserId = $(this).siblings('.userIdinput').val(); + $(document).on('click', '.page-link', function(e) { + e.preventDefault(); + + if ($(this).parent().hasClass('disabled')) { + return false; + } - const formData = new FormData(); - formData.append('user_id', selectedUserId); + let page = $(this).data('page'); + + if (page > 0) { + currentPage = page; + fetchSessions(currentPage, searchQuery); + } + }); - $.ajax({ - url: base_url + '/users/edit/' + selectedUserId, - type: 'GET', - processData: false, - contentType: false, - beforeSend: function() { - $('#editSuccessArea').text(""); - $('#editErrorArea').text("Please wait ... loading user details!"); - }, - success: function(data) { - var jason = data.data; - if(data.success == true){ - var allowedAppsArray = []; - if (jason['allowed_apps']) { - allowedAppsArray = jason['allowed_apps'].split(","); - } - console.log(jason['full_name']); - $('#editFullName').val(jason['full_name']); - $('#editEmail').val(jason['email']); - $('#editUsername').val(jason['username']); - $('#editGender').val(jason['gender']); - $('#editTitle').val(jason['title']); - $('#editUaPostion').val(jason['ua_position']); - $('#editPhone').val(jason['phone']); - $('#editAllowedApps').val(allowedAppsArray).trigger('change'); - $('#editRegionID').val(jason['region_id']); - $('#editDistrictId').val(jason['district_id']); - $('#editUaPostion').val(jason['ua_position']); - $('#editGender').val(jason['gender']); - $("input[name='user_id']").val(jason.ua_id); - - } - //$('#editUserModal').modal('show'); - }, - error: function(xhr, status, error) { - console.error('Error:', error); - $('#errorArea').text(error); - $('#errorArea').text(error); - } - }); + function renderPagination(response) { + let linksHtml = ''; + currentPage = response.sessions.current_page; + let lastPage = response.sessions.last_page; - }); + let prevDisabled = currentPage === 1 ? 'disabled' : ''; + let prevTabIndex = currentPage === 1 ? 'tabindex="-1"' : ''; + + linksHtml += ` +
  • + Previous +
  • `; - $('.viewUserBtn').click(function(evnt){ - evnt.preventDefault(); - var selectedUserId = $(this).siblings('.userIdinput').val(); - const formData = new FormData(); - formData.append('user_id', selectedUserId); + for (let i = 1; i <= lastPage; i++) { + let activeClass = currentPage === i ? 'active' : ''; + let activeAria = currentPage === i ? 'aria-current="page"' : ''; + + linksHtml += ` +
  • + ${i} +
  • `; + } + let nextDisabled = currentPage === lastPage ? 'disabled' : ''; + let nextTabIndex = currentPage === lastPage ? 'tabindex="-1"' : ''; + + linksHtml += ` +
  • + Next +
  • `; - $.ajax({ - url: base_url + '/users/' + selectedUserId, - type: 'GET', - processData: false, - contentType: false, - beforeSend: function() { - $('#viewSuccessArea').text(""); - $('#viewErrorArea').text("Please wait ... loading user details!"); - }, - success: function(data) { - var jason = data.data; - if(data.success == true){ - var allowedAppsArray = []; - if (jason['allowed_apps']) { - allowedAppsArray = jason['allowed_apps'].split(","); - } - console.log(jason['full_name']); - $('#viewFullName').val(jason['full_name']); - $('#viewEmail').val(jason['email']); - $('#viewUsername').val(jason['username']); - $('#viewGender').val(jason['gender']); - $('#viewTitle').val(jason['title']); - $('#viewUaPostion').val(jason['ua_position']); - $('#viewPhone').val(jason['phone']); - $('#viewAllowedApps').val(allowedAppsArray).trigger('change'); - $('#viewRegionID').val(jason['region_id']); - $('#viewDistrictId').val(jason['district_id']); - $('#viewUaPostion').val(jason['ua_position']); - $('#viewGender').val(jason['gender']); - $("input[name='user_id']").val(jason.ua_id); - - } - //$('#editUserModal').modal('show'); - }, - error: function(xhr, status, error) { - console.error('Error:', error); - $('#errorArea').text(error); - $('#errorArea').text(error); - } - }); - - }); - - $("#newUserForm").submit(function(evt){ - evt.preventDefault(); - $('#successArea').addClass('d-none'); - $('#errorsArea').removeClass('d-none'); - var formData = new FormData($(this)[0]); - - $.ajax({ - url: base_url + '/users', - type: 'POST', - data: formData, - processData: false, - contentType: false, - beforeSend: function() { - $('#successArea').text(""); - $('#successArea').text("Please wait ... user creation in progress!"); - }, - success: function(data) { - - if (data['success'] == true) { - $('#successArea').removeClass('d-none'); - $('#errorsArea').addClass('d-none'); - - $('#successArea').text(""); - $('#successArea').text("User successfully created!"); - // location.reload(); - setTimeout(function() { - location.reload(); // Reloads the current page - }, 15000); - } - else{ - $('#successArea').addClass('d-none'); - $('#errorArea').removeClass('d-none'); - $('#errorArea').text(""); - $('#errorArea').text("User could not be created!"); - } - }, - error: function(xhr, status, error) { - console.error('Error:', error); - $('#successArea').text(error); - $('#successArea').text(error); - } - }); - }); - - $("#editUserForm").submit(function(evt){ - evt.preventDefault(); - $('#successArea').addClass('d-none'); - $('#errorsArea').removeClass('d-none'); - var formData = new FormData($(this)[0]); - $.ajax({ - url: base_url + '/users/update/', - type: 'POST', - data: formData, - processData: false, - contentType: false, - beforeSend: function() { - // $('#updateBtn').addClass('d-none'); - // $('#uodateProgressBtn').removeClass('d-none'); - // $('#updateResultsDiv').removeClass('d-none'); - // $('#updateResultsParagraph').text("Processing Please wait ..."); - }, - success: function(data) { - console.log(data); - $('#editSuccessArea').removeClass('d-none'); - $('#editErrorArea').addClass('d-none'); - $('#editSuccessArea').text(""); - $('#editSuccessArea').text("User successfully details updated!"); - - }, - error: function(xhr, status, error) { - console.error('Error:', error); - $('#editSuccessArea').text(error); - $('#editErrorArea').text(error); - location.reload(); - } - }); - }); - - $('#regionID').change(function(){ - var options = $('#districtID'); - var region_id = $('#regionID').val(); - $.get( base_url + '/admin/districts/' + region_id, function (data) { - $('#districtID').empty(); - $.each(data['districts'], function(id, row) { - $('#districtID').append($("