diff --git a/app/Http/Controllers/DashboardController.php b/app/Http/Controllers/DashboardController.php
index 036ec97..3cfc5ed 100644
--- a/app/Http/Controllers/DashboardController.php
+++ b/app/Http/Controllers/DashboardController.php
@@ -3,18 +3,143 @@
namespace App\Http\Controllers;
use Illuminate\Http\Request;
+use Illuminate\Http\JsonResponse;
+use Illuminate\View\View;
+use Illuminate\Support\Facades\DB;
+use Carbon\Carbon;
+
+// Explicitly import models for better readability and IDE support
+use App\Models\Client;
+use App\Models\UserActivity;
+use App\Models\StaffMember;
+use App\Models\NationalHoliday;
+use App\Models\DailyQuote;
+use App\Models\NetworkOps;
class DashboardController extends Controller
{
- public function index()
+ public function index(): View
{
- $data = [
- 'page_title' => 'Dashboard'
- ];
- return view('dashboard.index_two', $data);
+ // Grouped OR clauses inside a closure to prevent query bleeding
+ $ussd_clients = Client::where(function ($query) {
+ $query->where('services', 'LIKE', '%ussd%')
+ ->orWhere('services', 'LIKE', '%A2P%');
+ })->count();
+
+ $airtime_clients = Client::where('services', 'LIKE', '%airtime%')->count();
+ $sms_clients = Client::where('services', 'LIKE', '%sms%')->count();
+ $voice_clients = Client::where('services', 'LIKE', '%ivr%')->count();
+
+ // Cleaned up the null/empty checks and removed the dangerous global orWhere
+ $expiring_contracts = Client::where('contract_auto_renew', '!=', 'YES')
+ ->whereNotNull('contract_validity')
+ ->where('contract_validity', '!=', '')
+ ->orderBy('contract_validity', 'ASC')
+ ->take(5)
+ ->get();
+
+ $user_activities = UserActivity::where('user_id', '>', 1)
+ ->with('userInfo')
+ ->orderBy('created_at', 'DESC')
+ ->take(5)
+ ->get();
+
+ $recent_clients = Client::with('auth_user_info')
+ ->orderBy('id', 'DESC')
+ ->take(5)
+ ->get();
+
+ // Cleaner raw query for upcoming birthdays ignoring the current year
+ $upcoming_birthdays = StaffMember::whereRaw("DATE_FORMAT(dob, '%m-%d') >= DATE_FORMAT(CURDATE(), '%m-%d')")
+ ->orderByRaw("DATE_FORMAT(dob, '%m-%d') ASC")
+ ->take(5)
+ ->get();
+
+ $upcoming_hodidays = NationalHoliday::whereDate('event_date', '>=', Carbon::today())
+ ->orderBy('event_date', 'ASC')
+ ->take(15)
+ ->get();
+
+ $main_quote = DailyQuote::where('status', 'active')->first();
+
+ return view('dashboard.index_two', [
+ 'page_title' => 'Dashboard',
+ 'sms' => $sms_clients,
+ 'ussd' => $ussd_clients,
+ 'voice' => $voice_clients,
+ 'airtime' => $airtime_clients,
+ 'total' => Client::count(),
+ 'main_quote' => $main_quote,
+ 'recent_clients' => $recent_clients,
+ 'user_activities' => $user_activities,
+ 'expiring_contracts' => $expiring_contracts,
+ 'upcoming_birthdays' => $upcoming_birthdays,
+ 'upcoming_hodidays' => $upcoming_hodidays
+ ]);
}
-}
+ public function getEvents(): JsonResponse
+ {
+ $event_arr = [
+ [
+ "title" => 'Airtel MW Top Up',
+ "start" => date("Y-m-d") // Fixed format: Most JS calendars expect Y-m-d, not Y, m, d
+ ],
+ [
+ "title" => 'Airtel MW Top Down',
+ "start" => date("Y-m-d")
+ ],
+ ];
+ return response()->json($event_arr);
+ }
-// Nana Ekua Banson
\ No newline at end of file
+ public function getQuotes(): JsonResponse
+ {
+ // Replaced complex raw SQL with Laravel's native randomized sorting
+ $daily_quote = DailyQuote::inRandomOrder()->first();
+
+ return response()->json($daily_quote);
+ }
+
+ public function getMnosContracts(): JsonResponse
+ {
+ $threeMonthsLater = Carbon::now()->addMonths(3)->format('m-d');
+ $currentMonth = Carbon::now()->format('m');
+
+ // Added ->count() directly to the query builder to save RAM
+ $expiring_three_months = NetworkOps::where('contract_auto_renew', 'NO')
+ ->whereRaw("DATE_FORMAT(contract_validity, '%m-%d') = ?", [$threeMonthsLater])
+ ->count();
+
+ $expired_overall = NetworkOps::whereDate('contract_validity', '<', Carbon::now())->count();
+
+ $expiring_current_month = NetworkOps::whereMonth('contract_validity', $currentMonth)->count();
+
+ return response()->json([
+ 'expiring_three_months' => $expiring_three_months,
+ 'expired_overall' => $expired_overall,
+ 'expiring_current_month' => $expiring_current_month
+ ]);
+ }
+
+ public function getClientContracts(): JsonResponse
+ {
+ $threeMonthsLater = Carbon::now()->addMonths(3)->format('m-d');
+ $currentMonth = Carbon::now()->format('m');
+
+ $expiring_three_months = Client::where('contract_auto_renew', 'NO')
+ ->whereRaw("DATE_FORMAT(contract_validity, '%m-%d') = ?", [$threeMonthsLater])
+ ->count();
+
+ $expired_overall = Client::whereDate('contract_validity', '<', Carbon::now())->count();
+
+ $expiring_current_month = Client::whereMonth('contract_validity', $currentMonth)->count();
+
+ return response()->json([
+ 'expiring_three_months' => $expiring_three_months,
+ 'expired_overall' => $expired_overall,
+ 'expiring_current_month' => $expiring_current_month
+ ]);
+ }
+}
\ No newline at end of file
diff --git a/app/Models/BranchFile.php b/app/Models/BranchFile.php
new file mode 100644
index 0000000..5002106
--- /dev/null
+++ b/app/Models/BranchFile.php
@@ -0,0 +1,18 @@
+hasOne('App\Models\OfficeLocation', 'id', 'branch_id');
+ }
+ public function staff_info(){
+ return $this->hasOne('App\Models\StaffMember', 'id', 'uploaded_by');
+ }
+}
diff --git a/app/Models/ClickApps.php b/app/Models/ClickApps.php
new file mode 100755
index 0000000..270baec
--- /dev/null
+++ b/app/Models/ClickApps.php
@@ -0,0 +1,26 @@
+hasMany('App\Models\DirectConnection', 'server_id', 'id');
+ }
+ public function credentials_info(){
+ return $this->hasMany('App\Models\ServerCredential', 'server_id', 'id');
+ }
+ public function modified_by_info(){
+ return $this->hasOne('App\Models\SystemUser', 'id', 'last_modified_by_id');
+ }
+
+
+ public function getRootPasswordAttribute(){
+ $credentials = $this->credentials_info;
+ foreach ($credentials as $value) {
+ if ($value->username == 'root') {
+ return $value->password;
+ }
+ }
+ }
+}
diff --git a/app/Models/Client.php b/app/Models/Client.php
index b3b35f7..3be8606 100644
--- a/app/Models/Client.php
+++ b/app/Models/Client.php
@@ -11,4 +11,44 @@ class Client extends Model
];
public $table = "clients";
// protected $appends = ['client_services'];
+
+ public function country_info(){
+ return $this->hasOne('App\Models\Country', 'alpha_2_code', 'country');
+ }
+ public function country_flag_info(){
+ return $this->hasOne('App\Models\CountryFlag', 'country', 'country');
+ }
+ public function payment_type_info(){
+ return $this->hasOne('App\Models\PaymentType', 'id', 'pay_mode');
+ }
+
+ public function service_info(){
+ return $this->hasMany('App\Models\ClientCategory', 'client_id', 'id');
+ }
+ public function report_info(){
+ return $this->hasMany('App\Models\MeetingReport', 'client', 'id');
+ }
+ public function auth_user_info(){
+ return $this->hasOne('App\Models\SystemUser', 'id', 'auth_user_id');
+ }
+ public function created_by_info(){
+ return $this->hasOne('App\Models\SystemUser', 'id', 'created_by');
+ }
+ public function modified_by_info(){
+ return $this->hasOne('App\Models\SystemUser', 'id', 'last_modified_by');
+ }
+ public function short_code_info(){
+ return $this->hasMany('App\Models\ShortCode', 'client_id', 'id');
+ }
+
+
+ public function getClientServicesAttribute(){
+ $services = $this->service_info;
+ $service_name_arr = [];
+ foreach ($services as $value) {
+ $service_name = Models\Service::find($value['category_id']);
+ $service_name_arr[] = $service_name->name;
+ }
+ return $service_name_arr;
+ }
}
diff --git a/app/Models/ClientCategory.php b/app/Models/ClientCategory.php
new file mode 100755
index 0000000..a3fd8b0
--- /dev/null
+++ b/app/Models/ClientCategory.php
@@ -0,0 +1,16 @@
+hasOne('App\Models\Client', 'id', 'client_id');
+ }
+
+}
diff --git a/app/Models/ClientFile.php b/app/Models/ClientFile.php
new file mode 100644
index 0000000..bda725b
--- /dev/null
+++ b/app/Models/ClientFile.php
@@ -0,0 +1,14 @@
+hasOne('App\Models\Client', 'id', 'client_id');
+ }
+}
diff --git a/app/Models/ClientIndicator.php b/app/Models/ClientIndicator.php
new file mode 100644
index 0000000..69ec75c
--- /dev/null
+++ b/app/Models/ClientIndicator.php
@@ -0,0 +1,10 @@
+logUnguarded();
+ // }
+
+ public function client_info(){
+ return $this->hasOne('App\Models\Client', 'id', 'client_id');
+ }
+ public function created_by_info(){
+ return $this->hasOne('App\Models\SystemUser', 'id', 'auth_user_id');
+ }
+
+}
diff --git a/app/Models/ClientOnboardingMainStage.php b/app/Models/ClientOnboardingMainStage.php
new file mode 100644
index 0000000..674fabe
--- /dev/null
+++ b/app/Models/ClientOnboardingMainStage.php
@@ -0,0 +1,10 @@
+hasOne('App\Models\ClientOnboardingMainStage', 'stage_id', 'id');
+ }
+ //this will produce one stage
+ //I need a relationship to show all sub items
+}
diff --git a/app/Models/ClientOnboardingStage.php b/app/Models/ClientOnboardingStage.php
new file mode 100644
index 0000000..841dc93
--- /dev/null
+++ b/app/Models/ClientOnboardingStage.php
@@ -0,0 +1,11 @@
+hasOne('App\Models\Client', 'id', 'client_id');
+ }
+ public function created_by_info(){
+ return $this->hasOne('App\Models\Account', 'id', 'auth_user_id');
+ }
+}
diff --git a/app/Models/ClientShortCode.php b/app/Models/ClientShortCode.php
new file mode 100644
index 0000000..021f08a
--- /dev/null
+++ b/app/Models/ClientShortCode.php
@@ -0,0 +1,18 @@
+hasOne('App\Models\Client', 'id', 'client_id');
+ }
+ public function update_info(){
+ return $this->hasOne('App\Models\SystemUser', 'id', 'last_updated_by');
+ }
+}
diff --git a/app/Models/ClientSupportFees.php b/app/Models/ClientSupportFees.php
new file mode 100644
index 0000000..0f09c88
--- /dev/null
+++ b/app/Models/ClientSupportFees.php
@@ -0,0 +1,19 @@
+hasOne('App\Models\Client', 'id', 'client_id');
+ }
+ public function created_by_info(){
+ // return $this->hasOne('App\Models\Account', 'id', 'auth_user_id');
+ return $this->hasOne('App\Models\SystemUser', 'id', 'user_id');
+ }
+}
diff --git a/app/Models/Comment.php b/app/Models/Comment.php
deleted file mode 100644
index 66b48b3..0000000
--- a/app/Models/Comment.php
+++ /dev/null
@@ -1,23 +0,0 @@
-hasOne(User::class);
- }
- public function projectStatus(): BelongsTo
- {
- return $this->belongsTo(ProjectStatus::class);
- }
-}
diff --git a/app/Models/Country.php b/app/Models/Country.php
new file mode 100755
index 0000000..a48d2bd
--- /dev/null
+++ b/app/Models/Country.php
@@ -0,0 +1,11 @@
+hasOne('App\Models\SystemUser', 'id', 'user_id');
+ }
+}
diff --git a/app/Models/MarketReport.php b/app/Models/MarketReport.php
new file mode 100755
index 0000000..43890eb
--- /dev/null
+++ b/app/Models/MarketReport.php
@@ -0,0 +1,24 @@
+hasOne('App\Models\Client', 'id', 'client');
+ }
+ public function payment_info(){
+ return $this->hasOne('App\Models\PaymentType', 'id', 'payment_type');
+ }
+ public function auth_user_info(){
+ return $this->hasOne('App\Models\Account', 'id', 'auth_user_id');
+ }
+ public function sam_comment_info(){
+ return $this->hasMany('App\Models\SamComment', 'report_id', 'id');
+ }
+}
diff --git a/app/Models/MeetingReport.php b/app/Models/MeetingReport.php
new file mode 100755
index 0000000..eaa30a0
--- /dev/null
+++ b/app/Models/MeetingReport.php
@@ -0,0 +1,21 @@
+hasOne('App\Models\Client', 'id', 'client');
+ }
+ public function auth_user_info(){
+ return $this->hasOne('App\Models\Account', 'id', 'auth_user_id');
+ }
+ public function sam_comment_info(){
+ return $this->hasMany('App\Models\SamComment', 'report_id', 'id');
+ }
+}
diff --git a/app/Models/MnoFile.php b/app/Models/MnoFile.php
new file mode 100644
index 0000000..7f4ed8c
--- /dev/null
+++ b/app/Models/MnoFile.php
@@ -0,0 +1,15 @@
+hasOne('App\Models\NetworkOps', 'id', 'mno_id');
+ }
+}
diff --git a/app/Models/Mnoips.php b/app/Models/Mnoips.php
new file mode 100644
index 0000000..3b37d02
--- /dev/null
+++ b/app/Models/Mnoips.php
@@ -0,0 +1,14 @@
+hasOne('App\Models\SystemUser', 'id', 'last_modified_by');
+ }
+}
diff --git a/app/Models/Mnonote.php b/app/Models/Mnonote.php
new file mode 100644
index 0000000..da06dba
--- /dev/null
+++ b/app/Models/Mnonote.php
@@ -0,0 +1,19 @@
+hasOne('App\Models\NetworkOps', 'id', 'mno_id');
+ }
+
+ public function created_by_info(){
+ return $this->hasOne('App\Models\SystemUser', 'id', 'user_id');
+ }
+}
diff --git a/app/Models/Mnopayment.php b/app/Models/Mnopayment.php
new file mode 100644
index 0000000..7e0f230
--- /dev/null
+++ b/app/Models/Mnopayment.php
@@ -0,0 +1,19 @@
+hasOne('App\Models\NetworkOps', 'id', 'mno_id');
+ }
+
+ public function created_by_info(){
+ return $this->hasOne('App\Models\SystemUser', 'id', 'user_id');
+ }
+}
diff --git a/app/Models/NationalHoliday.php b/app/Models/NationalHoliday.php
new file mode 100644
index 0000000..bc3a816
--- /dev/null
+++ b/app/Models/NationalHoliday.php
@@ -0,0 +1,17 @@
+hasOne('App\Models\OfficeLocation', 'id', 'branch_id');
+ // }
+}
+
+ ?>
\ No newline at end of file
diff --git a/app/Models/NetworkOps.php b/app/Models/NetworkOps.php
new file mode 100755
index 0000000..b162af5
--- /dev/null
+++ b/app/Models/NetworkOps.php
@@ -0,0 +1,15 @@
+hasOne('App\Models\StaffMember', 'id', 'account_manager_id');
+ }
+}
diff --git a/app/Models/Networkstatus.php b/app/Models/Networkstatus.php
new file mode 100755
index 0000000..1e101a6
--- /dev/null
+++ b/app/Models/Networkstatus.php
@@ -0,0 +1,11 @@
+hasOne('App\Models\StaffMember', 'id', 'user_id');
+ }
+ public function country_manager_info(){
+ // return $this->hasOne('App\Models\StaffMember', 'id', 'country_manager_id');
+ return $this->hasOne('App\Models\StaffMember', 'id', 'country_manager_id');
+ }
+}
diff --git a/app/Models/OnboardingStage.php b/app/Models/OnboardingStage.php
new file mode 100644
index 0000000..5690457
--- /dev/null
+++ b/app/Models/OnboardingStage.php
@@ -0,0 +1,10 @@
+hasMany('App\Models\User', 'org_id', 'id');
- // }
- // public function phone(): HasOne
- // {
- // return $this->hasOne(Phone::class);
- // }
- public function userInfo(): HasMany
- {
- return $this->hasMany(User::class);
- }
- public function statusInfo(): HasMany
- {
- return $this->hasMany(ProjectStatus::class);
- }
-}
diff --git a/app/Models/ProjectStatus.php b/app/Models/ProjectStatus.php
deleted file mode 100644
index eb7a9aa..0000000
--- a/app/Models/ProjectStatus.php
+++ /dev/null
@@ -1,23 +0,0 @@
-belongsTo(Project::class);
- }
- public function comments(): HasMany
- {
- return $this->hasMany(Comment::class);
- }
-}
\ No newline at end of file
diff --git a/app/Models/SamComment.php b/app/Models/SamComment.php
new file mode 100755
index 0000000..952c334
--- /dev/null
+++ b/app/Models/SamComment.php
@@ -0,0 +1,13 @@
+logUnguarded()->useLogName('senderid_log')->logOnlyDirty()->dontSubmitEmptyLogs();
+ // }
+
+
+ public function modified_by_info(){
+ return $this->hasOne('App\Models\StaffMember', 'id', 'last_modified_by');
+ }
+ public function network_info(){
+ return $this->hasOne('App\Models\NetworkOps', 'id', 'network_id');
+ }
+ public function created_by_info(){
+ return $this->hasOne('App\Models\StaffMember', 'id', 'created_by');
+ }
+}
diff --git a/app/Models/Senderidold.php b/app/Models/Senderidold.php
new file mode 100644
index 0000000..02e78a5
--- /dev/null
+++ b/app/Models/Senderidold.php
@@ -0,0 +1,27 @@
+hasOne('App\Models\StaffMember', 'id', 'last_modified_by');
+ }
+ public function network_info(){
+ return $this->hasOne('App\Models\NetworkOps', 'id', 'network_id');
+ }
+ public function client_info(){
+ return $this->hasOne('App\Models\Client', 'id', 'client_id');
+ }
+ public function created_by_info(){
+ return $this->hasOne('App\Models\StaffMember', 'id', 'created_by');
+ }
+}
diff --git a/app/Models/ServerCredential.php b/app/Models/ServerCredential.php
new file mode 100755
index 0000000..62c3e1a
--- /dev/null
+++ b/app/Models/ServerCredential.php
@@ -0,0 +1,16 @@
+hasOne('App\Models\ClickServer', 'id', 'server_id');
+ }
+}
diff --git a/app/Models/Service.php b/app/Models/Service.php
new file mode 100755
index 0000000..9122a13
--- /dev/null
+++ b/app/Models/Service.php
@@ -0,0 +1,11 @@
+hasOne('App\Models\Client', 'id', 'client_id');
+ }
+ public function update_info(){
+ return $this->hasOne('App\Models\SystemUser', 'id', 'last_updated_by');
+ }
+ public function account_mgr_info(){
+ return $this->hasOne('App\Models\SystemUser', 'id', 'account_manager_id');
+ }
+}
diff --git a/app/Models/SupportTicket.php b/app/Models/SupportTicket.php
new file mode 100644
index 0000000..cf26544
--- /dev/null
+++ b/app/Models/SupportTicket.php
@@ -0,0 +1,25 @@
+hasOne('App\Models\StaffMember', 'id', 'last_modified_by');
+ }
+
+ public function created_by_info(){
+ return $this->hasOne('App\Models\StaffMember', 'id', 'created_by');
+ }
+ public function assignedTo(){
+ return $this->hasOne('App\Models\StaffMember', 'id', 'assigned_to');
+ }
+}
diff --git a/app/Models/SupportTicketsDocument.php b/app/Models/SupportTicketsDocument.php
new file mode 100644
index 0000000..fb9068a
--- /dev/null
+++ b/app/Models/SupportTicketsDocument.php
@@ -0,0 +1,14 @@
+hasOne('App\Models\SystemUser', 'id', 'last_modified_by_id');
+ }
+ public function network_info(){
+ return $this->hasOne('App\Models\NetworkOps', 'id', 'network_id');
+ }
+ public function client_info(){
+ return $this->hasOne('App\Models\Client', 'id', 'client_id');
+ }
+ public function created_by_info(){
+ return $this->hasOne('App\Models\SystemUser', 'id', 'created_by_id');
+ }
+}
diff --git a/app/Models/SystemUser.php b/app/Models/SystemUser.php
new file mode 100755
index 0000000..c189d87
--- /dev/null
+++ b/app/Models/SystemUser.php
@@ -0,0 +1,18 @@
+hasOne('App\Models\Designation', 'id', 'designation');
+ }
+}
diff --git a/app/Models/UserActivity.php b/app/Models/UserActivity.php
new file mode 100644
index 0000000..6a3c2af
--- /dev/null
+++ b/app/Models/UserActivity.php
@@ -0,0 +1,27 @@
+hasOne('App\Models\StaffMember', 'id', 'user_id');
+ }
+ public function userInfoSystem(){
+ return $this->hasOne('App\Models\SystemUser', 'id', 'user_id');
+ }
+ public function getActivityTimeAttribute(){
+ $created = $this->created_at;
+ $parsed_created_date = Carbon::parse($created);
+ $current_date = Carbon::parse(date('Y-m-d'));
+ $days = $parsed_created_date->diffForHumans();
+ return $days;
+ }
+}
diff --git a/app/Models/UssdClientPayment.php b/app/Models/UssdClientPayment.php
new file mode 100755
index 0000000..06e554d
--- /dev/null
+++ b/app/Models/UssdClientPayment.php
@@ -0,0 +1,17 @@
+hasOne('App\Models\Client', 'id', 'client_id');
+ }
+
+}
diff --git a/app/Models/Utility.php b/app/Models/Utility.php
new file mode 100644
index 0000000..eb40c61
--- /dev/null
+++ b/app/Models/Utility.php
@@ -0,0 +1,23 @@
+logUnguarded();
+ // ->logOnly(['name', 'type']);
+ // Chain fluent methods for configuration options
+ }
+}
diff --git a/app/Models/VpnClients.php b/app/Models/VpnClients.php
new file mode 100644
index 0000000..f00de83
--- /dev/null
+++ b/app/Models/VpnClients.php
@@ -0,0 +1,109 @@
+ 'boolean',
+ 'compress' => 'boolean',
+ 'active' => 'boolean',
+ ];
+
+ // Relationships
+ public function server()
+ {
+ return $this->belongsTo(Server::class);
+ }
+
+ // Scopes
+ public function scopeActive($q)
+ {
+ return $q->where('active', 1);
+ }
+
+ public function scopeOfType($q, $type)
+ {
+ return $q->where('type', $type);
+ }
+
+ // Generate a basic OpenVPN config (example). Adapt to your templates and security needs.
+ public function generateConfig()
+ {
+ // If a pre-generated config is stored, return it
+ if ($this->config_path && Storage::exists($this->config_path)) {
+ return Storage::get($this->config_path);
+ }
+
+ // Example template — replace with your real generation logic
+ $server = $this->server ? $this->server->address : 'vpn.example.com';
+ $proto = 'udp';
+ $port = 1194;
+
+ $conf = [];
+ $conf[] = "client";
+ $conf[] = "dev tun";
+ $conf[] = "proto {$proto}";
+ $conf[] = "remote {$server} {$port}";
+ $conf[] = "resolv-retry infinite";
+ $conf[] = "nobind";
+ if ($this->compress) {
+ $conf[] = "comp-lzo";
+ }
+ if ($this->push_dns) {
+ $conf[] = "dhcp-option DNS 10.8.0.1";
+ }
+ if ($this->allowed_ips) {
+ // allowed_ips are usually server-side; include as comment for operator
+ $conf[] = "# Allowed IPs: " . $this->allowed_ips;
+ }
+
+ // insert certs/keys placeholders — do NOT store private keys in cleartext unless secure
+ if ($this->auth_type === 'cert') {
+ $conf[] = "