Initial commit

This commit is contained in:
Kwesi Banson Jnr
2026-09-09 09:50:06 +00:00
commit c7f93a7dc1
8369 changed files with 517137 additions and 0 deletions

View File

@@ -0,0 +1,73 @@
@extends('layouts.app')
@section('content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">{{ __('Login') }}</div>
<div class="card-body">
<form method="POST" action="{{ route('login') }}">
@csrf
<div class="row mb-3">
<label for="email" class="col-md-4 col-form-label text-md-end">{{ __('Email Address') }}</label>
<div class="col-md-6">
<input id="email" type="email" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ old('email') }}" required autocomplete="email" autofocus>
@error('email')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>
<div class="row mb-3">
<label for="password" class="col-md-4 col-form-label text-md-end">{{ __('Password') }}</label>
<div class="col-md-6">
<input id="password" type="password" class="form-control @error('password') is-invalid @enderror" name="password" required autocomplete="current-password">
@error('password')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>
<div class="row mb-3">
<div class="col-md-6 offset-md-4">
<div class="form-check">
<input class="form-check-input" type="checkbox" name="remember" id="remember" {{ old('remember') ? 'checked' : '' }}>
<label class="form-check-label" for="remember">
{{ __('Remember Me') }}
</label>
</div>
</div>
</div>
<div class="row mb-0">
<div class="col-md-8 offset-md-4">
<button type="submit" class="btn btn-primary">
{{ __('Login') }}
</button>
@if (Route::has('password.request'))
<a class="btn btn-link" href="{{ route('password.request') }}">
{{ __('Forgot Your Password?') }}
</a>
@endif
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection

View File

@@ -0,0 +1,49 @@
@extends('layouts.app')
@section('content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">{{ __('Confirm Password') }}</div>
<div class="card-body">
{{ __('Please confirm your password before continuing.') }}
<form method="POST" action="{{ route('password.confirm') }}">
@csrf
<div class="row mb-3">
<label for="password" class="col-md-4 col-form-label text-md-end">{{ __('Password') }}</label>
<div class="col-md-6">
<input id="password" type="password" class="form-control @error('password') is-invalid @enderror" name="password" required autocomplete="current-password">
@error('password')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>
<div class="row mb-0">
<div class="col-md-8 offset-md-4">
<button type="submit" class="btn btn-primary">
{{ __('Confirm Password') }}
</button>
@if (Route::has('password.request'))
<a class="btn btn-link" href="{{ route('password.request') }}">
{{ __('Forgot Your Password?') }}
</a>
@endif
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection

View File

@@ -0,0 +1,47 @@
@extends('layouts.app')
@section('content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">{{ __('Reset Password') }}</div>
<div class="card-body">
@if (session('status'))
<div class="alert alert-success" role="alert">
{{ session('status') }}
</div>
@endif
<form method="POST" action="{{ route('password.email') }}">
@csrf
<div class="row mb-3">
<label for="email" class="col-md-4 col-form-label text-md-end">{{ __('Email Address') }}</label>
<div class="col-md-6">
<input id="email" type="email" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ old('email') }}" required autocomplete="email" autofocus>
@error('email')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>
<div class="row mb-0">
<div class="col-md-6 offset-md-4">
<button type="submit" class="btn btn-primary">
{{ __('Send Password Reset Link') }}
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection

View File

@@ -0,0 +1,65 @@
@extends('layouts.app')
@section('content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">{{ __('Reset Password') }}</div>
<div class="card-body">
<form method="POST" action="{{ route('password.update') }}">
@csrf
<input type="hidden" name="token" value="{{ $token }}">
<div class="row mb-3">
<label for="email" class="col-md-4 col-form-label text-md-end">{{ __('Email Address') }}</label>
<div class="col-md-6">
<input id="email" type="email" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ $email ?? old('email') }}" required autocomplete="email" autofocus>
@error('email')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>
<div class="row mb-3">
<label for="password" class="col-md-4 col-form-label text-md-end">{{ __('Password') }}</label>
<div class="col-md-6">
<input id="password" type="password" class="form-control @error('password') is-invalid @enderror" name="password" required autocomplete="new-password">
@error('password')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>
<div class="row mb-3">
<label for="password-confirm" class="col-md-4 col-form-label text-md-end">{{ __('Confirm Password') }}</label>
<div class="col-md-6">
<input id="password-confirm" type="password" class="form-control" name="password_confirmation" required autocomplete="new-password">
</div>
</div>
<div class="row mb-0">
<div class="col-md-6 offset-md-4">
<button type="submit" class="btn btn-primary">
{{ __('Reset Password') }}
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection

