diff --git a/info.md b/info.md new file mode 100644 index 00000000..07c2847f --- /dev/null +++ b/info.md @@ -0,0 +1,52 @@ +# Tit Bits +## API Base URL +- https://api.lupmis4luspa.org/ + + +```` +- g!_ArOqhC$Gs9-u2 +```` + +## API Testing Tool +- https://restful-api.dev/ + +git remote add https://gitea.lupmis4luspa.org/kwesigitea/main-backend.git + +title - add N/A + +from the server 29-10-2025 +services: + l4lauthentication-docker: + container_name: l4lauthentication + build: . + volumes: + - ./php_code:/var/www/html + #ports: + #- 8831:80 + restart: unless-stopped + networks: + - nginx_proxy_manager_default +networks: + nginx_proxy_manager_default: + external: true + + + +curl -X 'POST' \ + 'https://api.lupmis4luspa.org/api/user_mgt/get_district_by_region_id.php' \ + -H 'accept: application/json' \ + -H 'Content-Type: application/json' \ + -d '{ + "region_id": "3", + "api_token": "1c46538c712e9b5b" +}' + + + + +permit application comments +id, user_id, application_code, comment_body, created_at, updated_at + +php artisan make:model PermitApplicationComments -m + + diff --git a/lupmis.sqlite b/lupmis.sqlite new file mode 100644 index 00000000..3da265b0 Binary files /dev/null and b/lupmis.sqlite differ diff --git a/php_code/app/Http/Controllers/Admin/UserController.php b/php_code/app/Http/Controllers/Admin/UserController.php index 37b9250d..c990c64c 100644 --- a/php_code/app/Http/Controllers/Admin/UserController.php +++ b/php_code/app/Http/Controllers/Admin/UserController.php @@ -4,7 +4,7 @@ namespace App\Http\Controllers\Admin; use App\Http\Controllers\Controller; use Illuminate\Http\Request; -use App\Models\User; // adjust if you have a custom model +use App\Models\User; use Illuminate\Validation\Rule; use Illuminate\Support\Facades\Validator; @@ -13,6 +13,7 @@ class UserController extends Controller public function index() { + dd(session('current_user')); return view('admin.users'); // Blade file below } @@ -102,4 +103,5 @@ class UserController extends Controller $user->delete(); return response()->json(['deleted'=>true]); } + } diff --git a/php_code/app/Http/Controllers/AdminController.php b/php_code/app/Http/Controllers/AdminController.php index 110b7c4f..1b3eda81 100644 --- a/php_code/app/Http/Controllers/AdminController.php +++ b/php_code/app/Http/Controllers/AdminController.php @@ -7,11 +7,15 @@ use Illuminate\Http\Request; use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Support\Collection; use App\Models; +use Illuminate\Support\Facades\Log; + class AdminController extends Controller { public function index() { + //dd(session('current_user')); //"ua_position" => "district-ppd-head" "user_type" => "district_user" + if (session('current_user.user_type') == 'district_user') { $users_url = "user_mgt/get_all_users_by_district.php"; $data = ['district_id' => session('current_user.district_id'), 'api_token' => env('LUPMISAPIKEY')]; @@ -20,19 +24,47 @@ class AdminController extends Controller $users_url = "user_mgt/get_all_users_by_district.php"; $data = ['region_id' => session('region_id'), 'api_token' => env('LUPMISAPIKEY')]; } + elseif(session('current_user.user_type') == 'national_luspa'){ + $users_url = "user_mgt/get_all_users.php"; + $data = ['api_token' => env('LUPMISAPIKEY')]; + } else{ $users_url = "user_mgt/get_all_users.php"; $data = ['api_token' => env('LUPMISAPIKEY') ]; } $result = ApiCalls::CurlPost(json_encode($data), $users_url); - + /* $users_arr = json_decode($result, true); - - if ($users_arr == null || $users_arr['success'] == false) { - \Log::info(session('current_user.username') . " tried accessing Admin/home and got empty or no success users_arr"); + dump($users_arr); + if ($users_arr == null || $users_arr['success'] == false || $users_arr['status'] == 'error') { + Log::info(session('current_user.username') . " tried accessing Admin/home and got empty or no success users_arr"); return redirect()->back()->withErrors('Your request cannot be handled at this time. Try again later'); } + */ + + try { + $usersArr = json_decode($result, true, 512, JSON_THROW_ON_ERROR); + } + catch (\JsonException $e) { + Log::warning('Admin/home API returned invalid JSON', [ + 'user' => session('current_user.username'), + 'json_error' => $e->getMessage(), + ]); + + return redirect()->back() + ->withErrors(['api' => 'Your request cannot be handled at this time. Try again later.']); + } + + if (!is_array($usersArr) || ($usersArr['success'] ?? null) === false || strtolower((string) ($usersArr['status'] ?? '')) === 'error') { + Log::warning('Admin/home API returned an unsuccessful response', [ + 'user' => session('current_user.username'), + 'success' => $usersArr['success'] ?? 'missing', + 'status' => $usersArr['status'] ?? 'missing', + ]); + return redirect()->back() + ->withErrors(['api' => 'Your request cannot be handled at this time. Try again later.']); + } $regions_url = "user_mgt/get_all_regions.php"; @@ -40,7 +72,7 @@ class AdminController extends Controller $result = ApiCalls::CurlPost(json_encode($data), $regions_url); $regions_arr = json_decode($result, true); if ($regions_arr == null || $regions_arr['success'] == false) { - \Log::info(session('current_user.username') . " tried accessing Admin/home and got empty or no success region"); + Log::info(session('current_user.username') . " tried accessing Admin/home and got empty or no success region"); return redirect()->back()->withErrors('Your request cannot be handled at this time. Try again later'); } @@ -98,15 +130,7 @@ class AdminController extends Controller $paginatedDistrict = $this->paginateCollection($districtUsers, 10, 'district_page'); $paginatedRegional = $this->paginateCollection($regionalUsers, 10, 'regional_page'); $paginatedNational = $this->paginateCollection($nationalUsers, 10, 'national_page'); - // dd($paginatedRegional); - // return view('admin.home_cats', [ - // 'page_title' => 'User Management', - // 'nationalUsers' => $nationalUsers, - // 'regionalUsers' => $regionalUsers, - // 'districtUsers' => $districtUsers, - // 'totalUsers' => $users->count() - // ]); - // dd($regionalUsers->count()); + return view('admin.home_cats', [ 'page_title' => 'User Admin', 'regions_arr' => $regions_arr['data'], @@ -194,37 +218,7 @@ class AdminController extends Controller // return view('admin.home', $data); } - public function indexOld(){ - $users_url = "user_mgt/get_all_users.php"; - $data = ['api_token' => env('LUPMISAPIKEY')]; - $result = ApiCalls::CurlPost(json_encode($data), $users_url); - $users_arr = json_decode($result, true); - if ($users_arr['success'] == false) { - return redirect->back(); - } - - $regions_url = "user_mgt/get_all_regions.php"; - $data = ['api_token' => env('LUPMISAPIKEY')]; - $result = ApiCalls::CurlPost(json_encode($data), $regions_url); - $regions_arr = json_decode($result, true); - if ($regions_arr['success'] == false) { - return redirect->back(); - } - - $user_type_arr = [ - 'district_user' => 'District User', - 'regional_user' => 'Regional User', - 'national_user' => 'National User' - ]; - - $data = [ - 'page_title' => 'User Admin', - 'users_arr' => $users_arr['data'], - 'regions_arr' => $regions_arr['data'], - 'user_type_arr' => $user_type_arr - ]; - return view('admin.home', $data); - } + public function districtparams(){ /* $url = "user_mgt/get_district_parameters.php"; diff --git a/php_code/app/Http/Controllers/PermitsController.php b/php_code/app/Http/Controllers/PermitsController.php index bbab71ca..4e823f21 100644 --- a/php_code/app/Http/Controllers/PermitsController.php +++ b/php_code/app/Http/Controllers/PermitsController.php @@ -6,35 +6,86 @@ use Session; use Illuminate\Http\Request; use Storage; use Response; +use Illuminate\Support\Collection; +use Illuminate\Pagination\LengthAwarePaginator; +use Carbon\Carbon; class PermitsController extends Controller { - public function index(){ - // dd(session('current_user.district_id')); + public function indexOld(){ $url = "permit/get_applications_by_district.php"; $data = json_encode([ 'district_id' => session('current_user.district_id'), 'api_token' => env('LUPMISAPIKEY') ]); $result = ApiCalls::CurlPost($data, $url); - // dd($result); - - - // { "id":163,"applicant_id":376,"client_generated_id":"7d707dc2-938f-47e7-8979-2def086d4a8d","application_code":"REQ-20260310-2ZK4","permit_type":"Development Permit","permit_form_type":"structural_alteration","project_description":"jkhghhjghjgh","project_location":"mbvmbnv","project_region_id":10,"project_district_id":122,"estimated_cost":0.00,"status":"submitted","property_size":null,"expected_completion_date":null,"is_paid":null,"submitted_at":"2026-03-10T09:18:04.224672","updated_at":"2026-03-10T09:18:04.224672"}, - // {"id":313,"applicant_id":482,"client_generated_id":"34ceb996-c809-4dbe-ba27-df1527e75538","application_code":"REQ-20260312-22AL","permit_type":"Development Permit","permit_form_type":"erection_of_building","project_description":"Two floor residence","project_location":"Butumagyabu","project_region_id":10,"project_district_id":122,"estimated_cost":0.00,"status":"submitted","property_size":null,"expected_completion_date":null,"is_paid":null,"submitted_at":"2026-03-12T13:14:04.420739","updated_at":"2026-03-12T13:14:04.420739"}]} - - - - $result = json_decode($result, true); - // dd(is_array($result['data'])); - + dd($result); $data = [ 'page_title' => 'Permits Dashboard', 'permits_arr' => $result ]; return view('permits.dashboard', $data); } + public function index(Request $request) + { + $url = "permit/get_applications_by_district.php"; + $data = json_encode([ + 'district_id' => session('current_user.district_id'), + 'api_token' => env('LUPMISAPIKEY') + ]); + $result = ApiCalls::CurlPost($data, $url); + $response = json_decode($result, true); + // dd($response); + $permits = collect($response['data'] ?? []); + + + $summaries = [ + 'total' => $permits->count(), + 'submitted' => $permits->filter(fn($p) => strtolower($p['status'] ?? '') === 'submitted')->count(), + 'development' => $permits->filter(fn($p) => str_contains(strtolower($p['permit_type'] ?? ''), 'development'))->count(), + 'planning' => $permits->filter(fn($p) => str_contains(strtolower($p['permit_type'] ?? ''), 'planning'))->count(), + ]; + if ($request->has('search') && $request->search != '') { + $search = strtolower($request->search); + $permits = $permits->filter(function($p) use ($search) { + $code = strtolower($p['application_code'] ?? ''); + $location = strtolower($p['project_location'] ?? ''); + $type = strtolower($p['permit_type'] ?? ''); + $form_type = strtolower($p['permit_form_type'] ?? ''); + + return str_contains($code, $search) || + str_contains($location, $search) || + str_contains($form_type, $search) || + str_contains($type, $search); + }); + } + $paginatedPermits = $this->paginateCollection($permits, 15, 'page'); + + return view('permits.dashboard', [ + 'page_title' => 'Permits Management', + 'permits' => $paginatedPermits, + 'summaries' => $summaries + ]); + } + private function paginateCollection(Collection $items, $perPage = 15, $pageName = 'page') + { + $page = LengthAwarePaginator::resolveCurrentPage($pageName); + $currentPageItems = $items->slice(($page - 1) * $perPage, $perPage)->values(); + + $paginator = new LengthAwarePaginator( + $currentPageItems, + $items->count(), + $perPage, + $page, + [ + 'path' => LengthAwarePaginator::resolveCurrentPath(), + 'pageName' => $pageName, + ] + ); + + return $paginator->withQueryString(); + } public function settings(){ //pull email address, sms configs, /* @@ -74,6 +125,7 @@ class PermitsController extends Controller return view('permits.index', $data); } public function show($id){ + // dd($id); // dump(session('current_user')); $url = "permit/get_applications_with_applicant_by_application_code.php"; diff --git a/php_code/app/Http/Controllers/UsersController.php b/php_code/app/Http/Controllers/UsersController.php index 07e52377..622d5c79 100644 --- a/php_code/app/Http/Controllers/UsersController.php +++ b/php_code/app/Http/Controllers/UsersController.php @@ -50,8 +50,9 @@ class UsersController extends Controller //send reset link email //baduhupir@mailinator.com $reset_link = env('APP_URL') . "/reset-auth/" . $user_id; - Mail::to($recipientEmail)->send(new PasswordResetMail($fullname, $reset_link)); - + if(env('APP_ENV') == 'production'){ + Mail::to($recipientEmail)->send(new PasswordResetMail($fullname, $reset_link)); + } return redirect(url('reset-notice')); //get // return redirect(url('new-password-form')); @@ -72,16 +73,6 @@ class UsersController extends Controller } public function handle_reset(Request $request){ - // code...show_new_passform - // dd('foo bar handle reset'); - /* - { - "username": "string", - "pass": "string", - "user_id": "string", - "api_token": "string" - } - */ $url = "user_mgt/update_usr_user.php"; $user_data = [ @@ -167,7 +158,6 @@ class UsersController extends Controller } - public function store(Request $request){ $url = "user_mgt/add_usr_user.php"; @@ -175,7 +165,7 @@ class UsersController extends Controller 'full_name' => 'required|string|max:255', 'username' => 'required|string|max:255|unique:users,username', 'ua_position' => 'required|string', - 'allowed_apps'=> 'required|array', + // 'allowed_apps'=> 'required|array', 'user_status' => 'required|string', 'gender' => 'required|in:male,female', 'district_id' => [ @@ -198,7 +188,7 @@ class UsersController extends Controller 'username.required' => 'A username is required.', 'username.unique' => 'This username is already taken.', 'ua_position.required' => 'Position is mandatory.', - 'allowed_apps.required'=> 'Select at least one application.', + // 'allowed_apps.required'=> 'Select at least one application.', 'gender.required' => 'Gender is required.', 'district_id.required' => 'District must be selected.', 'region_id.required' => 'Region is required', @@ -210,15 +200,29 @@ class UsersController extends Controller 'phone.required' => 'Phone number is required.', ]); - // Generate random password + // dd($request->ua_position); $password = Str::random(10); + $user_group_matrix = Config('usergroupmatrix'); + //$allowed_apps_all = $user_group_matrix[ucwords($request->ua_position)]; + $allowed_apps_all = collect($user_group_matrix) + ->filter( + fn (array $user_group_matrix, string $groupName) => + str_contains(strtolower($groupName), strtolower($request->ua_position)) + )->all(); + + $allowed_apps = collect($allowed_apps_all) + ->filter(fn ($allowed) => $allowed === true) + ->keys() + ->values() + ->all(); + $data = json_encode([ 'full_name' => $request['full_name'], 'username' => $request['username'], 'ua_position' => $request['ua_position'], 'email' => $request['email'], 'title' => $request['title'], - 'allowed_apps' => implode(", ", $request['allowed_apps']), + 'allowed_apps' => implode(", ", $allowed_apps), 'is_password_changed' => false, 'password_hint' => 'none', 'phone' => str_replace('+', '',$request['phone']), @@ -230,7 +234,33 @@ class UsersController extends Controller 'district_id' => $request['district_id'], 'api_token' => env('LUPMISAPIKEY'), ]); + + // dd($data); + //todo determine the allowed_apps user + //user_type and ua_position + if ($request->user_type == 'district_user') { + // code... + } + elseif ($request->user_type == 'regional_luspa') { + // code... + } + elseif ($request->user_type == 'national_luspa') { + // code... + } + /* + 'permit_tools_backend', + 'update_permits', + 'drawing_tools', + 'view_drawing_tools', + 'admin_gui', + + */ + // "drawing-tools" + // "permit-tools" + // "admin-gui" + // "" + $result = ApiCalls::CurlPost($data, $url); $result = json_decode($result, true); @@ -239,9 +269,9 @@ class UsersController extends Controller } \Log::info("Your Password is $password"); - - Mail::to($request->email)->send(new UserAccountsMail($password, $request->username)); - + if(env('APP_ENV') == 'production'){ + Mail::to($request->email)->send(new UserAccountsMail($password, $request->username)); + } $sms_message = "Hello {$request->full_name}, your LUPMIS account has been successfully created\n"; $sms_message .= "Username: {$request->username}\n"; $sms_message .= "Password: $password\n"; @@ -251,9 +281,10 @@ class UsersController extends Controller 'recipient' => $request['phone'], 'message' => $sms_message ]; - $sms = new SmsLibrary; - $retval = $sms->SendMnotitySms($sms_data); - + if(env('APP_ENV') == 'production'){ + $sms = new SmsLibrary; + $retval = $sms->SendMnotitySms($sms_data); + } \Log::info("SMS Body : $sms_message"); if ($request->expectsJson()) { @@ -269,10 +300,10 @@ class UsersController extends Controller 'full_name' => 'required|string|max:255', 'username' => 'required|string|max:20|', 'ua_position' => 'required|string', - 'allowed_apps'=> 'required|array', + // 'allowed_apps'=> 'required|array', 'user_status' => 'required|string', 'district_id' => 'required|integer', - 'region_id' => 'required|integer', + 'region_id' => 'sometimes|integer', 'gender' => 'required|in:male,female', /* 'district_id' => [ @@ -295,7 +326,7 @@ class UsersController extends Controller 'full_name.required' => 'Please provide the full name.', 'username.required' => 'A username is required.', 'ua_position.required' => 'Position is mandatory.', - 'allowed_apps.required'=> 'Select at least one application.', + // 'allowed_apps.required'=> 'Select at least one application.', 'gender.required' => 'Gender is required.', 'district_id.required' => 'District must be selected.', 'region_id.required' => 'Region is required', @@ -307,6 +338,20 @@ class UsersController extends Controller 'phone.required' => 'Phone number is required.', ]); $is_disabled = ($request->user_status == 'active') ? 'false' : 'true'; + $user_group_matrix = Config('usergroupmatrix'); + //$allowed_apps_all = $user_group_matrix[ucwords($request->ua_position)]; + $allowed_apps_all = collect($user_group_matrix) + ->filter( + fn (array $user_group_matrix, string $groupName) => + str_contains(strtolower($groupName), strtolower($request->ua_position)) + )->all(); + + $allowed_apps = collect($allowed_apps_all) + ->filter(fn ($allowed) => $allowed === true) + ->keys() + ->values() + ->all(); + $userData = [ 'user_id' => $request->user_id, 'full_name' => $request->full_name, @@ -314,8 +359,8 @@ class UsersController extends Controller 'ua_position' => $request->ua_position, 'email' => $request->email, 'title' => $request->title, - - 'allowed_apps' => implode(", ", $request->allowed_apps), + // 'allowed_apps' => implode(", ", $request->allowed_apps), + 'allowed_apps' => implode(", ", $allowed_apps), 'phone' => str_replace('+', '', $request->phone), 'gender' => $request->gender, @@ -379,4 +424,29 @@ class UsersController extends Controller return response()->json($result); } } + public function getPositionsMap() + { + $district_groups = array_keys(Config('usergroupmatrix')); + $map = [ + 'district_user' => $district_groups, + /* + 'district_user' => [ + "PPD Head", "Works Head", "MIS", "PPD Staff", "Works Staff", + "Devt Planning Officer", "MCE", "DCE", "Urban Roads Department Head", + "District Fire Officer", "District Disaster Prevention Department", + "Head of District Health Department", "Representative of the Lands Commission", + "Representative of the Environmental Protection Agency", "Rep from Traditional Council", + "Chairman of the Works Sub Committee", "Chairman of Development Sub Planning Committee", + "Nominated Elected Assembly Memebers" + ], + */ + 'national_luspa' => ["Director", "IT Head", "Staff"], + 'regional_luspa' => ["Director", "Staff"] + ]; + + return response()->json([ + 'success' => true, + 'data' => $map + ]); + } } diff --git a/php_code/app/Http/Controllers/UtilityController.php b/php_code/app/Http/Controllers/UtilityController.php index a2ea9dc8..19e1efd7 100644 --- a/php_code/app/Http/Controllers/UtilityController.php +++ b/php_code/app/Http/Controllers/UtilityController.php @@ -5,6 +5,7 @@ namespace App\Http\Controllers; use Illuminate\Http\Request; use App\Utilities\ApiCalls; use App\Utilities\SmsLibrary; +use App\Services\MicrosoftMailService; class UtilityController extends Controller { @@ -23,4 +24,15 @@ class UtilityController extends Controller \Log::info("SMS API Response : $sms_result"); } + + public function testMSEmail(MicrosoftMailService $mailService) + { + $mailService->sendEmail( + 'kwesilabs@outlook.com', + 'Test from LUPMIS', + '

