From a797d9587c7684a396b5a15ca4af48323d2b2772 Mon Sep 17 00:00:00 2001 From: Kwesi Banson Jnr Date: Thu, 13 Aug 2026 20:12:24 +0000 Subject: [PATCH] bug fix in clients controller --- app/Http/Controllers/ClientsController.php | 95 +++- app/Http/Controllers/HelperController.php | 19 + app/Http/Controllers/code-1784661420553.php | 424 ----------------- app/Models/Client.php | 9 +- countries_new.sql | 283 +++++++++++ public/assets/js/client-index.js | 416 +++++++++++++++++ public/assets/js/client-show-modal.js | 2 +- public/favicon/android-chrome-192x192.png | Bin 0 -> 9486 bytes public/favicon/android-chrome-512x512.png | Bin 0 -> 32029 bytes public/favicon/apple-touch-icon.png | Bin 0 -> 8149 bytes public/favicon/favicon-16x16.png | Bin 0 -> 350 bytes public/favicon/favicon-32x32.png | Bin 0 -> 728 bytes public/favicon/favicon.ico | Bin 0 -> 15406 bytes public/favicon/site.webmanifest | 1 + resources/views/clients/export-pdf.blade.php | 125 +++++ resources/views/clients/index.blade.php | 441 ++---------------- .../views/clients/partials/create.blade.php | 8 +- .../clients/partials/edit-modal.blade.php | 10 +- resources/views/layouts/masterbeta.blade.php | 5 +- routes/web.php | 3 +- 20 files changed, 989 insertions(+), 852 deletions(-) create mode 100644 app/Http/Controllers/HelperController.php delete mode 100644 app/Http/Controllers/code-1784661420553.php create mode 100644 countries_new.sql create mode 100644 public/assets/js/client-index.js create mode 100644 public/favicon/android-chrome-192x192.png create mode 100644 public/favicon/android-chrome-512x512.png create mode 100644 public/favicon/apple-touch-icon.png create mode 100644 public/favicon/favicon-16x16.png create mode 100644 public/favicon/favicon-32x32.png create mode 100644 public/favicon/favicon.ico create mode 100644 public/favicon/site.webmanifest create mode 100644 resources/views/clients/export-pdf.blade.php diff --git a/app/Http/Controllers/ClientsController.php b/app/Http/Controllers/ClientsController.php index 703bbc0..5fa3806 100644 --- a/app/Http/Controllers/ClientsController.php +++ b/app/Http/Controllers/ClientsController.php @@ -13,6 +13,9 @@ use Illuminate\Support\Facades\Config; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Storage; use Illuminate\Support\Str; +use Barryvdh\DomPDF\Facade\Pdf; // Ensure you have dompdf installed if exporting PDFs + + class ClientsController extends Controller { @@ -199,14 +202,18 @@ class ClientsController extends Controller private function buildFilteredQuery(Request $request) { - $query = Models\Client::query(); + // $query = Models\Client::query(); + $query = Models\Client::with('account_manager'); if ($request->filled('search')) { $searchTerm = $request->search; $query->where(function ($q) use ($searchTerm) { $q->where('name', 'like', "%{$searchTerm}%") - ->orWhere('email', 'like', "%{$searchTerm}%") - ->orWhere('contact_person', 'like', "%{$searchTerm}%"); + ->orWhere('email', 'like', "%{$searchTerm}%") + ->orWhere('contact_person', 'like', "%{$searchTerm}%") + ->orWhereHas('account_manager', function($amQuery) use ($searchTerm) { + $amQuery->where('name', 'like', "%{$searchTerm}%"); + }); }); } @@ -240,17 +247,85 @@ class ClientsController extends Controller /** * Handle the Export Request */ + + public function export(Request $request) { - // Get the filtered data but execute get() instead of paginate() - $clients = $this->buildFilteredQuery($request)->get(); - $format = $request->input('format', 'csv'); + // Build query using the exact same filters as your data table + // $query = Models\Client::query(); + $query = Models\Client::with('account_manager'); - if ($format === 'pdf') { - return $this->exportToPdf($clients); + if ($request->filled('search')) { + $search = $request->search; + $query->where(function($q) use ($search) { + $q->where('name', 'like', "%{$search}%") + ->orWhere('email', 'like', "%{$search}%") + ->orWhere('contact_person', 'like', "%{$search}%") + ->orWhereHas('account_manager', function($amQuery) use ($search) { + $amQuery->where('name', 'like', "%{$search}%"); + }); + }); } - return $this->exportToCsv($clients); + if ($request->filled('service')) { + $query->whereJsonContains('services', $request->service); + } + + if ($request->filled('billing')) { + $query->where('pay_mode', $request->billing); + } + + if ($request->filled('status')) { + $query->where('status', $request->status); + } + + $clients = $query->get(); + $format = $request->get('format', 'csv'); + + // Handle CSV Export + if ($format === 'csv') { + $filename = 'clients_export_' . date('Y-m-d') . '.csv'; + + $headers = [ + "Content-Type" => "text/csv", + "Content-Disposition" => "attachment; filename=\"$filename\"", + "Pragma" => "no-cache", + "Cache-Control" => "must-revalidate, post-check=0, pre-check=0", + "Expires" => "0" + ]; + + $callback = function() use ($clients) { + $file = fopen('php://output', 'w'); + // CSV Header Row + fputcsv($file, ['ID', 'Client Name', 'Primary Email', 'Phone', 'Contact Person', 'Account Manager', 'Pay Mode', 'Status', 'Country', 'Date Added']); + + foreach ($clients as $client) { + fputcsv($file, [ + $client->id, + $client->name, + $client->email, + $client->phone, + $client->contact_person, + $client->account_manager->name, + $client->pay_mode, + $client->status, + $client->country, + $client->created_at, + ]); + } + fclose($file); + }; + + return response()->stream($callback, 200, $headers); + } + + // Handle PDF Export (Optional: requires barryvdh/laravel-dompdf) + if ($format === 'pdf') { + $pdf = Pdf::loadView('clients.export-pdf', compact('clients')); + return $pdf->download('clients_export_' . date('Y-m-d') . '.pdf'); + } + + return redirect()->back(); } /** @@ -370,7 +445,7 @@ class ClientsController extends Controller 'last_modified_by_id' => Auth::user()->id ]); } - $user_id = $auth_user->id; + $user_id = Auth::user()->id; Models\UserActivity::create([ 'type' => 'staff', diff --git a/app/Http/Controllers/HelperController.php b/app/Http/Controllers/HelperController.php new file mode 100644 index 0000000..f74a6b4 --- /dev/null +++ b/app/Http/Controllers/HelperController.php @@ -0,0 +1,19 @@ +orderBy('en_short_name', 'asc') + ->select('en_short_name') + ->get(); + + return response()->json($countries); + } +} diff --git a/app/Http/Controllers/code-1784661420553.php b/app/Http/Controllers/code-1784661420553.php deleted file mode 100644 index cbc1356..0000000 --- a/app/Http/Controllers/code-1784661420553.php +++ /dev/null @@ -1,424 +0,0 @@ - 'Clients', - 'current_user' => session('current_user') - ]); - } - - public function indexInactive(): View - { - return view('client.index-inactive', [ - 'page_title' => 'Inactive Clients', - 'current_user' => session('current_user') - ]); - } - - public function indexStatus($status): View - { - return view('client.index-status', [ - 'status' => $status, - 'page_title' => strtoupper($status) . ' Clients', - 'current_user' => session('current_user') - ]); - } - - public function indexCancelled(): View - { - return view('client.index-prospective', [ - 'page_title' => 'Prospective Clients', - 'current_user' => session('current_user') - ]); - } - - public function indexDiscussion(): View - { - return view('client.index-prospective', [ - 'page_title' => 'In Discussion Clients', - 'current_user' => session('current_user') - ]); - } - - public function getClientJson(Request $request): JsonResponse - { - $query = DB::table('clients') - ->join('auth_users AS aumngr', 'aumngr.id', '=', 'clients.auth_user_id') - ->join('auth_users AS aumodify', 'aumodify.id', '=', 'clients.last_modified_by') - ->join('flags AS flags', 'flags.country', '=', 'clients.country') - ->select('clients.id', 'clients.name', 'clients.status', 'clients.progress_indicator_score', 'clients.country', 'aumngr.name As accountMgr', 'aumodify.name AS modifiedBy', 'flags.url AS theflag') - ->whereNotIn('status', ['inactive', 'Cancelled']); - - if ($request->filled('keyword')) { - $keyword = $request->keyword; - $query->where(function ($q) use ($keyword) { - $q->where('clients.name', 'LIKE', "%{$keyword}%") - ->orWhere('clients.country', 'LIKE', "%{$keyword}%") - ->orWhere('aumngr.name', 'LIKE', "%{$keyword}%") - ->orWhere('aumodify.name', 'LIKE', "%{$keyword}%"); - }); - } - - return response()->json($query->orderBy('name', 'ASC')->get()); - } - - public function getInactiveClientJson(Request $request): JsonResponse - { - $query = DB::table('clients') - ->join('auth_users AS aumngr', 'aumngr.id', '=', 'clients.auth_user_id') - ->join('auth_users AS aumodify', 'aumodify.id', '=', 'clients.last_modified_by') - ->join('flags AS flags', 'flags.country', '=', 'clients.country') - ->select('clients.id', 'clients.name', 'clients.status', 'clients.progress_indicator_score', 'clients.country', 'aumngr.name As accountMgr', 'aumodify.name AS modifiedBy', 'flags.url AS theflag') - ->whereIn('status', ['inactive', 'Cancelled']); - - if ($request->filled('keyword')) { - $keyword = $request->keyword; - $query->where(function ($q) use ($keyword) { - $q->where('clients.name', 'LIKE', "%{$keyword}%") - ->orWhere('clients.status', 'LIKE', "%{$keyword}%") - ->orWhere('clients.country', 'LIKE', "%{$keyword}%") - ->orWhere('aumngr.name', 'LIKE', "%{$keyword}%") - ->orWhere('aumodify.name', 'LIKE', "%{$keyword}%") - ->orWhere('clients.progress_indicator_score', 'LIKE', "%{$keyword}%"); - }); - } - - return response()->json($query->orderBy('name', 'ASC')->get()); - } - - public function create(): View - { - return view('client.create', [ - 'page_title' => 'Create Client', - 'countries' => Models\Country::pluck('en_short_name', 'en_short_name'), - 'service_type' => Models\Service::pluck('name', 'name'), - 'status' => ['Live' => 'Live', 'Inactive' => 'Inactive', 'Prospective' => 'Prospective', 'Cancelled' => 'Cancelled'], - 'currency' => Models\Currency::pluck('name', 'name'), - 'auth_users' => Models\SystemUser::pluck('name', 'id'), - 'payment_type' => ['Prepaid' => 'Prepaid', 'Postpaid' => 'Postpaid'], - 'company_types' => ['Aggregator/Supplier' => 'Aggregator/Supplier', 'Enterprise' => 'Enterprise', 'Hybrid' => 'Hybrid'], - 'industries' => Models\Industry::orderBy('name', 'ASC')->pluck('name', 'name') - ]); - } - - public function store(Request $request): RedirectResponse - { - $request->validate([ - 'name' => 'required|unique:clients,name', - 'email' => 'required|email', - 'services' => 'required|array', - 'country' => 'required', - 'status' => 'required', - 'payment_mode' => 'required', - 'currency' => 'required', - 'company_type' => 'required', - 'industry' => 'required', - 'auth_user_id' => 'required', - ]); - - $onboarding_stages = Models\ClientOnboardingMainStage::orderBy('stage_id')->get(); - $client_current_stages = []; - - foreach ($onboarding_stages as $value) { - $client_current_stages[$value->stage] = "PENDING"; - } - - $client_arr = [ - 'name' => $request->name, - 'email' => $request->email, - 'country' => $request->country, - 'status' => $request->status, - 'pay_mode' => $request->payment_mode, - 'currency' => $request->currency, - 'auth_user_id' => $request->auth_user_id, - 'created_by' => session('current_user.id'), - 'last_modified_by' => session('current_user.id'), - 'progress_indicator_score' => 10, - 'progress_indicator' => $onboarding_stages[0]->stage, - 'onboarding_progress_stage' => json_encode($client_current_stages), - 'notes' => $request->notes, - 'services' => json_encode($request->services), - 'phone' => $request->phone, - 'skype_name' => $request->skype_name, - 'linkedin_name' => $request->linkedin_name, - 'contact_person' => $request->contact_person, - 'company_type' => $request->company_type, - 'industry' => $request->industry, - ]; - - $result = Models\Client::create(array_filter($client_arr)); // Remove nulls automatically - - $get_stage_subs_items = Models\ClientOnboardingSubItem::get(); - foreach ($get_stage_subs_items as $value) { - Models\ClientOnboardingProgress::create([ - 'stage_id' => $value->stage_id, - 'client_id' => $result->id, - 'name' => $value->name, - 'status' => 'PENDING' - ]); - } - - if (in_array('USSD', $request->services)) { - Log::info('ussd client detected'); - Models\UssdClientPayment::create([ - 'client_id' => $result->id, - 'last_modified_by_id' => session('current_user.id') - ]); - } - - Models\UserActivity::create([ - 'type' => 'staff', - 'content' => session('current_user.name') . " added a new client ({$result->name}) successfully!", - 'user_id' => session('current_user.id'), - 'ip_address' => request()->ip(), - 'device' => $request->header('User-Agent') - ]); - - Session::flash('success_message', 'Client successfully added'); - return redirect('clients'); - } - - public function show($id): View - { - $showclient = Models\Client::with('service_info', 'country_flag_info', 'auth_user_info', 'short_code_info')->findOrFail($id); - - $networks_raw = [ - 'AirtelTigo GH', 'MTN GH', 'Airtel MW', 'Airtel Zambia', 'TNM MW', - 'Safaricom Kenya', 'Airtel Kenya', 'Telkom Kenya', 'Orange Kenya' - ]; - sort($networks_raw); - - $renewal_due = 'N/A'; - $highlight_colour = 'none'; - - if (!empty($showclient->contract_validity)) { - $expiry_date = Carbon::parse($showclient->contract_validity); - $current_date = Carbon::today(); - $days = $current_date->diffInDays($expiry_date, false); // False allows negative values for past dates - - if ($days < 0) { - $highlight_colour = 'warning'; - $days_abs = abs($days); - if ($days_abs > 365) $renewal_due = "Contract expired " . floor($days_abs / 365) . " year(s) ago"; - elseif ($days_abs > 31) $renewal_due = "Contract expired " . floor($days_abs / 31) . " month(s) ago"; - else $renewal_due = "Contract expired {$days_abs} days(s) ago"; - } else { - if ($days > 365) $renewal_due = "In " . floor($days / 365) . " year(s)"; - elseif ($days > 31) $renewal_due = "In " . floor($days / 31) . " months"; - else $renewal_due = "In {$days} day(s)"; - } - } - - $change_account_mgr_permission = Config::get('permissions.CHANGE_ACCOUNT_MANAGERS'); - $has_permission = $this->hasAnyAccess([$change_account_mgr_permission]) ? 'YES' : 'NO'; - - return view('client.show_accordion', [ - 'page_title' => 'Client Profile', - 'showclient' => $showclient, - 'show_services' => Models\ClientCategory::where('client_id', $id)->get(), - 'service_type' => Models\Service::pluck('name', 'id'), - 'service_type_names' => Models\Service::pluck('name', 'name'), - 'show_notes' => Models\ClientNote::with('created_by_info', 'client_info')->where('client_id', $id)->latest()->take(20)->get(), - 'show_notes_highlight' => Models\ClientNote::with('created_by_info', 'client_info')->where('client_id', $id)->where('highlight', 'YES')->latest()->take(1)->get(), - 'status_bg' => $showclient->status == 'Live' ? 'info' : ($showclient->status == 'Prospective' ? 'warning' : 'danger'), - 'progress_status_bg' => $showclient->progress_indicator_score >= 70 ? 'success' : ($showclient->progress_indicator_score >= 50 ? 'warning' : 'danger'), - 'voice_codes' => Models\ClientShortCode::where('client_id', $id)->where('code_type', 'voice')->get(), - 'sms_codes' => Models\ClientShortCode::where('client_id', $id)->where('code_type', 'sms')->get(), - 'ussd_codes' => Models\ClientShortCode::where('client_id', $id)->where('code_type', 'ussd')->get(), - 'countries' => Models\Country::pluck('en_short_name', 'en_short_name'), - 'networks' => Models\NetworkOps::pluck('name', 'id'), - 'progress_indicators' => Models\ClientIndicator::pluck('name', 'name'), - 'networks_raw' => array_combine($networks_raw, $networks_raw), - 'renewal_due' => $renewal_due, - 'recent_payments' => Models\ClientPayment::where('client_id', $id)->latest()->get(), - 'highlight_colour' => $highlight_colour, - 'showdocuments' => Models\ClientFile::where('client_id', $id)->get(), - 'support_fees' => Models\ClientSupportFees::where('client_id', $id)->latest()->get(), - 'recurring_arr' => ['NO' => 'NO', 'Monthly' => 'Monthly', 'Quarterly' => 'Quarterly', 'Semiannual' => 'Semiannual', 'Yearly' => 'Yearly'], - 'change_account_mgr_permisson' => $has_permission, - 'am_list_arr' => Models\SystemUser::orderBy('name', 'ASC')->pluck('name', 'id'), - 'country_network_arr' => DB::table('network_operators')->selectRaw('id, concat(name, " (", country, ")") AS network')->orderBy('network')->pluck('network', 'network'), - 'mnos_arr' => ['' => '-- Select Country first --'] - ]); - } - - public function update(Requests\UpdateClientRequest $request, $id): RedirectResponse - { - $client_update = Models\Client::findOrFail($id); - $paperless = new PaperLessNgx(); - - if ($client_update->progress_indicator != 'COMPLETED') { - $current_pending_stage_details = Models\ClientOnboardingMainStage::where('stage', $request->current_pending_stage)->first(); - $get_stage_subs_items = Models\ClientOnboardingSubItem::where('stage_id', $current_pending_stage_details->stage_id)->get(); - - if ($request->onboarding_sub_items_progress) { - foreach ($request->onboarding_sub_items_progress as $value) { - Models\ClientOnboardingProgress::updateOrCreate( - ['stage_id' => $current_pending_stage_details->stage_id, 'client_id' => $id, 'name' => $value], - ['status' => 'COMPLETED'] - ); - } - } - - $get_stage_onboarding_status = Models\ClientOnboardingProgress::where('client_id', $id) - ->where('stage_id', $current_pending_stage_details->stage_id) - ->where('status', 'COMPLETED') - ->get(); - - $onboarding_progress_stage = json_decode($client_update->onboarding_progress_stage, true); - - if (count($get_stage_subs_items) == count($get_stage_onboarding_status)) { - $onboarding_progress_stage[$current_pending_stage_details->stage] = 'COMPLETED'; - } - - $pending_stage = Arr::where($onboarding_progress_stage, fn($value) => $value == "PENDING"); - - if (!empty($pending_stage)) { - $client_update->progress_indicator = array_key_first($pending_stage); - } else { - $client_update->progress_indicator = 'COMPLETED'; - dispatch(new SendOnboardingCompletedEmailAlert(Models\Client::with('auth_user_info')->find($id))); - } - } - - // File handling logic encapsulated safely - $fileFields = [ - 'document_one' => 'document_one_name', - 'document_two' => 'document_two_name', - 'document_three' => 'document_three_name', - 'other_document' => 'other_document_name' - ]; - - foreach ($fileFields as $fileInput => $nameInput) { - if ($request->hasFile($fileInput) && $request->file($fileInput)->isValid() && $request->filled($nameInput)) { - $filename = "erp_" . time() . Str::random(4) . "." . $request->file($fileInput)->extension(); - $request->file($fileInput)->storeAs('client_files', $filename, 'public'); - - $document_name = $fileInput === 'other_document' && !$request->filled($nameInput) - ? 'Other Document' - : $request->input($nameInput); - - Models\ClientFile::updateOrCreate( - ['client_id' => $id, 'name' => $document_name], - [ - 'file_path' => $filename, - 'file_extension' => $request->file($fileInput)->extension(), - 'file_reff' => time() . uniqid(), - 'created_by' => session('current_user.id'), - ] - ); - } - } - - $client_update->name = $request->name; - $client_update->email = $request->email; - $client_update->phone = $request->phone ?? ""; - $client_update->contact_person = $request->contact_person; - $client_update->status = $request->status; - $client_update->pay_mode = $request->payment_mode; - $client_update->country = $request->country; - $client_update->currency = $request->currency; - $client_update->notes = $request->notes; - $client_update->industry = $request->industry; - - if ($client_update->progress_indicator != 'COMPLETED') { - $client_update->onboarding_progress_stage = json_encode($onboarding_progress_stage); - $progress_breakdown = array_count_values($onboarding_progress_stage); - - $indicator_score = Arr::has($progress_breakdown, 'COMPLETED') - ? ($progress_breakdown['COMPLETED'] / count($onboarding_progress_stage)) * 100 - : 0; - - $client_update->progress_indicator_score = number_format($indicator_score); - } else { - $client_update->progress_indicator_score = 100; - } - - $client_update->skype_name = $request->skype_name ?? ""; - $client_update->linkedin_name = $request->linkedin_name ?? ""; - $client_update->smpp_username = $request->smpp_username ?? ""; - $client_update->company_type = $request->company_type ?? ""; - $client_update->auth_user_id = $request->auth_user_id ?? ""; - $client_update->contract_type = $request->contract_type ?? ""; - $client_update->contract_validity = $request->contract_validity ?? ""; - $client_update->contract_auto_renew = $request->contract_auto_renew ?? ""; - $client_update->sender_ids = $request->sender_ids ? json_encode($request->sender_ids) : ""; - $client_update->connections = $request->connections ? json_encode($request->connections) : ""; - $client_update->services = $request->services ? json_encode($request->services) : ""; - $client_update->message_types = $request->message_types ? json_encode($request->message_types) : ""; - $client_update->finance_email = $request->finance_email ? json_encode($request->finance_email) : ""; - $client_update->support_emails = $request->support_emails ? json_encode($request->support_emails) : ""; - $client_update->rate_emails = $request->rate_emails ? json_encode($request->rate_emails) : ""; - $client_update->support_phones = $request->support_phones ? json_encode($request->support_phones) : ""; - $client_update->support_skype = $request->support_skype ? json_encode($request->support_skype) : ""; - - if ($request->has('how_we_got_client')) { - $client_update->how_we_got_client = $request->how_we_got_client == 'Other' ? ($request->how_we_got_client_other ?? "") : $request->how_we_got_client; - } - - $client_update->last_modified_by = session('current_user.id'); - $client_update->save(); - - Models\UserActivity::create([ - 'type' => 'staff', - 'content' => session('current_user.name') . " updated ({$client_update->name}) details successfully!", - 'user_id' => session('current_user.id'), - 'ip_address' => request()->ip(), - 'device' => $request->header('User-Agent') - ]); - - Session::flash('success_message', 'Client successfully Updated'); - return redirect(url('clients', $id)); - } - - public function getClientFile($id) - { - $client_file = Models\ClientFile::with('client_info')->findOrFail($id); - - // Use proper storage path logic - $file = storage_path('app/public/client_files/' . $client_file->file_path); - - if (!file_exists($file)) { - abort(404, 'File not found on the server.'); - } - - $filename = Str::slug($client_file->client_info->name . "_" . $client_file->name, '_'); - $filename = $filename . "." . $client_file->file_extension; - - return Response::download($file, $filename); - } - - public function cleanStr($string): string - { - // Replaced custom regex block with Laravel's native slug helper - return Str::slug($string, '_'); - } - - // ... Additional unchanged methods (getMnoCountry, etc.) follow below. -} \ No newline at end of file diff --git a/app/Models/Client.php b/app/Models/Client.php index 1b818a8..290a2f8 100644 --- a/app/Models/Client.php +++ b/app/Models/Client.php @@ -29,13 +29,16 @@ class Client extends Model return $this->hasMany('App\Models\MeetingReport', 'client', 'id'); } public function auth_user_info(){ - return $this->hasOne('App\Models\SystemUser', 'id', 'auth_user_id'); + return $this->hasOne('App\Models\StaffMember', 'id', 'auth_user_id'); + } + public function account_manager(){ + return $this->hasOne('App\Models\StaffMember', 'id', 'auth_user_id'); } public function created_by_info(){ - return $this->hasOne('App\Models\SystemUser', 'id', 'created_by'); + return $this->hasOne('App\Models\StaffMember', 'id', 'created_by'); } public function modified_by_info(){ - return $this->hasOne('App\Models\SystemUser', 'id', 'last_modified_by'); + return $this->hasOne('App\Models\StaffMember', 'id', 'last_modified_by'); } public function short_code_info(){ return $this->hasMany('App\Models\ShortCode', 'client_id', 'id'); diff --git a/countries_new.sql b/countries_new.sql new file mode 100644 index 0000000..9c1fe41 --- /dev/null +++ b/countries_new.sql @@ -0,0 +1,283 @@ +-- Create table +CREATE TABLE countries_new ( + num_code INT PRIMARY KEY, + alpha_2_code CHAR(2) NOT NULL, + alpha_3_code CHAR(3) NOT NULL, + en_short_name VARCHAR(100) NOT NULL, + nationality VARCHAR(100) NOT NULL +); + +-- Insert countries_new (chunk 1: 1–50) +INSERT INTO countries_new (num_code, alpha_2_code, alpha_3_code, en_short_name, nationality) VALUES +(4, 'AF', 'AFG', 'Afghanistan', 'Afghan'), +(8, 'AL', 'ALB', 'Albania', 'Albanian'), +(10, 'AQ', 'ATA', 'Antarctica', 'Antarctic'), +(12, 'DZ', 'DZA', 'Algeria', 'Algerian'), +(16, 'AS', 'ASM', 'American Samoa', 'American Samoan'), +(20, 'AD', 'AND', 'Andorra', 'Andorran'), +(24, 'AO', 'AGO', 'Angola', 'Angolan'), +(28, 'AG', 'ATG', 'Antigua and Barbuda', 'Antiguan'), +(31, 'AZ', 'AZE', 'Azerbaijan', 'Azerbaijani'), +(32, 'AR', 'ARG', 'Argentina', 'Argentine'), +(36, 'AU', 'AUS', 'Australia', 'Australian'), +(40, 'AT', 'AUT', 'Austria', 'Austrian'), +(44, 'BS', 'BHS', 'Bahamas', 'Bahamian'), +(48, 'BH', 'BHR', 'Bahrain', 'Bahraini'), +(50, 'BD', 'BGD', 'Bangladesh', 'Bangladeshi'), +(51, 'AM', 'ARM', 'Armenia', 'Armenian'), +(52, 'BB', 'BRB', 'Barbados', 'Barbadian'), +(56, 'BE', 'BEL', 'Belgium', 'Belgian'), +(60, 'BM', 'BMU', 'Bermuda', 'Bermudian'), +(64, 'BT', 'BTN', 'Bhutan', 'Bhutanese'), +(68, 'BO', 'BOL', 'Bolivia', 'Bolivian'), +(70, 'BA', 'BIH', 'Bosnia and Herzegovina', 'Bosnian'), +(72, 'BW', 'BWA', 'Botswana', 'Botswanan'), +(74, 'BV', 'BVT', 'Bouvet Island', 'Bouvet Islander'), +(76, 'BR', 'BRA', 'Brazil', 'Brazilian'), +(84, 'BZ', 'BLZ', 'Belize', 'Belizean'), +(86, 'IO', 'IOT', 'British Indian Ocean Territory', 'BIOT'), +(90, 'SB', 'SLB', 'Solomon Islands', 'Solomon Islander'), +(92, 'VG', 'VGB', 'British Virgin Islands', 'British Virgin Islander'), +(96, 'BN', 'BRN', 'Brunei Darussalam', 'Bruneian'), +(100, 'BG', 'BGR', 'Bulgaria', 'Bulgarian'), +(104, 'MM', 'MMR', 'Myanmar', 'Burmese'), +(108, 'BI', 'BDI', 'Burundi', 'Burundian'), +(112, 'BY', 'BLR', 'Belarus', 'Belarusian'), +(116, 'KH', 'KHM', 'Cambodia', 'Cambodian'), +(120, 'CM', 'CMR', 'Cameroon', 'Cameroonian'), +(124, 'CA', 'CAN', 'Canada', 'Canadian'), +(132, 'CV', 'CPV', 'Cabo Verde', 'Cape Verdean'), +(136, 'KY', 'CYM', 'Cayman Islands', 'Caymanian'), +(140, 'CF', 'CAF', 'Central African Republic', 'Central African'), +(144, 'LK', 'LKA', 'Sri Lanka', 'Sri Lankan'), +(148, 'TD', 'TCD', 'Chad', 'Chadian'), +(152, 'CL', 'CHL', 'Chile', 'Chilean'), +(156, 'CN', 'CHN', 'China', 'Chinese'), +(158, 'TW', 'TWN', 'Taiwan', 'Taiwanese'), +(162, 'CX', 'CXR', 'Christmas Island', 'Christmas Islander'), +(166, 'CC', 'CCK', 'Cocos Islands', 'Cocos Islander'); + + + +-- Insert countries_new (chunk 2: 51–100) +INSERT INTO countries_new (num_code, alpha_2_code, alpha_3_code, en_short_name, nationality) VALUES +(170, 'CO', 'COL', 'Colombia', 'Colombian'), +(174, 'KM', 'COM', 'Comoros', 'Comorian'), +(175, 'YT', 'MYT', 'Mayotte', 'Mahoran'), +(178, 'CG', 'COG', 'Congo', 'Congolese'), +(180, 'CD', 'COD', 'Democratic Republic of the Congo', 'Congolese'), +(184, 'CK', 'COK', 'Cook Islands', 'Cook Islander'), +(188, 'CR', 'CRI', 'Costa Rica', 'Costa Rican'), +(191, 'HR', 'HRV', 'Croatia', 'Croatian'), +(192, 'CU', 'CUB', 'Cuba', 'Cuban'), +(196, 'CY', 'CYP', 'Cyprus', 'Cypriot'), +(203, 'CZ', 'CZE', 'Czech Republic', 'Czech'), +(204, 'BJ', 'BEN', 'Benin', 'Beninese'), +(208, 'DK', 'DNK', 'Denmark', 'Danish'), +(212, 'DM', 'DMA', 'Dominica', 'Dominican'), +(214, 'DO', 'DOM', 'Dominican Republic', 'Dominican'), +(218, 'EC', 'ECU', 'Ecuador', 'Ecuadorian'), +(222, 'SV', 'SLV', 'El Salvador', 'Salvadoran'), +(226, 'GQ', 'GNQ', 'Equatorial Guinea', 'Equatoguinean'), +(231, 'ET', 'ETH', 'Ethiopia', 'Ethiopian'), +(232, 'ER', 'ERI', 'Eritrea', 'Eritrean'), +(233, 'EE', 'EST', 'Estonia', 'Estonian'), +(234, 'FO', 'FRO', 'Faroe Islands', 'Faroese'), +(238, 'FK', 'FLK', 'Falkland Islands', 'Falkland Islander'), +(239, 'GS', 'SGS', 'South Georgia and the South Sandwich Islands', 'South Georgian'), +(242, 'FJ', 'FJI', 'Fiji', 'Fijian'), +(246, 'FI', 'FIN', 'Finland', 'Finnish'), +(248, 'AX', 'ALA', 'Åland Islands', 'Åland Islander'), +(250, 'FR', 'FRA', 'France', 'French'), +(254, 'GF', 'GUF', 'French Guiana', 'French Guianese'), +(258, 'PF', 'PYF', 'French Polynesia', 'French Polynesian'), +(260, 'TF', 'ATF', 'French Southern Territories', 'French Southern Territories'), +(262, 'DJ', 'DJI', 'Djibouti', 'Djiboutian'), +(266, 'GA', 'GAB', 'Gabon', 'Gabonese'), +(268, 'GE', 'GEO', 'Georgia', 'Georgian'), +(270, 'GM', 'GMB', 'Gambia', 'Gambian'), +(275, 'PS', 'PSE', 'Palestine', 'Palestinian'), +(276, 'DE', 'DEU', 'Germany', 'German'), +(288, 'GH', 'GHA', 'Ghana', 'Ghanaian'), +(292, 'GI', 'GIB', 'Gibraltar', 'Gibraltarian'), +(296, 'KI', 'KIR', 'Kiribati', 'I-Kiribati'), +(300, 'GR', 'GRC', 'Greece', 'Greek'), +(304, 'GL', 'GRL', 'Greenland', 'Greenlandic'), +(308, 'GD', 'GRD', 'Grenada', 'Grenadian'), +(312, 'GP', 'GLP', 'Guadeloupe', 'Guadeloupean'), +(316, 'GU', 'GUM', 'Guam', 'Guamanian'), +(320, 'GT', 'GTM', 'Guatemala', 'Guatemalan'); + + + +-- Insert countries_new (chunk 3: 101–150) +INSERT INTO countries_new (num_code, alpha_2_code, alpha_3_code, en_short_name, nationality) VALUES +(324, 'GN', 'GIN', 'Guinea', 'Guinean'), +(328, 'GY', 'GUY', 'Guyana', 'Guyanese'), +(332, 'HT', 'HTI', 'Haiti', 'Haitian'), +(334, 'HM', 'HMD', 'Heard Island and McDonald Islands', 'Heard Islander'), +(336, 'VA', 'VAT', 'Vatican City', 'Vatican'), +(340, 'HN', 'HND', 'Honduras', 'Honduran'), +(344, 'HK', 'HKG', 'Hong Kong', 'Hong Konger'), +(348, 'HU', 'HUN', 'Hungary', 'Hungarian'), +(352, 'IS', 'ISL', 'Iceland', 'Icelandic'), +(356, 'IN', 'IND', 'India', 'Indian'), +(360, 'ID', 'IDN', 'Indonesia', 'Indonesian'), +(364, 'IR', 'IRN', 'Iran', 'Iranian'), +(368, 'IQ', 'IRQ', 'Iraq', 'Iraqi'), +(372, 'IE', 'IRL', 'Ireland', 'Irish'), +(376, 'IL', 'ISR', 'Israel', 'Israeli'), +(380, 'IT', 'ITA', 'Italy', 'Italian'), +(384, 'CI', 'CIV', 'Côte d''Ivoire', 'Ivorian'), +(388, 'JM', 'JAM', 'Jamaica', 'Jamaican'), +(392, 'JP', 'JPN', 'Japan', 'Japanese'), +(398, 'KZ', 'KAZ', 'Kazakhstan', 'Kazakhstani'), +(400, 'JO', 'JOR', 'Jordan', 'Jordanian'), +(404, 'KE', 'KEN', 'Kenya', 'Kenyan'), +(408, 'KP', 'PRK', 'North Korea', 'North Korean'), +(410, 'KR', 'KOR', 'South Korea', 'South Korean'), +(414, 'KW', 'KWT', 'Kuwait', 'Kuwaiti'), +(417, 'KG', 'KGZ', 'Kyrgyzstan', 'Kyrgyzstani'), +(418, 'LA', 'LAO', 'Laos', 'Laotian'), +(422, 'LB', 'LBN', 'Lebanon', 'Lebanese'), +(426, 'LS', 'LSO', 'Lesotho', 'Basotho'), +(428, 'LV', 'LVA', 'Latvia', 'Latvian'), +(430, 'LR', 'LBR', 'Liberia', 'Liberian'), +(434, 'LY', 'LBY', 'Libya', 'Libyan'), +(438, 'LI', 'LIE', 'Liechtenstein', 'Liechtensteiner'), +(440, 'LT', 'LTU', 'Lithuania', 'Lithuanian'), +(442, 'LU', 'LUX', 'Luxembourg', 'Luxembourger'), +(446, 'MO', 'MAC', 'Macao', 'Macanese'), +(450, 'MG', 'MDG', 'Madagascar', 'Malagasy'), +(454, 'MW', 'MWI', 'Malawi', 'Malawian'), +(458, 'MY', 'MYS', 'Malaysia', 'Malaysian'), +(462, 'MV', 'MDV', 'Maldives', 'Maldivian'), +(466, 'ML', 'MLI', 'Mali', 'Malian'), +(470, 'MT', 'MLT', 'Malta', 'Maltese'), +(474, 'MQ', 'MTQ', 'Martinique', 'Martiniquais'), +(478, 'MR', 'MRT', 'Mauritania', 'Mauritanian'), +(480, 'MU', 'MUS', 'Mauritius', 'Mauritian'), +(484, 'MX', 'MEX', 'Mexico', 'Mexican'), +(492, 'MC', 'MCO', 'Monaco', 'Monégasque'), +(496, 'MN', 'MNG', 'Mongolia', 'Mongolian'); + + +-- Insert countries_new (chunk 4: 151–200) +INSERT INTO countries_new (num_code, alpha_2_code, alpha_3_code, en_short_name, nationality) VALUES +(498, 'MD', 'MDA', 'Moldova', 'Moldovan'), +(499, 'ME', 'MNE', 'Montenegro', 'Montenegrin'), +(500, 'MS', 'MSR', 'Montserrat', 'Montserratian'), +(504, 'MA', 'MAR', 'Morocco', 'Moroccan'), +(508, 'MZ', 'MOZ', 'Mozambique', 'Mozambican'), +(512, 'OM', 'OMN', 'Oman', 'Omani'), +(516, 'NA', 'NAM', 'Namibia', 'Namibian'), +(520, 'NR', 'NRU', 'Nauru', 'Nauruan'), +(524, 'NP', 'NPL', 'Nepal', 'Nepali'), +(528, 'NL', 'NLD', 'Netherlands', 'Dutch'), +(531, 'CW', 'CUW', 'Curaçao', 'Curaçaoan'), +(533, 'AW', 'ABW', 'Aruba', 'Aruban'), +(534, 'SX', 'SXM', 'Sint Maarten', 'Sint Maartener'), +(535, 'BQ', 'BES', 'Bonaire, Sint Eustatius and Saba', 'Bonairean'), +(540, 'NC', 'NCL', 'New Caledonia', 'New Caledonian'), +(548, 'VU', 'VUT', 'Vanuatu', 'Ni-Vanuatu'), +(554, 'NZ', 'NZL', 'New Zealand', 'New Zealander'), +(558, 'NI', 'NIC', 'Nicaragua', 'Nicaraguan'), +(562, 'NE', 'NER', 'Niger', 'Nigerien'), +(566, 'NG', 'NGA', 'Nigeria', 'Nigerian'), +(570, 'NU', 'NIU', 'Niue', 'Niuean'), +(574, 'NF', 'NFK', 'Norfolk Island', 'Norfolk Islander'), +(578, 'NO', 'NOR', 'Norway', 'Norwegian'), +(580, 'MP', 'MNP', 'Northern Mariana Islands', 'Northern Marianan'), +(581, 'UM', 'UMI', 'United States Minor Outlying Islands', 'American'), +(583, 'FM', 'FSM', 'Micronesia', 'Micronesian'), +(584, 'MH', 'MHL', 'Marshall Islands', 'Marshallese'), +(585, 'PW', 'PLW', 'Palau', 'Palauan'), +(586, 'PK', 'PAK', 'Pakistan', 'Pakistani'), +(591, 'PA', 'PAN', 'Panama', 'Panamanian'), +(598, 'PG', 'PNG', 'Papua New Guinea', 'Papua New Guinean'), +(600, 'PY', 'PRY', 'Paraguay', 'Paraguayan'), +(604, 'PE', 'PER', 'Peru', 'Peruvian'), +(608, 'PH', 'PHL', 'Philippines', 'Filipino'), +(612, 'PN', 'PCN', 'Pitcairn Islands', 'Pitcairn Islander'), +(616, 'PL', 'POL', 'Poland', 'Polish'), +(620, 'PT', 'PRT', 'Portugal', 'Portuguese'), +(624, 'GW', 'GNB', 'Guinea-Bissau', 'Bissau-Guinean'), +(626, 'TL', 'TLS', 'Timor-Leste', 'Timorese'), +(630, 'PR', 'PRI', 'Puerto Rico', 'Puerto Rican'), +(634, 'QA', 'QAT', 'Qatar', 'Qatari'), +(638, 'RE', 'REU', 'Réunion', 'Réunionese'), +(642, 'RO', 'ROU', 'Romania', 'Romanian'), +(643, 'RU', 'RUS', 'Russia', 'Russian'), +(646, 'RW', 'RWA', 'Rwanda', 'Rwandan'); + + + +-- Insert countries_new (chunk 5: 201–250) +INSERT INTO countries_new (num_code, alpha_2_code, alpha_3_code, en_short_name, nationality) VALUES +(652, 'BL', 'BLM', 'Saint Barthélemy', 'Barthélemois'), +(654, 'SH', 'SHN', 'Saint Helena, Ascension and Tristan da Cunha', 'Saint Helenian'), +(659, 'KN', 'KNA', 'Saint Kitts and Nevis', 'Kittitian'), +(660, 'AI', 'AIA', 'Anguilla', 'Anguillan'), +(662, 'LC', 'LCA', 'Saint Lucia', 'Saint Lucian'), +(663, 'MF', 'MAF', 'Saint Martin', 'Saint-Martinoise'), +(666, 'PM', 'SPM', 'Saint Pierre and Miquelon', 'Saint-Pierrais'), +(670, 'VC', 'VCT', 'Saint Vincent and the Grenadines', 'Vincentian'), +(674, 'SM', 'SMR', 'San Marino', 'Sammarinese'), +(678, 'ST', 'STP', 'Sao Tome and Principe', 'São Toméan'), +(682, 'SA', 'SAU', 'Saudi Arabia', 'Saudi'), +(686, 'SN', 'SEN', 'Senegal', 'Senegalese'), +(688, 'RS', 'SRB', 'Serbia', 'Serbian'), +(690, 'SC', 'SYC', 'Seychelles', 'Seychellois'), +(694, 'SL', 'SLE', 'Sierra Leone', 'Sierra Leonean'), +(702, 'SG', 'SGP', 'Singapore', 'Singaporean'), +(703, 'SK', 'SVK', 'Slovakia', 'Slovak'), +(704, 'VN', 'VNM', 'Vietnam', 'Vietnamese'), +(705, 'SI', 'SVN', 'Slovenia', 'Slovenian'), +(706, 'SO', 'SOM', 'Somalia', 'Somali'), +(710, 'ZA', 'ZAF', 'South Africa', 'South African'), +(716, 'ZW', 'ZWE', 'Zimbabwe', 'Zimbabwean'), +(724, 'ES', 'ESP', 'Spain', 'Spanish'), +(728, 'SS', 'SSD', 'South Sudan', 'South Sudanese'), +(729, 'SD', 'SDN', 'Sudan', 'Sudanese'), +(732, 'EH', 'ESH', 'Western Sahara', 'Sahrawi'), +(740, 'SR', 'SUR', 'Suriname', 'Surinamese'), +(744, 'SJ', 'SJM', 'Svalbard and Jan Mayen', 'Svalbardian'), +(748, 'SZ', 'SWZ', 'Eswatini', 'Swazi'), +(752, 'SE', 'SWE', 'Sweden', 'Swedish'), +(756, 'CH', 'CHE', 'Switzerland', 'Swiss'), +(760, 'SY', 'SYR', 'Syria', 'Syrian'), +(762, 'TJ', 'TJK', 'Tajikistan', 'Tajikistani'), +(764, 'TH', 'THA', 'Thailand', 'Thai'), +(768, 'TG', 'TGO', 'Togo', 'Togolese'), +(772, 'TK', 'TKL', 'Tokelau', 'Tokelauan'), +(776, 'TO', 'TON', 'Tonga', 'Tongan'), +(780, 'TT', 'TTO', 'Trinidad and Tobago', 'Trinidadian'), +(784, 'AE', 'ARE', 'United Arab Emirates', 'Emirati'), +(788, 'TN', 'TUN', 'Tunisia', 'Tunisian'), +(792, 'TR', 'TUR', 'Turkey', 'Turkish'), +(795, 'TM', 'TKM', 'Turkmenistan', 'Turkmen'), +(796, 'TC', 'TCA', 'Turks and Caicos Islands', 'Turks and Caicos Islander'), +(798, 'TV', 'TUV', 'Tuvalu', 'Tuvaluan'), +(800, 'UG', 'UGA', 'Uganda', 'Ugandan'), +(804, 'UA', 'UKR', 'Ukraine', 'Ukrainian'), +(807, 'MK', 'MKD', 'North Macedonia', 'Macedonian'), +(818, 'EG', 'EGY', 'Egypt', 'Egyptian'); + + +-- Insert countries_new (chunk 6: 251–end) +INSERT INTO countries_new (num_code, alpha_2_code, alpha_3_code, en_short_name, nationality) VALUES +(826, 'GB', 'GBR', 'United Kingdom', 'British'), +(831, 'GG', 'GGY', 'Guernsey', 'Channel Islander'), +(832, 'JE', 'JEY', 'Jersey', 'Channel Islander'), +(833, 'IM', 'IMN', 'Isle of Man', 'Manx'), +(834, 'TZ', 'TZA', 'Tanzania', 'Tanzanian'), +(840, 'US', 'USA', 'United States of America', 'American'), +(850, 'VI', 'VIR', 'United States Virgin Islands', 'U.S. Virgin Islander'), +(854, 'BF', 'BFA', 'Burkina Faso', 'Burkinabé'), +(858, 'UY', 'URY', 'Uruguay', 'Uruguayan'), +(860, 'UZ', 'UZB', 'Uzbekistan', 'Uzbekistani'), +(862, 'VE', 'VEN', 'Venezuela', 'Venezuelan'), +(876, 'WF', 'WLF', 'Wallis and Futuna', 'Wallisian'), +(882, 'WS', 'WSM', 'Samoa', 'Samoan'), +(887, 'YE', 'YEM', 'Yemen', 'Yemeni'), +(894, 'ZM', 'ZMB', 'Zambia', 'Zambian'); diff --git a/public/assets/js/client-index.js b/public/assets/js/client-index.js new file mode 100644 index 0000000..eb14476 --- /dev/null +++ b/public/assets/js/client-index.js @@ -0,0 +1,416 @@ +// public/js/client-index.js + +$(document).ready(function() { + $('#clientService').select2({ + placeholder: "-- Select Services --", + allowClear: true, + dropdownParent: $('#createClientModal') + }); + let searchTimer; + + // Initialize the table on page load + fetchClients(); + + // Event Listeners for Search and Filters + $('#searchClient').on('keyup', function() { + clearTimeout(searchTimer); + searchTimer = setTimeout(() => fetchClients(1), 400); + }); + + $('#filterService, #filterBilling').on('change', function() { + fetchClients(1); + }); + $('#filterService, #filterStatus').on('change', function() { + fetchClients(1); + }); + + // Handle Pagination Clicks dynamically + $(document).on('click', '.page-link-ajax', function(e) { + e.preventDefault(); + let page = $(this).data('page'); + if (page) fetchClients(page); + }); + + $('#btnOpenClientModal').on('click', function(e) { + e.preventDefault(); + $('#createClientModal').modal('show'); + }); + + $(document).on('click', '.btn-view-client', function(e) { + e.preventDefault(); + let clientId = $(this).data('id'); + window.location.href = base_url + "/clients/" + clientId; + }); + + // --------------------------------------------------------- + // EDIT CLIENT MODAL TRIGGER & POPULATION + // --------------------------------------------------------- + $(document).on('click', '.btn-edit-client', function(e) { + e.preventDefault(); + let clientId = $(this).data('id'); + let $form = $('#editClientForm'); + + // Set dynamic action URL for update + $form.attr('action', base_url + '/clients/' + clientId); + + // Fetch Services first, then Client data to populate the edit modal + $.ajax({ + url: base_url + '/api/services', + type: 'GET', + success: function(services) { + let $servicesSelect = $('#edit_services'); + $servicesSelect.empty(); + if (services && Array.isArray(services)) { + services.forEach(function(s) { + $servicesSelect.append(new Option(s.name, s.id)); + }); + } + + // Fetch client details JSON + $.ajax({ + url: base_url + '/clients/' + clientId + '/json', + type: 'GET', + success: function(client) { + $('#edit_name').val(client.name); + $('#edit_email').val(client.email); + $('#edit_phone').val(client.phone); + $('#edit_contact_person').val(client.contact_person); + $('#edit_company_type').val(client.company_type); + $('#edit_contract_type').val(client.contract_type); + $('#edit_industry').val(client.industry); + $('#edit_status').val(client.status); + $('#edit_currency').val(client.currency); + // $('#edit_country').val(client.country); + loadCountries(client.country); + // Helper for multi-select Select2 fields + function setSelect2Values(selector, values) { + let $el = $(selector); + $el.val(null).trigger('change'); + if (values && Array.isArray(values)) { + values.forEach(function(val) { + if ($el.find("option[value='" + val + "']").length === 0) { + $el.append(new Option(val, val, true, true)); + } + }); + $el.val(values).trigger('change'); + } + } + + // Parse services if stored as JSON string + let clientServices = client.services; + if (typeof clientServices === 'string') { + try { clientServices = JSON.parse(clientServices); } catch(err) {} + } + + setSelect2Values('#edit_services', clientServices); + setSelect2Values('#edit_message_types', client.message_types); + setSelect2Values('#edit_connections', client.connections); + setSelect2Values('#edit_support_phones', client.support_phones); + setSelect2Values('#edit_support_emails', client.support_emails); + setSelect2Values('#edit_rate_emails', client.rate_emails); + setSelect2Values('#edit_support_skype', client.support_skype); + + // Show the modal after populating + $('#editClientModal select[multiple]').each(function() { + if (!$(this).hasClass("select2-hidden-accessible")) { + $(this).select2({ + theme: 'bootstrap-5', + dropdownParent: $('#editClientModal'), + tags: true, // Allows typing custom entries for emails, phones, etc. + tokenSeparators: [',', ' '], + placeholder: 'Select or type and hit enter...' + }); + } + }); + $('#editClientModal').modal('show'); + }, + error: function() { + Swal.fire('Error', 'Could not fetch client details.', 'error'); + } + }); + } + }); + }); + + $('#createClientModal, #editClientModal').on('hidden.bs.modal', function () { + let $form = $(this).find('form'); + if ($form.length) { + $form[0].reset(); + // Reset any select elements or select2 tags back to blank + $form.find('select').val(null).trigger('change'); + } + }); + // --------------------------------------------------------- + // AJAX FORM SUBMISSION (Create Client) + // --------------------------------------------------------- + $('#createClientForm').on('submit', function(e) { + e.preventDefault(); + + let $form = $(this); + let $submitBtn = $('#btnSubmitClient'); + let originalText = $submitBtn.html(); + let $alertBox = $('#clientModalAlert'); + + $submitBtn.html('Saving...').prop('disabled', true); + $alertBox.html(''); + $form.find('.is-invalid').removeClass('is-invalid'); + $form.find('.invalid-feedback').remove(); + + $.ajax({ + url: $form.attr('action'), + method: 'POST', + data: $form.serialize(), + success: function(response) { + if(response.success) { + $alertBox.html(` + + `); + fetchClients(1); + setTimeout(() => { + $('#createClientModal').modal('hide'); + $form[0].reset(); + $alertBox.html(''); + }, 2000); + } + }, + error: function(xhr) { + if (xhr.status === 401) { + window.location.href = base_url + '/login'; + } + if (xhr.status === 422) { + let errors = xhr.responseJSON.errors; + $alertBox.html(` + + `); + $.each(errors, function(key, value) { + let fieldName = key; + if(key === 'services') fieldName = 'services[]'; + let $input = $form.find('[name="' + fieldName + '"]'); + if ($input.length) { + $input.addClass('is-invalid'); + $input.parent().append('
' + value[0] + '
'); + } + }); + } else { + $alertBox.html(` + + `); + } + }, + complete: function() { + $submitBtn.html(originalText).prop('disabled', false); + } + }); + }); + + $('#createClientModal').on('hidden.bs.modal', function () { + $('#createClientForm')[0].reset(); + $('#clientService').val(null).trigger('change'); + $('#clientModalAlert').html(''); + $('#createClientForm').find('.is-invalid').removeClass('is-invalid'); + $('#createClientForm').find('.invalid-feedback').remove(); + }); + + // AJAX Fetch Function + function fetchClients(page = 1) { + const search = $('#searchClient').val(); + const service = $('#filterService').val(); + const billing = $('#filterBilling').val(); + const status = $('#filterStatus').val(); + + $('#clientTableBody').html(` + + +
+
Loading clients...
+ + + `); + + $.ajax({ + url: base_url + "/clients/data", + type: "GET", + data: { page: page, search: search, service: service, billing: billing, status: status }, + success: function(response) { + renderTable(response.data); + renderPagination(response); + }, + error: function() { + $('#clientTableBody').html('Failed to load data. Please try again.'); + } + }); + } + + // Handle Export Actions + $('.btn-export').on('click', function(e) { + e.preventDefault(); + const search = $('#searchClient').val(); + const service = $('#filterService').val(); + const billing = $('#filterBilling').val(); + const status = $('#filterStatus').val(); + const format = $(this).data('format'); + + const queryParams = $.param({ + search: search, + service: service, + billing: billing, + status: status, + format: format + }); + + window.location.href = base_url + "/clients/export?" + queryParams; + }); + + // Render Table Rows + function renderTable(clients) { + let html = ''; + + if (clients.length === 0) { + $('#clientTableBody').html('No clients found matching your criteria.'); + return; + } + + clients.forEach(client => { + let initials = client.name.substring(0, 2).toUpperCase(); + + let servicesHtml = 'N/A'; + if (client.services) { + try { + let servicesArray = typeof client.services === 'string' ? JSON.parse(client.services) : client.services; + if (Array.isArray(servicesArray) && servicesArray.length > 0) { + servicesHtml = servicesArray.map(service => + `${service}` + ).join(''); + } + } catch (e) { + console.error("Could not parse services for client: " + client.name); + } + } + + let statusBadge = client.status === 'Live' || client.status === 'active' + ? 'Active' + : '' + client.status + ''; + + let formattedDate = 'N/A'; + if (client.created_at) { + const dateObj = new Date(client.created_at); + formattedDate = dateObj.toLocaleDateString('en-GB', { + day: 'numeric', + month: 'short', + year: 'numeric' + }); + } + + + // Extract Account Manager name safely + let amName = 'Unassigned'; + if (client.account_manager && client.account_manager.name) { + amName = client.account_manager.name; + } + + html += ` + + +
+
${initials}
+
+
${client.name}
+
${client.country || 'N/A'}
+
+
+ + +
${client.contact_person || 'N/A'}
+ + + ${amName} + + +
${client.email || 'N/A'}
+ + + ${servicesHtml} + + ${client.pay_mode || 'N/A'} + ${formattedDate} + ${statusBadge} + +
+ + +
+ + + `; + }); + + $('#clientTableBody').html(html); + } + + // Render Pagination Links + function renderPagination(response) { + $('#paginationInfo').text(`Showing ${response.from || 0} to ${response.to || 0} of ${response.total} entries`); + + let paginationHtml = ''; + if (response.last_page > 1) { + let prevDisabled = response.current_page === 1 ? 'disabled' : ''; + paginationHtml += `
  • Previous
  • `; + + for (let i = 1; i <= response.last_page; i++) { + let activeClass = response.current_page === i ? 'active' : ''; + let style = response.current_page === i ? 'style="background-color: #5c4df0; border-color: #5c4df0;"' : 'class="page-link text-dark"'; + paginationHtml += `
  • ${i}
  • `; + } + + let nextDisabled = response.current_page === response.last_page ? 'disabled' : ''; + paginationHtml += `
  • Next
  • `; + } + + $('#paginationLinks').html(paginationHtml); + } + + // Helper function to populate country dropdowns dynamically + function loadCountries(selectedCountry = '') { + $.ajax({ + url: base_url + '/api/countries', + type: 'GET', + success: function(countries) { + // Target both create and edit country dropdowns if they exist + let $selectors = $('#create_country, #edit_country'); + + $selectors.each(function() { + let $select = $(this); + let currentVal = $select.val() || selectedCountry; + + $select.empty().append(''); + + if (countries && Array.isArray(countries)) { + countries.forEach(function(c) { + $select.append(new Option(c.en_short_name, c.en_short_name)); + }); + } + + if (currentVal) { + $select.val(currentVal); + } + }); + } + }); + } + + // Call loadCountries on page load so it's ready for the create modal + loadCountries(); +}); \ No newline at end of file diff --git a/public/assets/js/client-show-modal.js b/public/assets/js/client-show-modal.js index 490457f..861a1a7 100644 --- a/public/assets/js/client-show-modal.js +++ b/public/assets/js/client-show-modal.js @@ -343,7 +343,7 @@ document.addEventListener("DOMContentLoaded", function() { $.ajax({ url: $form.attr('action'), - type: 'POST', // Spoofed as PUT via hidden input + type: 'POST', data: $form.serialize(), headers: { 'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content') diff --git a/public/favicon/android-chrome-192x192.png b/public/favicon/android-chrome-192x192.png new file mode 100644 index 0000000000000000000000000000000000000000..55746fbb4b560ae6f2a23c8d50f5e61cd533cefe GIT binary patch literal 9486 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 A)c^nh literal 0 HcmV?d00001 diff --git a/public/favicon/android-chrome-512x512.png b/public/favicon/android-chrome-512x512.png new file mode 100644 index 0000000000000000000000000000000000000000..f6d76d4143f2196f5278095a9980d29a81647744 GIT binary patch literal 32029 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 literal 0 HcmV?d00001 diff --git a/public/favicon/favicon-16x16.png b/public/favicon/favicon-16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..951a9bddcd3247d5db60f4cae010a085c1f4d74a GIT binary patch literal 350 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 literal 0 HcmV?d00001 diff --git a/public/favicon/site.webmanifest b/public/favicon/site.webmanifest new file mode 100644 index 0000000..45dc8a2 --- /dev/null +++ b/public/favicon/site.webmanifest @@ -0,0 +1 @@ +{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"} \ No newline at end of file diff --git a/resources/views/clients/export-pdf.blade.php b/resources/views/clients/export-pdf.blade.php new file mode 100644 index 0000000..8b36de6 --- /dev/null +++ b/resources/views/clients/export-pdf.blade.php @@ -0,0 +1,125 @@ + + + + + Clients Report + + + + + +
    +

    Click ERP - Client Master Report

    +

    Generated on: {{ date('d M Y, h:i A') }} • Total Records: {{ count($clients) }}

    +
    + + + + + + + + + + + + + + + + @forelse($clients as $index => $client) + + + + + + + + + + @empty + + + + @endforelse + +
    #Client NameContact PersonEmailPay ModeCountryStatus
    {{ $index + 1 }}{{ $client->name }}{{ $client->contact_person ?? 'N/A' }}{{ $client->email ?? 'N/A' }}{{ $client->pay_mode ?? 'N/A' }}{{ $client->country ?? 'N/A' }} + @php + $isActive = $client->status === 'Live' || $client->status === 'active'; + @endphp + + {{ $client->status ?? 'N/A' }} + +
    No client records found matching the criteria.
    + + + + + + \ No newline at end of file diff --git a/resources/views/clients/index.blade.php b/resources/views/clients/index.blade.php index 628183c..f93b6ae 100644 --- a/resources/views/clients/index.blade.php +++ b/resources/views/clients/index.blade.php @@ -32,6 +32,13 @@ border-color: #dc3545; } .avatar-circle { width: 36px; height: 36px; display: flex; align-items: center; justify-content: center; border-radius: 50%; font-weight: bold; font-size: 0.85rem; } + /* Ensure all Select2 containers expand to fill their grid column width */ + .select2-container { + width: 100% !important; + } + .select2-selection--multiple { + min-height: 38px !important; + } @endpush @@ -46,12 +53,10 @@

    Client Management

    -

    Manage clients, payment modes, services etc.

    +

    Manage clients, payment modes, contact person, services etc.