View File

@@ -0,0 +1,77 @@
@extends('layouts.app')
@section('content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">{{ __('Register') }}</div>
<div class="card-body">
<form method="POST" action="{{ route('register') }}">
@csrf
<div class="row mb-3">
<label for="name" class="col-md-4 col-form-label text-md-end">{{ __('Name') }}</label>
<div class="col-md-6">
<input id="name" type="text" class="form-control @error('name') is-invalid @enderror" name="name" value="{{ old('name') }}" required autocomplete="name" autofocus>
@error('name')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>
<div class="row mb-3">
<label for="email" class="col-md-4 col-form-label text-md-end">{{ __('Email Address') }}</label>
<div class="col-md-6">
<input id="email" type="email" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ old('email') }}" required autocomplete="email">
@error('email')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>
<div class="row mb-3">
<label for="password" class="col-md-4 col-form-label text-md-end">{{ __('Password') }}</label>
<div class="col-md-6">
<input id="password" type="password" class="form-control @error('password') is-invalid @enderror" name="password" required autocomplete="new-password">
@error('password')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>
<div class="row mb-3">
<label for="password-confirm" class="col-md-4 col-form-label text-md-end">{{ __('Confirm Password') }}</label>
<div class="col-md-6">
<input id="password-confirm" type="password" class="form-control" name="password_confirmation" required autocomplete="new-password">
</div>
</div>
<div class="row mb-0">
<div class="col-md-6 offset-md-4">
<button type="submit" class="btn btn-primary">
{{ __('Register') }}
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection

View File