Hello!

This is sent via Microsoft Graph API.

' + ); + + return 'Email sent!'; + } } diff --git a/php_code/app/Models/User.php b/php_code/app/Models/User.php index 65642024..af7514c6 100644 --- a/php_code/app/Models/User.php +++ b/php_code/app/Models/User.php @@ -47,4 +47,23 @@ class User extends Authenticatable 'password' => 'hashed', ]; } + /* + public function boot(): void + { + $matrix = [ + 'LUSPA National' => ['permit backend', 'drawing tools', 'view drawing tools', 'admin gui'], + 'District Chief Executive' => ['permit backend', 'view drawing tools'], + 'District Coordinating Director' => ['view drawing tools'], + 'Head of Physical Planning Department' => ['permit backend', 'update permits', 'drawing tools', 'view drawing tools', 'admin gui'], + ]; + + Gate::before(function (User $user, string $ability) use ($matrix) { + $role = $user->role; + + if (isset($matrix[$role]) && in_array($ability, $matrix[$role])) { + return true; + } + }); + } + */ } diff --git a/php_code/app/Services/MicrosoftMailService.php b/php_code/app/Services/MicrosoftMailService.php new file mode 100644 index 00000000..0a120e43 --- /dev/null +++ b/php_code/app/Services/MicrosoftMailService.php @@ -0,0 +1,65 @@ +post("https://login.microsoftonline.com/{$tenantId}/oauth2/v2.0/token", [ + 'client_id' => $clientId, + 'client_secret' => $clientSecret, + 'scope' => 'https://graph.microsoft.com/.default', + 'grant_type' => 'client_credentials', + ]); + + if ($response->failed()) { + throw new Exception('Failed to get Microsoft Graph token: ' . $response->body()); + } + // dd($response); + return $response->json('access_token'); + } + + /** + * Sends an email using the Microsoft Graph API. + */ + public function sendEmail($toEmail, $subject, $body) + { + $accessToken = $this->getAccessToken(); + $sender = env('MS_GRAPH_SENDER_EMAIL'); + + $response = Http::withToken($accessToken) + ->post("https://graph.microsoft.com/v1.0/users/{$sender}/sendMail", [ + 'message' => [ + 'subject' => $subject, + 'body' => [ + 'contentType' => 'HTML', // Or 'Text' + 'content' => $body + ], + 'toRecipients' => [ + [ + 'emailAddress' => [ + 'address' => $toEmail + ] + ] + ] + ], + 'saveToSentItems' => 'true' + ]); + + if ($response->failed()) { + throw new Exception('Failed to send email: ' . $response->body()); + } + + return true; + } +} \ No newline at end of file diff --git a/php_code/config/usergroupmatrix.php b/php_code/config/usergroupmatrix.php new file mode 100644 index 00000000..4e17c75f --- /dev/null +++ b/php_code/config/usergroupmatrix.php @@ -0,0 +1,326 @@ + [ + 'permit_backend' => true, + 'update_permits' => false, + 'drawing_tools' => true, + 'view_drawing_tools' => true, + 'admin_gui' => true, + ], + + 'luspa_regional' => [ + 'permit_backend' => true, + 'update_permits' => false, + 'drawing_tools' => true, + 'view_drawing_tools' => true, + 'admin_gui' => true, + ], + + 'district_chief_executive' => [ + 'permit_backend' => true, + 'update_permits' => false, + 'drawing_tools' => false, + 'view_drawing_tools' => true, + 'admin_gui' => false, + ], + + 'district_coordinating_director' => [ + 'permit_backend' => false, + 'update_permits' => false, + 'drawing_tools' => false, + 'view_drawing_tools' => true, + 'admin_gui' => false, + ], + + 'district_mis' => [ + 'permit_backend' => true, + 'update_permits' => false, + 'drawing_tools' => false, + 'view_drawing_tools' => true, + 'admin_gui' => true, + ], + + 'head_of_physical_planning_department' => [ + 'permit_backend' => true, + 'update_permits' => true, + 'drawing_tools' => true, + 'view_drawing_tools' => true, + 'admin_gui' => true, + ], + + 'physical_planning_department_staff' => [ + 'permit_backend' => true, + 'update_permits' => false, + 'drawing_tools' => true, + 'view_drawing_tools' => true, + 'admin_gui' => true, + ], + + 'development_planning_officer' => [ + 'permit_backend' => true, + 'update_permits' => false, + 'drawing_tools' => true, + 'view_drawing_tools' => true, + 'admin_gui' => false, + ], + + 'head_of_works_department' => [ + 'permit_backend' => true, + 'update_permits' => false, + 'drawing_tools' => true, + 'view_drawing_tools' => true, + 'admin_gui' => false, + ], + + 'works_department_staff' => [ + 'permit_backend' => true, + 'update_permits' => false, + 'drawing_tools' => true, + 'view_drawing_tools' => true, + 'admin_gui' => false, + ], + + 'head_of_the_roads_unit_or_urban_roads_department' => [ + 'permit_backend' => true, + 'update_permits' => false, + 'drawing_tools' => true, + 'view_drawing_tools' => true, + 'admin_gui' => false, + ], + + 'district_fire_officer' => [ + 'permit_backend' => true, + 'update_permits' => false, + 'drawing_tools' => true, + 'view_drawing_tools' => true, + 'admin_gui' => false, + ], + + 'district_disaster_prevention_department' => [ + 'permit_backend' => true, + 'update_permits' => false, + 'drawing_tools' => false, + 'view_drawing_tools' => true, + 'admin_gui' => false, + ], + + 'head_of_district_health_department' => [ + 'permit_backend' => true, + 'update_permits' => false, + 'drawing_tools' => false, + 'view_drawing_tools' => true, + 'admin_gui' => false, + ], + + 'representative_of_the_lands_commission' => [ + 'permit_backend' => true, + 'update_permits' => false, + 'drawing_tools' => false, + 'view_drawing_tools' => true, + 'admin_gui' => false, + ], + + 'representative_of_the_environmental_protection_agency' => [ + 'permit_backend' => true, + 'update_permits' => false, + 'drawing_tools' => false, + 'view_drawing_tools' => true, + 'admin_gui' => false, + ], + + 'rep_from_traditional_council' => [ + 'permit_backend' => true, + 'update_permits' => false, + 'drawing_tools' => false, + 'view_drawing_tools' => true, + 'admin_gui' => false, + ], + + 'chairman_of_the_works_sub_committee' => [ + 'permit_backend' => true, + 'update_permits' => false, + 'drawing_tools' => false, + 'view_drawing_tools' => true, + 'admin_gui' => false, + ], + + 'chairman_of_development_sub_planning_committee' => [ + 'permit_backend' => true, + 'update_permits' => false, + 'drawing_tools' => false, + 'view_drawing_tools' => true, + 'admin_gui' => false, + ], + + 'nominated_elected_assembly_members' => [ + 'permit_backend' => true, + 'update_permits' => false, + 'drawing_tools' => false, + 'view_drawing_tools' => true, + 'admin_gui' => false, + ], +]; + +/*return [ + 'LUSPA National' => [ + 'permit_backend' => true, + 'update_permits' => false, + 'drawing_tools' => true, + 'view_drawing_tools' => true, + 'admin_gui' => true, + ], + + 'LUSPA Regional' => [ + 'permit_backend' => true, + 'update_permits' => false, + 'drawing_tools' => true, + 'view_drawing_tools' => true, + 'admin_gui' => true, + ], + + 'District Chief Executive' => [ + 'permit_backend' => true, + 'update_permits' => false, + 'drawing_tools' => false, + 'view_drawing_tools' => true, + 'admin_gui' => false, + ], + + 'District Coordinating Director' => [ + 'permit_backend' => false, + 'update_permits' => false, + 'drawing_tools' => false, + 'view_drawing_tools' => true, + 'admin_gui' => false, + ], + + 'District MIS' => [ + 'permit_backend' => true, + 'update_permits' => false, + 'drawing_tools' => false, + 'view_drawing_tools' => true, + 'admin_gui' => true, + ], + + 'Head of Physical Planning Department' => [ + 'permit_backend' => true, + 'update_permits' => true, + 'drawing_tools' => true, + 'view_drawing_tools' => true, + 'admin_gui' => true, + ], + 'Physical Planning Department Staff' => [ + 'permit_backend' => true, + 'update_permits' => false, + 'drawing_tools' => true, + 'view_drawing_tools' => true, + 'admin_gui' => true, + ], + + 'Development Planning Officer' => [ + 'permit_backend' => true, + 'update_permits' => false, + 'drawing_tools' => true, + 'view_drawing_tools' => true, + 'admin_gui' => false, + ], + + 'Head of Works Department' => [ + 'permit_backend' => true, + 'update_permits' => false, + 'drawing_tools' => true, + 'view_drawing_tools' => true, + 'admin_gui' => false, + ], + 'Works Department Staff' => [ + 'permit_backend' => true, + 'update_permits' => false, + 'drawing_tools' => true, + 'view_drawing_tools' => true, + 'admin_gui' => false, + ], + + 'Head of the Roads Unit or Urban Roads Department' => [ + 'permit_backend' => true, + 'update_permits' => false, + 'drawing_tools' => true, + 'view_drawing_tools' => true, + 'admin_gui' => false, + ], + + 'District Fire Officer' => [ + 'permit_backend' => true, + 'update_permits' => false, + 'drawing_tools' => true, + 'view_drawing_tools' => true, + 'admin_gui' => false, + ], + + 'District Disaster Prevention Department' => [ + 'permit_backend' => true, + 'update_permits' => false, + 'drawing_tools' => false, + 'view_drawing_tools' => true, + 'admin_gui' => false, + ], + + 'Head of District Health Department' => [ + 'permit_backend' => true, + 'update_permits' => false, + 'drawing_tools' => false, + 'view_drawing_tools' => true, + 'admin_gui' => false, + ], + + 'Representative of the Lands Commission' => [ + 'permit_backend' => true, + 'update_permits' => false, + 'drawing_tools' => false, + 'view_drawing_tools' => true, + 'admin_gui' => false, + ], + + 'Representative of the Environmental Protection Agency' => [ + 'permit_backend' => true, + 'update_permits' => false, + 'drawing_tools' => false, + 'view_drawing_tools' => true, + 'admin_gui' => false, + ], + + 'Rep from Traditional Council' => [ + 'permit_backend' => true, + 'update_permits' => false, + 'drawing_tools' => false, + 'view_drawing_tools' => true, + 'admin_gui' => false, + ], + + 'Chairman of the Works Sub Committee' => [ + 'permit_backend' => true, + 'update_permits' => false, + 'drawing_tools' => false, + 'view_drawing_tools' => true, + 'admin_gui' => false, + ], + 'Chairman of Development Sub Planning Committee' => [ + 'permit_backend' => true, + 'update_permits' => false, + 'drawing_tools' => false, + 'view_drawing_tools' => true, + 'admin_gui' => false, + ], + 'Nominated Elected Assembly Members' => [ + 'permit_backend' => true, + 'update_permits' => false, + 'drawing_tools' => false, + 'view_drawing_tools' => true, + 'admin_gui' => false, + ], +]; + +*/ + + + ?> \ No newline at end of file diff --git a/php_code/info.md b/php_code/info.md index 4851e714..f9e47f25 100644 --- a/php_code/info.md +++ b/php_code/info.md @@ -1,9 +1,9 @@ # Acounts - assiamah/win - kwesilupmis/7aa0478bce/virgin200 -- kwesibanson/virgin200 +- kwesibanson/virgin200 : district user -Username: saxiquzipa/lwCDfAMaBy +Username: saxiquzipa/lwCDfAMaBy : national_luspa composer create-project laravel/laravel:^11.0 my-laravel-11-project @@ -11,6 +11,10 @@ chmod -R 0777 storage/ chmod -R 0777 bootstrap/ +https://lupmis4luspa.org/landing +https://lupmis4luspa.org/landing +afuaabasa / 2jcukPZGiCsf +
  • Username: kwesilupmis
  • Password: 7aa0478bce
  • @@ -35,28 +39,19 @@ add measurements to the backend for works department 216.55.185.131 -ua_id": 10, - "user_id": "34ba702b-18f8-4d85-948d-8c55e8500f32", - "username": "hyhix", - "title": "Mr", - "full_name": "Myles Benton", - "ua_position": "district-works-head", - "email": "baduhupir@mailinator.com", - "phone": "233244678092", - "gender": "male", - "is_disabled": "true", - "is_logged_in": null, - "is_password_changed": "false", - "password_hint": "none", - "created_by_id": null, - "created_date": "2025-09-08T20:06:11.290737", - "updated_by_id": null, - "updated_date": "2025-09-08T20:06:11.290737", - "allowed_apps": "permit-tools", - "region_id": 3, - "district_id": 180, - "user_type": "District User" +## Microsoft Email +Application (client) ID: +8e31f6b1-4a4f-4bb6-9019-aaf64e66143f + +Directory (tenant) ID: +afab14ca-ab60-42a6-8a29-ff101424f318 + +Secret ID: +bd797934-94be-4506-977c-cd65042c9667 + +Secret Value: +Obk8Q~6pnUQEIn3b9xBe~havTyRgn7u5~mZ7Ecan diff --git a/php_code/public/assets/js/add_edit_user.js b/php_code/public/assets/js/add_edit_user.js index f7c2fe66..5a704f48 100644 --- a/php_code/public/assets/js/add_edit_user.js +++ b/php_code/public/assets/js/add_edit_user.js @@ -7,27 +7,25 @@ $(document).ready(function () { } }); + // --- PHONE INPUT LOGIC --- const phoneInstances = {}; function initPhoneInput(selector) { const input = document.querySelector(selector); if (!input) return null; - // If it was already initialized, don't do it again if (phoneInstances[selector]) return phoneInstances[selector]; phoneInstances[selector] = window.intlTelInput(input, { initialCountry: "gh", onlyCountries: ["gh"], - nationalMode: true, // ALLOW users to type 024... natively + nationalMode: true, autoPlaceholder: "polite", utilsScript: "https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/17.0.19/js/utils.js" }); - // Auto-format on blur using the plugin's built-in engine input.addEventListener("blur", function () { if (phoneInstances[selector].isValidNumber()) { - // Reformats to local standard (e.g., 024 123 4567) for UI phoneInstances[selector].setNumber(phoneInstances[selector].getNumber()); } }); @@ -35,10 +33,10 @@ $(document).ready(function () { return phoneInstances[selector]; } - // Initialize inputs when their respective modals open $('#editUserModal').on('shown.bs.modal', () => initPhoneInput("#editPhone")); $('#addUserModal').on('shown.bs.modal', () => initPhoneInput("#addUserphone")); + // --- SELECT2 LOGIC --- $('#editAllowedApps, #inputPermissions').select2({ dropdownParent: $('#editUserModal'), placeholder: "Select options, multiple allowed" @@ -49,48 +47,76 @@ $(document).ready(function () { placeholder: "Select options, multiple allowed" }); - const optionsMap = { - district_user: [ - "PPD Head", "Works Head", "MIS", "PPD Staff", "Works Staff", - "Devt Planning Officer", "MCE", "DCE", "Urban Roads Department Head", - "District Fire Officer", "District Disaster Prevention Department", - "Head of District Health Department", "Representative of the Lands Commission", - "Representative of the Environmental Protection Agency", "Rep from Traditional Council", - "Chairman of the Works Sub Committee", "Chairman of Development Sub Planning Committee", - "Nominated Elected Assembly Memebers" - ], - national_luspa: ["Director", "IT Head", "Staff"], - regional_luspa: ["Director", "Staff"] - }; + + // --- DYNAMIC POSITIONS MAP LOGIC --- + let optionsMap = {}; // Start empty function populatePositions(userType, targetDropdown) { targetDropdown.empty().append(''); if (optionsMap[userType]) { $.each(optionsMap[userType], function(index, value) { - targetDropdown.append($("").attr("value", value.toLowerCase()).text(value)); + targetDropdown.append($("").attr("value", value.toLowerCase()).text(toTitleCase(value))); }); } } + function toSentenceCase(value) { + value = value + .replace(/_+/g, ' ') + .replace(/\s+/g, ' ') + .trim() + .toLowerCase(); + + return value + ? value.charAt(0).toUpperCase() + value.slice(1) + : ''; + } + function toTitleCase(value) { + return value + .replace(/_+/g, ' ') + .replace(/\s+/g, ' ') + .trim() + .toLowerCase() + .replace(/\b\w/g, char => char.toUpperCase()); + } + + + // Fetch the map from the backend on load + $.ajax({ + url: base_url + '/users/positions-map', + type: 'GET', + success: function(response) { + let mapData = response.data || response; + if (mapData && Object.keys(mapData).length > 0) { + optionsMap = mapData; + // Populate if a radio is already checked + let initialUserType = $("input[name='user_type']:checked").val(); + if(initialUserType) { + populatePositions(initialUserType, $("#uaPostionAdd")); + } + } else { + console.warn("The map data is empty or formatted incorrectly."); + } + }, + error: function(xhr, status, error) { + console.error("Failed to load positions map:", error); + console.log(xhr.responseText); // Shows PHP errors if the route crashed + } + }); - let initialUserType = $("input[name='user_type']:checked").val(); - if(initialUserType) populatePositions(initialUserType, $("#uaPostionAdd")); - // Handle Radio Changes $("input[name='user_type']").change(function() { let userType = $(this).val(); let isDistrict = (userType === 'district_user'); let isRegional = (userType === 'regional_luspa'); - //$('#regionID, #editRegionID').prop('disabled', !isDistrict && !isRegional); - //$('#districtID, #editdistrictID').prop('disabled', !isDistrict); - populatePositions(userType, $("#uaPostionAdd")); }); + // --- DEPENDENT DROPDOWNS --- $('.regionIDD').change(function() { - let region_id = $(this).val(); + let region_id = $(this).val(); let $districtSelect = $('.districtIDD'); $districtSelect.empty().append(''); @@ -111,6 +137,7 @@ $(document).ready(function () { }); + // --- EDIT USER LOGIC --- let lastTrigger = null; $(document).on('click', '.editUserBtn', function(evnt) { @@ -120,7 +147,6 @@ $(document).ready(function () { const row = $(this).closest('.user-row'); let selectedUserId = row.data('id'); - // Ensure phone instance exists before trying to set data into it let phoneIti = initPhoneInput("#editPhone"); $.ajax({ @@ -143,12 +169,12 @@ $(document).ready(function () { $('#editUsername').val(user.username); $('#editGender').val(user.gender); $('#editTitle').val(user.title); - $('#editUaPostion').val(user.ua_position); $('#editRegionID').val(user.region_id); $('#editDistrictId').val(user.district_id); $("input[name='user_id']").val(user.user_id); $('#editAllowedApps').val(allowedAppsArray).trigger('change'); + if (user.user_type === 'district_user') { $('#editUserTypeDistrict').prop('checked', true); } @@ -159,12 +185,17 @@ $(document).ready(function () { $('#editUserTypeLuspaNational').prop('checked', true); } + // Populate positions based on the loaded user_type + if(user.user_type) { + populatePositions(user.user_type, $("#editUaPostion")); + } + $('#editUaPostion').val(user.ua_position); + if (phoneIti && user.phone) { phoneIti.setNumber(user.phone); } else { $('#editPhone').val(user.phone); } - // $('#editUserModal').modal('show') } }, error: function(xhr, status, error) { @@ -179,6 +210,7 @@ $(document).ready(function () { }); + // --- FORM SUBMISSION LOGIC --- function handleFormSubmission(formElement, url, successMessage, phoneInstanceSelector) { const $form = $(formElement); const isEdit = formElement.id === 'editUserForm'; @@ -194,17 +226,14 @@ $(document).ready(function () { const formData = new FormData(formElement); let errors = []; - // Validate Phone cleanly if (itiInstance) { if (!itiInstance.isValidNumber()) { errors.push("01|Please enter a valid Ghana phone number."); } else { - // Get the clean E.164 database format (e.g. +233241234567) formData.set("phone", itiInstance.getNumber()); } } - // Validate standard fields const fullName = $form.find('input[name="full_name"], [id*="FullName"], [id*="Fullname"]').val().trim(); const username = $form.find('input[name="username"], [id*="Username"]').val().trim(); const email = $form.find('input[type="email"], [id*="Email"]').val().trim(); @@ -260,7 +289,6 @@ $(document).ready(function () { }); } - $("#editUserForm").on("submit", function (evt) { evt.preventDefault(); handleFormSubmission(this, base_url + '/userupdate', "User details successfully updated!", "#editPhone"); diff --git a/php_code/public/assets/js/permits_index.js b/php_code/public/assets/js/permits_index.js new file mode 100644 index 00000000..365edba9 --- /dev/null +++ b/php_code/public/assets/js/permits_index.js @@ -0,0 +1,30 @@ +$(document).ready(function() { + let searchTimer; + + $('#permitSearch').on('keyup', function() { + clearTimeout(searchTimer); + let query = $(this).val(); + + $('#permitBody').css('opacity', '0.5'); + + searchTimer = setTimeout(function() { + $.ajax({ + url: window.location.pathname, + type: "GET", + data: { search: query }, + success: function(response) { + let html = $(response); + + $('#permitBody').html(html.find('#permitBody').html()); + + $('#permit-pagination').html(html.find('#permit-pagination').html()); + $('#permitBody').css('opacity', '1'); + }, + error: function() { + console.error("Live search failed"); + $('#permitBody').css('opacity', '1'); + } + }); + }, 500); // 500ms delay + }); +}); \ No newline at end of file diff --git a/php_code/resources/views/admin/partials/create-user.blade.php b/php_code/resources/views/admin/partials/create-user.blade.php index 61d8fc7a..3ab00c02 100644 --- a/php_code/resources/views/admin/partials/create-user.blade.php +++ b/php_code/resources/views/admin/partials/create-user.blade.php @@ -3,7 +3,7 @@