diff --git a/app/Http/Controllers/SenderidsController.php b/app/Http/Controllers/SenderidsController.php index 1b1e738..9d82557 100644 --- a/app/Http/Controllers/SenderidsController.php +++ b/app/Http/Controllers/SenderidsController.php @@ -3,14 +3,141 @@ namespace App\Http\Controllers; use Illuminate\Http\Request; - +use App\Models; class SenderidsController extends Controller { - public function index() + + + + public function index(Request $request) { + // Fetch KPI Metrics (Keep your existing counts) + // $totalCount = \App\Models\SenderId::count(); + // $approvedCount = \App\Models\SenderId::where('status', 'APPROVED')->count(); + // $pendingCount = \App\Models\SenderId::where('status', 'PENDING')->count(); + // $rejectedCount = \App\Models\SenderId::where('status', 'REJECTED')->count(); + + $totalCount = \App\Models\SenderId::count(); + $approvedCount = \App\Models\SenderId::where('status', 'LIKE', 'Approved%')->count(); + $pendingCount = \App\Models\SenderId::where('status', 'LIKE', 'Applied%')->count(); + $rejectedCount = \App\Models\SenderId::where('status', 'Rejected')->count(); + // Fetch dynamic lists for the modal dropdowns + $clients = Models\Client::orderBy('name', 'asc')->get(); + $networkOperators = Models\NetworkOperator::orderBy('name', 'asc')->get(); + + // Start the query + $query = \App\Models\SenderId::query(); + + // 1. Search Filter (Sender ID, MNO, or Supplier) + if ($request->filled('search')) { + $search = $request->search; + $query->where(function($q) use ($search) { + $q->where('senderid', 'like', "%{$search}%") + ->orWhere('mno_name', 'like', "%{$search}%") + ->orWhere('supplier_name', 'like', "%{$search}%"); + }); + } + + // 2. Direct MNO Filter + if ($request->filled('direct_mno')) { + $query->where('direct_mno', $request->direct_mno); + } + + // 3. Status Filter + if ($request->filled('status')) { + $query->where('status', 'LIKE', '%' . $request->status . '%'); + } + + // Execute query with pagination + $senderIds = $query->orderBy('created_at', 'desc')->paginate(10); + + // Crucial: Append the current request query strings to pagination links + $senderIds->appends($request->query()); + $data = [ - 'page_title' => 'Dashboard' + 'page_title' => 'Sender IDs', + 'totalCount' => $totalCount, + 'approvedCount' => $approvedCount, + 'pendingCount' => $pendingCount, + 'rejectedCount' => $rejectedCount, + 'senderIds' => $senderIds, + 'clients' => $clients, // <-- Pass Clients + 'networkOperators' => $networkOperators, // <-- Pass MNOs ]; + return view('senderids.index', $data); } + + public function store(Request $request) + { + $validated = $request->validate([ + 'senderid' => 'required|string|max:20', + 'direct_mno' => 'required|in:YES,NO', + + // Conditional Validation Rules + 'mno_name' => 'required_if:direct_mno,YES|nullable|string|max:192', + 'supplier_name' => 'required_if:direct_mno,NO|nullable|string|max:192', + + 'bind_name' => 'nullable|string|max:50', + 'type' => 'nullable|string|max:25', + 'status' => 'nullable|string|max:154', + 'remarks' => 'nullable|string|max:250', + ], [ + 'mno_name.required_if' => 'The MNO Name is required when routing directly to an MNO.', + 'supplier_name.required_if' => 'The Supplier Name is required when not routing directly to an MNO.', + ]); + + // Attach creator ID and save + $validated['created_by'] = auth()->id(); + + // Create the Sender ID + $senderId = \App\Models\SenderId::create($validated); + + return response()->json(['success' => true, 'message' => 'Sender ID added successfully!']); + } + public function update(Request $request, $id) + { + $senderId = Models\SenderId::findOrFail($id); + + $validated = $request->validate([ + 'senderid' => 'required|string|max:20', + 'direct_mno' => 'required|in:YES,NO', + + // Conditional validation rules for update as well + 'mno_name' => 'required_if:direct_mno,YES|nullable|string|max:192', + 'supplier_name' => 'required_if:direct_mno,NO|nullable|string|max:192', + + 'bind_name' => 'nullable|string|max:50', + 'type' => 'nullable|string|max:25', + 'status' => 'nullable|string|max:154', + 'remarks' => 'nullable|string|max:250', + ], [ + 'mno_name.required_if' => 'The MNO Name is required when routing directly to an MNO.', + 'supplier_name.required_if' => 'The Supplier Name is required when not routing directly to an MNO.', + ]); + + // Clear out the opposite field depending on the Direct MNO choice to keep data clean + if ($validated['direct_mno'] === 'YES') { + $validated['supplier_name'] = null; + } else { + $validated['mno_name'] = null; + } + + $validated['last_modified_by'] = auth()->id(); + + $senderId->update($validated); + + return response()->json([ + 'success' => true, + 'message' => 'Sender ID updated successfully!' + ]); + } + + public function destroy($id) + { + $senderId = SenderId::findOrFail($id); + $senderId->delete(); + + return redirect()->route('sender-ids.index')->with('success', 'Sender ID deleted successfully.'); + } } diff --git a/app/Http/Controllers/ShortCodesController.php b/app/Http/Controllers/ShortCodesController.php index 84b8033..922f139 100644 --- a/app/Http/Controllers/ShortCodesController.php +++ b/app/Http/Controllers/ShortCodesController.php @@ -2,15 +2,119 @@ namespace App\Http\Controllers; +use App\Models\ShortCode; +use App\Models\Client; +use App\Models\NetworkOperator; +use App\Models\Country; use Illuminate\Http\Request; class ShortCodesController extends Controller { - public function index() + public function index(Request $request) { + // KPI Metrics + $totalCount = ShortCode::count(); + $smsCount = ShortCode::where('code_type', 'sms')->count(); + $ussdCount = ShortCode::where('code_type', 'ussd')->count(); + $voiceCount = ShortCode::where('code_type', 'voice')->count(); + $countries = Country::orderBy('en_short_name', 'asc')->get(); + // dd($countries); + + // Dropdown options + $clients = Client::orderBy('name', 'asc')->get(); + $networkOperators = NetworkOperator::orderBy('name', 'asc')->get(); + + $query = ShortCode::query(); + + // Search Filter + if ($request->filled('search')) { + $search = $request->search; + $query->where(function($q) use ($search) { + $q->where('shortcode', 'like', "%{$search}%") + ->orWhere('name', 'like', "%{$search}%") + ->orWhere('toll_free', 'like', "%{$search}%") + ->orWhere('country', 'like', "%{$search}%") + ->orWhere('network', 'like', "%{$search}%") + ->orWhere('code_type', 'like', "%{$search}%"); + }); + } + + // Status Filter + if ($request->filled('status')) { + $query->where('status', $request->status); + } + + // Execute query with pagination (Fixed variable name from $senderIds to $shortcodes) + $shortcodes = $query->orderBy('created_at', 'desc')->paginate(10); + $shortcodes->appends($request->query()); + $data = [ - 'page_title' => 'Dashboard' + 'page_title' => 'Short Codes Management', + 'totalCount' => $totalCount, + 'smsCount' => $smsCount, + 'ussdCount' => $ussdCount, + 'voiceCount' => $voiceCount, + 'shortcodes' => $shortcodes, + 'clients' => $clients, + 'countries' => $countries, + 'networkOperators' => $networkOperators, ]; + return view('shortcodes.index', $data); } -} + + public function store(Request $request) + { + $validated = $request->validate([ + 'client_id' => 'nullable|integer', + 'name' => 'nullable|string|max:192', + 'shortcode' => 'required|string|max:20', + 'code_type' => 'nullable|string|max:15', + 'toll_free' => 'nullable|string|max:10', + 'monthly_fee' => 'nullable|numeric', + 'country' => 'nullable|string|max:45', + 'network' => 'nullable|string|max:45', + 'status' => 'nullable|string|max:15', + 'launch_date' => 'nullable|date', + 'expiry_date' => 'nullable|date', + 'remarks' => 'nullable|string|max:250', + ]); + + ShortCode::create($validated); + + return response()->json(['success' => true, 'message' => 'Short code created successfully!']); + } + + public function update(Request $request, $id) + { + $shortcode = ShortCode::findOrFail($id); + + $validated = $request->validate([ + 'client_id' => 'nullable|integer', + 'name' => 'nullable|string|max:192', + 'shortcode' => 'required|string|max:20', + 'code_type' => 'nullable|string|max:15', + 'toll_free' => 'nullable|string|max:10', + 'monthly_fee' => 'nullable|numeric', + 'country' => 'nullable|string|max:45', + 'network' => 'nullable|string|max:45', + 'status' => 'nullable|string|max:15', + 'launch_date' => 'nullable|date', + 'expiry_date' => 'nullable|date', + 'remarks' => 'nullable|string|max:250', + ]); + + $validated['last_updated_by'] = auth()->id(); + $shortcode->update($validated); + + return response()->json(['success' => true, 'message' => 'Short code updated successfully!']); + } + + public function destroy($id) + { + $shortcode = ShortCode::findOrFail($id); + $shortcode->delete(); + + return redirect()->route('shortcodes.index')->with('success', 'Short code deleted successfully.'); + } +} \ No newline at end of file diff --git a/app/Models/Country.php b/app/Models/Country.php index a48d2bd..3963b28 100755 --- a/app/Models/Country.php +++ b/app/Models/Country.php @@ -7,5 +7,5 @@ use Illuminate\Database\Eloquent\Model; class Country extends Model { protected $guarded = array('id'); - public $table = "countries"; + public $table = "countries_new"; } diff --git a/app/Models/ShortCode.php b/app/Models/ShortCode.php index 6394757..9509fdf 100644 --- a/app/Models/ShortCode.php +++ b/app/Models/ShortCode.php @@ -7,15 +7,15 @@ use Illuminate\Database\Eloquent\Model; class ShortCode extends Model { protected $guarded = array('id'); - public $table = "short_codes"; + public $table = "client_short_codes"; public function client_info(){ return $this->hasOne('App\Models\Client', 'id', 'client_id'); } public function update_info(){ - return $this->hasOne('App\Models\SystemUser', 'id', 'last_updated_by'); + return $this->hasOne('App\Models\StaffMember', 'id', 'last_updated_by'); } public function account_mgr_info(){ - return $this->hasOne('App\Models\SystemUser', 'id', 'account_manager_id'); + return $this->hasOne('App\Models\StaffMember', 'id', 'account_manager_id'); } } diff --git a/public/assets/js/sender-ids.js b/public/assets/js/sender-ids.js new file mode 100644 index 0000000..cd1894d --- /dev/null +++ b/public/assets/js/sender-ids.js @@ -0,0 +1,197 @@ +$(document).ready(function() { + + const senderModal = new bootstrap.Modal(document.getElementById('senderModal')); + const $form = $('#senderForm'); + + $('#edit_mno_name').select2({ + placeholder: "Select Network Operator", + allowClear: true, + width: '100%', + dropdownParent: $('#senderModal') // Crucial for Bootstrap Modals + }); + $('#edit_supplier_name').select2({ + placeholder: "Select Supplier", + allowClear: true, + width: '100%', + dropdownParent: $('#senderModal') // Crucial for Bootstrap Modals + }); + + $('#edit_senderid').on('input', function() { + // $(this).val($(this).val().toUpperCase().replace(/\s/g, '')); + //$(this).val($(this).val().replace(/\s/g, '')); + const max = 11; + const val = $(this).val(); + + if (val.length > max) { + $(this).val(val.slice(0, max)); + } + }); + + + // Function to handle the form layout based on Direct MNO selection + function toggleSenderIdRoutingFields(selection, isEdit = false) { + let prefix = isEdit ? 'edit_' : ''; + + let $mnoWrapper = $('#wrapper_mno_name' + (isEdit ? '_edit' : '')); + let $supplierWrapper = $('#wrapper_supplier_name' + (isEdit ? '_edit' : '')); + let $mnoInput = $('#input_mno_name' + (isEdit ? '_edit' : '')); + let $supplierInput = $('#input_supplier_name' + (isEdit ? '_edit' : '')); + + if (selection === 'YES') { + // Show MNO, Hide Supplier + $mnoWrapper.slideDown(); + $mnoInput.prop('required', true); + + $supplierWrapper.slideUp(); + $supplierInput.prop('required', false).val(''); // Clear hidden field + } else { + // Show Supplier, Hide MNO + $supplierWrapper.slideDown(); + $supplierInput.prop('required', true); + + $mnoWrapper.slideUp(); + $mnoInput.prop('required', false).val(''); // Clear hidden field + } + } + + // Trigger on change for Create Form + $('#create_direct_mno').on('change', function() { + toggleSenderIdRoutingFields($(this).val(), false); + }); + + // If you have an Edit form, trigger it there as well + // $('#edit_direct_mno').on('change', function() { + // toggleSenderIdRoutingFields($(this).val(), true); + // }); + + // Run on initial load to ensure correct state + toggleSenderIdRoutingFields($('#create_direct_mno').val(), false); + + // Bind change event to the select dropdown + $('#edit_direct_mno').on('change', function() { + toggleSenderIdRoutingFields($(this).val()); + }); + + // --- OPEN MODAL FOR CREATE --- + $('#btnOpenSenderModal').on('click', function() { + $form[0].reset(); + $('#senderRecordId').val(''); + + $('#senderModalTitle').text('Add Sender ID'); + $('#btnSubmitSender').html('Save Sender ID'); + + // Reset toggle state to YES + $('#edit_direct_mno').val('YES').trigger('change'); + + senderModal.show(); + }); + + // --- OPEN MODAL FOR EDIT --- + $('.btn-edit-sender').on('click', function() { + const data = $(this).data(); + + $form[0].reset(); + $('#senderRecordId').val(data.id); + + $('#edit_senderid').val(data.senderid); + $('#edit_direct_mno').val(data.direct_mno); + + // Set dependent fields before toggling + $('#edit_mno_name').val(data.mno_name); + $('#edit_supplier_name').val(data.supplier_name); + + $('#edit_type').val(data.type); + $('#edit_bind_name').val(data.bind_name); + $('#edit_status').val(data.status); + $('#edit_remarks').val(data.remarks); + + $('#senderModalTitle').text('Edit Sender ID: ' + data.senderid); + $('#btnSubmitSender').html('Update Sender ID'); + + // Trigger the toggle layout based on the loaded data + toggleSenderIdRoutingFields(data.direct_mno); + + senderModal.show(); + }); + + // --- HANDLE FORM SUBMISSION (Actual AJAX) --- + $form.on('submit', function(e) { + e.preventDefault(); + + const $btn = $('#btnSubmitSender'); + const originalText = $btn.html(); + const $alertBox = $('#senderIdModalAlert'); + + // Show loading state and clear old errors + $btn.html(' Processing...'); + $btn.prop('disabled', true); + $alertBox.html(''); + $form.find('.is-invalid').removeClass('is-invalid'); + $form.find('.invalid-feedback').remove(); + + // Determine if we are creating or updating based on the hidden ID field + const recordId = $('#senderRecordId').val(); + const submitUrl = base_url + "/" + recordId ? "senderids/" + recordId : "senderids"; + + // Add a hidden _method field for PUT requests if editing + let formData = $form.serialize(); + if (recordId) { + formData += '&_method=PUT'; + } + + $.ajax({ + url: submitUrl, + type: 'POST', // We use POST but pass _method=PUT in the data payload for Laravel + data: formData, + success: function(response) { + if (response.success) { + senderModal.hide(); + Swal.fire({ + icon: 'success', + title: 'Success!', + text: response.message || 'Sender ID record saved.', + confirmButtonColor: '#5c4df0' + }).then(() => { + // Reload the page to refresh the table and KPI counts + location.reload(); + }); + } + }, + error: function(xhr) { + // Handle Laravel Validation Errors + if (xhr.status === 422) { + let errors = xhr.responseJSON.errors; + + $alertBox.html(` +
Provision and manage dedicated/shared codes for SMS, USSD, and Voice routing.
+| Short Code | +Category | +Client / Assignment | +Billing Type | +Network Provisioning | +Actions | +
|---|---|---|---|---|---|
|
+
+
+
+
+
+ 1334
+ Dedicated
+ |
+ SMS | +
+ Click Tech Corp
+ https://api.clicktech.com/...
+ |
+ Standard | +
+
+ MTN
+ Telecel
+ AT
+
+ |
+ + + | +
|
+
+
+
+
+
+ *711*50#
+ Extension (Shared)
+ |
+ USSD | +
+ Alpha Telecom
+ https://ussd.alphatel.com/...
+ |
+ Reverse Billed | +
+
+ MTN
+ Telecel
+
+ |
+ + + | +
|
+
+
+
+
+
+ 3001
+ Dedicated IVR
+ |
+ Voice | +
+ Internal Customer Service
+ SIP: trunk.nexus.local
+ |
+ Standard | +
+
+ MTN
+
+ |
+ + + | +
Provision and manage dedicated/shared codes for SMS, USSD, and Voice routing.
+Manage SMS, USSD, and Voice short codes, toll-free lines, and validity periods.
| Short Code | -Category | -Client / Assignment | -Billing Type | -Network Provisioning | +Name / Description | +Type & Toll-Free | +Pricing / Fees | +Region / Network | +Status & Dates | Actions |
|---|---|---|---|---|---|---|---|---|---|---|
|
-
-
-
-
-
- 1334
- Dedicated
- |
- SMS | -
- Click Tech Corp
- https://api.clicktech.com/...
- |
- Standard | -
-
- MTN
- Telecel
- AT
-
- |
-
- |
- |||||
|
-
-
-
-
-
- *711*50#
- Extension (Shared)
- |
- USSD | -
- Alpha Telecom
- https://ussd.alphatel.com/...
- |
- Reverse Billed | -
-
- MTN
- Telecel
-
- |
-
- |
- |||||
|
-
-
-
-
-
- 3001
- Dedicated IVR
- |
- Voice | -
- Internal Customer Service
- SIP: trunk.nexus.local
- |
- Standard | -
-
- MTN
-
- |
-
- |
- |||||
|
+ {{ $code->shortcode }}
+ |
+
+ {{ $code->name ?? 'N/A' }}
+ Client ID: {{ $code->client_id ?? 'None' }}
+ |
+ + {{ $code->code_type ?? 'N/A' }} + @if($code->toll_free == 'YES') + Toll-Free + @endif + | +
+ ${{ number_format($code->monthly_fee, 2) }}
+ Monthly Fee
+ |
+
+ {{ $code->country ?? 'N/A' }}
+ {{ $code->network ?? 'All Networks' }}
+ |
+
+
+ {{ $code->status ?? 'Active' }}
+
+ Exp: {{ $code->expiry_date ? date('M d, Y', strtotime($code->expiry_date)) : 'N/A' }}
+ |
+
+ |
+ ||||
| No short codes found matching your criteria. | +||||||||||