@@ -0,0 +1,28 @@
@extends('layouts.app')
@section('content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">{{ __('Verify Your Email Address') }}</div>
<div class="card-body">
@if (session('resent'))
<div class="alert alert-success" role="alert">
{{ __('A fresh verification link has been sent to your email address.') }}
</div>
@endif
{{ __('Before proceeding, please check your email for a verification link.') }}
{{ __('If you did not receive the email') }},
<form class="d-inline" method="POST" action="{{ route('verification.resend') }}">
@csrf
<button type="submit" class="btn btn-link p-0 m-0 align-baseline">{{ __('click here to request another') }}</button>.
</form>
</div>
</div>
</div>
</div>
</div>
@endsection

View File

@@ -0,0 +1,46 @@
@extends('layouts.app')
@section('content')
<div class="container mt-5">
<h2>{{ isset($comment) ? 'Edit comment' : 'Manager Comment Section' }}</h2>
@include('commons.notifications')
<form action="{{ isset($comment) ? route('comments.update', $comment->id) : route('comments.store') }}" method="POST">
<input type="hidden" name="user_id" value="{{ \Auth::user()->id }}">
@csrf
@if (isset($comment_body))
@method('PUT')
@endif
<div class="card w-100">
<div class="card-body">
<h3>Project Details</h3>
<h5 class="card-title">{{ $project_arr->project->name }}</h5>
<p class="card-text">{{ $project_arr->project->description }} </p>
</div>
</div>
<hr>
<h3>Latest Update</h3>
<table class="table">
<thead>
<tr>
<th scope="col">Status Description</th>
<th scope="col">Current Status</th>
<th scope="col">Date Updated</th>
</tr>
</thead>
<tbody>
<tr>
<td>{{ $project_arr->description }}</td>
<td>{{ $project_arr->status }}</td>
<td>{{ $project_arr->created_at }}</td>
</tr>
</tbody>
</table>
<hr>
<div class="mb-3">
<label for="commnetBody" class="form-label">Comment Body</label>
<textarea class="form-control" id="commnetBody" name="comment_body" rows="4" required>{{ $comment->body ?? '' }}</textarea>
</div>
<button type="submit" class="btn btn-primary">Create comment</button>
</form>
</div>
@endsection

View File

@@ -0,0 +1,51 @@
@extends('layouts.app')
@section('content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-10">
@include('commons.notifications')
<div class="card">
<div class="card-header">{{ __('Projects') }}</div>
<div class="card-body">
@if (session('status'))
<div class="alert alert-success" role="alert">
{{ session('status') }}
</div>
@endif
<div class="float-end">
<a href="/projects/create" class="btn btn-primary btn-sm">Add Projects</a>
</div>
<table class="table">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Name</th>
<th scope="col">Description</th>
<th scope="col">Status</th>
<th scope="col">Date Created</th>
<th scope="col">Action</th>
</tr>
</thead>
<tbody>
<?php $count = 1; ?>
@foreach($projects as $row)
<tr>
<th scope="row">{{ $count }}</th>
<td>{{ $row->name }}</td>
<td>{{ $row->description }}</td>
<td>{{ $row->status }}</td>
<td>{{ $row->created_at }}</td>
<td><a href="{{ url('project-status/add_status', $row->id)}}" class="btn btn-link">Add Status</a></td>
</tr>
<?php $count++; ?>
@endforeach
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
@endsection

View File

@@ -0,0 +1,18 @@
@if ($errors->any())
<ul class="alert alert-danger" style="padding-left:10px; list-style-type: none;">
@foreach ($errors->all() as $error)
<li>{!! $error !!}</li>
@endforeach
</ul>
@endif
<!-- color: #fff; -->
@if(Session::has('success_message'))
<div style="text-align: center" class="alert alert-success notification-alert" style="">
{{ Session::get('success_message') }}
</div>
@elseif(Session::has('error_message'))
<div style="text-align: center" class="alert alert-danger notification-alert" style="">
{{ Session::get('error_message') }}
</div>
@endif

View File

@@ -0,0 +1,31 @@
@extends('layouts.app')
@section('content')
<div class="container mt-5">
<h2>{{ 'Add Project Status' }}</h2>
@include('commons.notifications')
<form action="{{ isset($project_status) ? route('project-status.update', $project_status->id) : route('project-status.store') }}" method="POST">
<input type="hidden" name="assignee_id" value="{{ \Auth::user()->id }}">
<input type="hidden" name="project_id" value="{{ $project->id }}">
@csrf
<div class="mb-3">
<label for="description" class="form-label">Description</label>
<textarea class="form-control" name="description" id="description" rows="4" required></textarea>
</div>
<div class="mb-3">
<label for="status" class="form-label">Status</label>
<select class="form-select" id="status" name="status" required>
<option value="" disabled>-- Select Status--</option>
<option value="Pending">Pending</option>
<option value="In Progress">In Progress</option>
<option value="Completed">Completed</option>
</select>
</div>
<button type="submit" class="btn btn-primary">{{ 'Add Status' }}</button>
</form>
</div>
@endsection

View File

@@ -0,0 +1,67 @@
@extends('layouts.app')
@section('content')
<div class="container mt-4">
<div class="d-flex justify-content-between align-items-center mb-4">
<div>
<h2>{{ $service->name }} Subscriber Log</h2>
<p class="text-muted mb-0">Schema Type: {{ strtoupper($service->schema_type) }} | Last Synced: {{ $service->last_synced_at ?? 'Never' }}</p>
</div>
<a href="{{ route('dashboard.index') }}" class="btn btn-outline-secondary">Back to Dashboard</a>
</div>
<!-- Search Bar -->
<form method="GET" action="{{ route('services.details', $service->id) }}" class="mb-4">
<div class="input-group">
<input type="text" name="search" class="form-control" placeholder="Search by phone number, content etc..." value="{{ request('search') }}">
<button class="btn btn-primary" type="submit">Search</button>
@if(request('search'))
<a href="{{ route('services.details', $service->id) }}" class="btn btn-outline-dark">Reset</a>
@endif
</div>
</form>
<!-- Subscribers Table -->
<div class="card shadow-sm">
<div class="card-body p-0">
<div class="table-responsive">
<table class="table table-hover mb-0 align-middle">
<thead class="table-light">
<tr>
<th>Phone Number</th>
<th>Subscribed Content</th>
<th>Status</th>
<th>Join Date</th>
<th>Last Synced</th>
</tr>
</thead>
<tbody>
@forelse($subscribers as $sub)
<tr>
<td class="fw-bold">{{ $sub->phone_number }}</td>
<td><span class="badge bg-info text-dark">{{ $sub->content_name }}</span></td>
<td>
<span class="badge {{ $sub->status === 'active' ? 'bg-success' : 'bg-danger' }}">
{{ ucfirst($sub->status) }}
</span>
</td>
<td>{{ $sub->join_date ?? 'N/A' }}</td>
<td class="text-muted small">{{ $sub->updated_at }}</td>
</tr>
@empty
<tr>
<td colspan="5" class="text-center py-4 text-muted">No subscribers found.</td>
</tr>
@endforelse
</tbody>
</table>
</div>
</div>
</div>
<!-- Pagination Links -->
<div class="mt-4">
{{ $subscribers->withQueryString()->links() }}
</div>
</div>
@endsection

View File

@@ -0,0 +1,57 @@
@extends('layouts.app')
@section('content')
<div class="container mt-4">
<h2 class="mb-4">Subscriptions Overview</h2>
<!-- Grand Totals Section -->
<div class="row mb-5">
<div class="col-md-4">
<div class="card bg-primary text-white shadow-sm">
<div class="card-body">
<h6 class="card-title text-uppercase text-white-50">Total Subscribers (All Services)</h6>
<h2 id="grand-total" class="mb-0">
<span class="spinner-border spinner-border-sm" role="status"></span>
</h2>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card bg-success text-white shadow-sm">
<div class="card-body">
<h6 class="card-title text-uppercase text-white-50">Total Active</h6>
<h2 id="grand-active" class="mb-0">
<span class="spinner-border spinner-border-sm" role="status"></span>
</h2>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card bg-secondary text-white shadow-sm">
<div class="card-body">
<h6 class="card-title text-uppercase text-white-50">Total Inactive</h6>
<h2 id="grand-inactive" class="mb-0">
<span class="spinner-border spinner-border-sm" role="status"></span>
</h2>
</div>
</div>
</div>
</div>
<h4 class="mb-3 text-muted">Service Breakdown</h4>
<!-- Dynamic Service Cards Container -->
<div class="row" id="service-cards-container">
<!-- jQuery will inject the individual service cards here -->
<div class="col-12 text-center py-5" id="loading-services">
<div class="spinner-border text-primary" role="status">
<span class="visually-hidden">Loading...</span>
</div>
<p class="text-muted mt-2">Aggregating cross-service metrics...</p>
</div>
</div>
</div>
@endsection
@push('scripts')
<script src="{{ asset('public/assets/js/dashboard.js') }}"></script>
@endpush

View File

@@ -0,0 +1,23 @@
@extends('layouts.app')
@section('content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">{{ __('Dashboard') }}</div>
<div class="card-body">
@if (session('status'))
<div class="alert alert-success" role="alert">
{{ session('status') }}
</div>
@endif
{{ __('You are logged in!') }}
</div>
</div>
</div>
</div>
</div>
@endsection

View File

@@ -0,0 +1,81 @@
<!doctype html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- CSRF Token -->
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>Click Content Management</title>
<!-- Fonts -->
<link rel="dns-prefetch" href="//fonts.bunny.net">
<link href="https://fonts.bunny.net/css?family=Nunito" rel="stylesheet">
<!-- Scripts -->
<link rel="stylesheet" href="{{ url('public/assets/libs/bootstrap/css/bootstrap5.3.2.css') }}">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.13.1/font/bootstrap-icons.min.css">
@stack('styles')
<script>
var base_url = "{!! url('/') !!}";
</script>
</head>
<body>
<div id="app">
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<div class="container-fluid">
<a class="navbar-brand" href="#">CML</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link active" aria-current="page" href="/">Home</a>
</li>
<!-- <li class="nav-item">
<a class="nav-link" href="">Projects</a>
</li>
<li class="nav-item">
<a class="nav-link" href="">Project Statuses</a>
</li>
-->
</ul>
<div class="dropdown">
<button class="btn btn-secondary dropdown-toggle" type="button" id="userDropdown" data-bs-toggle="dropdown" aria-expanded="false">
User Profile
</button>
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="userDropdown">
<li><a class="dropdown-item" href="#">My Profile</a></li>
<li><a class="dropdown-item" href="#">Settings</a></li>
<li>
<a class="dropdown-item" href="{{ route('logout') }}"
onclick="event.preventDefault();
document.getElementById('logout-form').submit();">
{{ __('Logout') }}
</a>
<form id="logout-form" action="{{ route('logout') }}" method="POST" class="d-none">
@csrf
</form>
</li>
</ul>
</div>
</div>
</div>
</nav>
<main class="py-4">
@yield('content')
</main>
</div>
@stack('modals')
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<script src="{{ url('public/assets/libs/bootstrap/js/bootstrap5.3.2.js') }}" defer></script>
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
@stack('scripts')
</body>
</html>

View File

@@ -0,0 +1,51 @@
<nav class="navbar navbar-expand-md navbar-light bg-white shadow-sm">
<div class="container">
<a class="navbar-brand" href="{{ url('/') }}">Project Status Reporting</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="{{ __('Toggle navigation') }}">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<!-- Left Side Of Navbar -->
<ul class="navbar-nav me-auto">
</ul>
<!-- Right Side Of Navbar -->
<ul class="navbar-nav ms-auto">
<!-- Authentication Links -->
@guest
@if (Route::has('login'))
<li class="nav-item">
<a class="nav-link" href="{{ route('login') }}">{{ __('Login') }}</a>
</li>
@endif
@if (Route::has('register'))
<li class="nav-item">
<a class="nav-link" href="{{ route('register') }}">{{ __('Register') }}</a>
</li>
@endif
@else
<li class="nav-item dropdown">
<a id="navbarDropdown" class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false" v-pre>
{{ Auth::user()->name }}
</a>
<div class="dropdown-menu dropdown-menu-end" aria-labelledby="navbarDropdown">
<a class="dropdown-item" href="{{ route('logout') }}"
onclick="event.preventDefault();
document.getElementById('logout-form').submit();">
{{ __('Logout') }}
</a>
<form id="logout-form" action="{{ route('logout') }}" method="POST" class="d-none">
@csrf
</form>
</div>
</li>
@endguest
</ul>
</div>
</div>
</nav>

View File

@@ -0,0 +1,198 @@
@extends('layouts.app')
@section('content')
<div class="container mt-5">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card shadow-sm">
<div class="card-header bg-primary text-white">
<h5 class="mb-0">Add New Subscription Service</h5>
</div>
<div class="card-body">
{{-- Success Message --}}
@if(session('success'))
<div class="alert alert-success alert-dismissible fade show" role="alert">
{{ session('success') }}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
@endif
<form action="{{ route('services.store') }}" method="POST">
@csrf
<div class="row mb-3">
<div class="col-md-6">
<label for="name" class="form-label">Service Name</label>
<input type="text" class="form-control @error('name') is-invalid @enderror" id="name" name="name" value="{{ old('name') }}" placeholder="e.g., Live Scores" required>
@error('name')<div class="invalid-feedback">{{ $message }}</div>@enderror
</div>
<div class="col-md-6">
<label for="schema_type" class="form-label">Schema Type</label>
<select class="form-select @error('schema_type') is-invalid @enderror" id="schema_type" name="schema_type" required>
<option value="" disabled selected>Select schema format...</option>
<option value="type_a" {{ old('schema_type') == 'type_a' ? 'selected' : '' }}>Type A (msisdn, is_active)</option>
<option value="type_b" {{ old('schema_type') == 'type_b' ? 'selected' : '' }}>Type B (cell_number, sub_state)</option>
</select>
@error('schema_type')<div class="invalid-feedback">{{ $message }}</div>@enderror
</div>
</div>
<hr class="my-4">
<h6 class="mb-3 text-muted">Database Connection</h6>
<div class="row mb-3">
<div class="col-md-8">
<label for="db_host" class="form-label">Host / IP Address</label>
<input type="text" class="form-control @error('db_host') is-invalid @enderror" id="db_host" name="db_host" value="{{ old('db_host') }}" required>
@error('db_host')<div class="invalid-feedback">{{ $message }}</div>@enderror
</div>
<div class="col-md-4">
<label for="db_port" class="form-label">Port</label>
<input type="number" class="form-control @error('db_port') is-invalid @enderror" id="db_port" name="db_port" value="{{ old('db_port', 3306) }}" required>
@error('db_port')<div class="invalid-feedback">{{ $message }}</div>@enderror
</div>
</div>
<div class="row mb-3">
<div class="col-md-12">
<label for="db_name" class="form-label">Database Name</label>
<input type="text" class="form-control @error('db_name') is-invalid @enderror" id="db_name" name="db_name" value="{{ old('db_name') }}" required>
@error('db_name')<div class="invalid-feedback">{{ $message }}</div>@enderror
</div>
</div>
<div class="row mb-4">
<div class="col-md-6">
<label for="db_user" class="form-label">Database Username</label>
<input type="text" class="form-control @error('db_user') is-invalid @enderror" id="db_user" name="db_user" value="{{ old('db_user') }}" required>
@error('db_user')<div class="invalid-feedback">{{ $message }}</div>@enderror
</div>
<div class="col-md-6">
<label for="db_password" class="form-label">Database Password</label>
<div class="input-group">
<input type="password" class="form-control @error('db_password') is-invalid @enderror" id="db_password" name="db_password" required>
<button class="btn btn-outline-secondary" type="button" id="togglePassword">Show</button>
</div>
@error('db_password')<div class="d-block invalid-feedback">{{ $message }}</div>@enderror
</div>
</div>
<!-- Add this placeholder for the AJAX response just above the buttons -->
<div id="test-connection-alert" class="alert d-none mb-4" role="alert"></div>
<div class="d-flex justify-content-between align-items-center">
<button type="button" id="testConnectionBtn" class="btn btn-outline-info">
<span class="spinner-border spinner-border-sm d-none me-1" id="testSpinner" role="status" aria-hidden="true"></span>
Test Connection
</button>
<div>
<button type="submit" class="btn btn-primary" id="saveServiceBtn" disabled>
Save Subscription Service
</button>
</div>
</div>
<small class="text-muted d-block text-end mt-1" id="saveHint">
You must successfully test the connection before saving.
</small>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection
@push('scripts')
<script>
$(document).ready(function() {
// Selectors for connection fields
const connectionInputs = '#db_host, #db_port, #db_name, #db_user, #db_password';
// 1. Automatically re-disable the save button if ANY connection field is edited
$(connectionInputs).on('input change', function() {
$('#saveServiceBtn').prop('disabled', true);
$('#saveHint').text('Connection parameters changed. Re-testing required before saving.')
.removeClass('text-success')
.addClass('text-muted');
});
// 2. Toggle password visibility
$('#togglePassword').on('click', function() {
const passwordInput = $('#db_password');
const type = passwordInput.attr('type') === 'password' ? 'text' : 'password';
passwordInput.attr('type', type);
$(this).text(type === 'password' ? 'Show' : 'Hide');
});
// 3. Test Connection AJAX Logic
$('#testConnectionBtn').on('click', function(e) {
e.preventDefault();
let btn = $(this);
let spinner = $('#testSpinner');
let alertBox = $('#test-connection-alert');
let saveBtn = $('#saveServiceBtn');
let saveHint = $('#saveHint');
let payload = {
db_host: $('#db_host').val(),
db_port: $('#db_port').val(),
db_name: $('#db_name').val(),
db_user: $('#db_user').val(),
db_password: $('#db_password').val(),
_token: $('input[name="_token"]').val()
};
if (!payload.db_host || !payload.db_name || !payload.db_user || !payload.db_password) {
alertBox.removeClass('d-none alert-success alert-warning')
.addClass('alert-danger')
.text('Please fill out all database fields before testing.');
return;
}
btn.prop('disabled', true);
spinner.removeClass('d-none');
alertBox.addClass('d-none');
$.ajax({
url: "{{ route('services.test') }}",
method: 'POST',
data: payload,
success: function(response) {
alertBox.removeClass('d-none alert-danger alert-warning')
.addClass('alert-success')
.text(response.message);
// ENABLE SAVE BUTTON
saveBtn.prop('disabled', false);
saveHint.text('Connection verified. Ready to save.')
.removeClass('text-muted')
.addClass('text-success');
},
error: function(xhr) {
let errorMessage = xhr.responseJSON && xhr.responseJSON.message
? xhr.responseJSON.message
: 'An unexpected error occurred.';
alertBox.removeClass('d-none alert-success alert-warning')
.addClass('alert-danger')
.text(errorMessage);
// KEEP SAVE BUTTON DISABLED
saveBtn.prop('disabled', true);
saveHint.text('Connection failed. Fix credentials and test again.')
.removeClass('text-success')
.addClass('text-muted');
},
complete: function() {
btn.prop('disabled', false);
spinner.addClass('d-none');
}
});
});
});
</script>
@endpush

File diff suppressed because one or more lines are too long