added sync button, improved UI
This commit is contained in:
@@ -9,7 +9,6 @@ class DashboardController extends Controller
|
||||
{
|
||||
|
||||
public function index(){
|
||||
// dd('public insanity');
|
||||
return view('dashboard.index');
|
||||
}
|
||||
|
||||
@@ -50,8 +49,6 @@ class DashboardController extends Controller
|
||||
'services' => $stats
|
||||
]);
|
||||
}
|
||||
// app/Http/Controllers/DashboardController.php
|
||||
// app/Http/Controllers/DashboardController.php
|
||||
public function details(Request $request, $id)
|
||||
{
|
||||
$service = DB::table('subscription_services')->where('id', $id)->first();
|
||||
@@ -77,7 +74,6 @@ class DashboardController extends Controller
|
||||
|
||||
return view('dashboard.details', compact('service', 'subscribers'));
|
||||
}
|
||||
// app/Http/Controllers/DashboardController.php
|
||||
public function subscriberProfile(Request $request, $serviceId, $msisdn)
|
||||
{
|
||||
$service = DB::table('subscription_services')->where('id', $serviceId)->first();
|
||||
|
||||
6
info.md
6
info.md
@@ -35,3 +35,9 @@
|
||||
techappsuser@206.225.84.201:/home/techappsuser/DockerProjects/subscription_services/src/
|
||||
|
||||
|
||||
|
||||
\App\Models\User::create([
|
||||
'name' => 'Admin User',
|
||||
'email' => 'kwesi@click-mobile.com',
|
||||
'password' => Hash::make('franctic@233')
|
||||
]);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
// public/js/dashboard.js
|
||||
$(document).ready(function() {
|
||||
console.log(base_url);
|
||||
function fetchDashboardStats() {
|
||||
$.ajax({
|
||||
url: base_url + '/api/dashboard/stats',
|
||||
@@ -83,4 +82,53 @@ $(document).ready(function() {
|
||||
|
||||
// Todo: Refresh data every 5 minutes (300000 ms) automatically without page reload
|
||||
// setInterval(fetchDashboardStats, 300000);
|
||||
|
||||
$('#syncTriggerBtn').on('click', function() {
|
||||
let btn = $(this);
|
||||
let icon = $('#syncIcon');
|
||||
let text = $('#syncText');
|
||||
|
||||
// Disable button and add spinning effect
|
||||
btn.prop('disabled', true);
|
||||
icon.addClass('spinner-border spinner-border-sm');
|
||||
text.text('Syncing...');
|
||||
|
||||
$.ajax({
|
||||
url: base_url + '/api/subscriptions/sync',
|
||||
method: 'POST',
|
||||
data: {
|
||||
_token: $('meta[name="csrf-token"]').attr('content')
|
||||
},
|
||||
success: function(response) {
|
||||
// Use SweetAlert2 for a clean success alert
|
||||
Swal.fire({
|
||||
icon: 'success',
|
||||
title: 'Synchronized!',
|
||||
text: response.message,
|
||||
timer: 2000,
|
||||
showConfirmButton: false
|
||||
});
|
||||
|
||||
// Refresh dashboard data instantly to reflect updated counts
|
||||
fetchDashboardStats();
|
||||
},
|
||||
error: function(xhr) {
|
||||
let errorMsg = xhr.responseJSON && xhr.responseJSON.message
|
||||
? xhr.responseJSON.message
|
||||
: 'An unexpected error occurred during synchronization.';
|
||||
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Sync Failed',
|
||||
text: errorMsg
|
||||
});
|
||||
},
|
||||
complete: function() {
|
||||
// Restore button state
|
||||
btn.prop('disabled', false);
|
||||
icon.removeClass('spinner-border spinner-border-sm');
|
||||
text.text('Sync Now');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -2,7 +2,15 @@
|
||||
|
||||
@section('content')
|
||||
<div class="container mt-4">
|
||||
<h2 class="mb-4">Subscriptions Overview</h2>
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h2>Subscriptions Overview</h2>
|
||||
|
||||
<!-- Trigger Button -->
|
||||
<button id="syncTriggerBtn" class="btn btn-primary d-flex align-items-center gap-2 shadow-sm">
|
||||
<i class="bi bi-arrow-repeat" id="syncIcon"></i>
|
||||
<span id="syncText">Sync Now</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Grand Totals Section -->
|
||||
<div class="row mb-5">
|
||||
|
||||
81
resources/views/layouts/app.blade copy.php
Normal file
81
resources/views/layouts/app.blade copy.php
Normal 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>
|
||||
@@ -11,66 +11,110 @@
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="dns-prefetch" href="//fonts.bunny.net">
|
||||
<link href="https://fonts.bunny.net/css?family=Nunito" rel="stylesheet">
|
||||
<link href="https://fonts.bunny.net/css?family=Nunito:wght@400;600;700&display=swap" rel="stylesheet">
|
||||
|
||||
<!-- Scripts -->
|
||||
<!-- Scripts & Styles -->
|
||||
<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>
|
||||
|
||||
<!-- Lighter Sea/Sky Blue Theme Customizations -->
|
||||
<style>
|
||||
:root {
|
||||
--bs-primary: #0096c7;
|
||||
--bs-primary-rgb: 0, 150, 199;
|
||||
}
|
||||
body {
|
||||
font-family: 'Nunito', sans-serif;
|
||||
background-color: #f0f7ff; /* Very soft bright ice blue */
|
||||
color: #333333;
|
||||
}
|
||||
.navbar-sea {
|
||||
background: linear-gradient(135deg, #0077b6, #00b4d8); /* Brighter, lighter sea blue gradient */
|
||||
box-shadow: 0 4px 12px rgba(0, 150, 199, 0.2);
|
||||
}
|
||||
.navbar-sea .navbar-brand,
|
||||
.navbar-sea .nav-link {
|
||||
color: #ffffff !important;
|
||||
font-weight: 600;
|
||||
}
|
||||
.navbar-sea .nav-link:hover {
|
||||
color: #caf0f8 !important;
|
||||
}
|
||||
.card {
|
||||
border: none;
|
||||
border-radius: 0.75rem;
|
||||
box-shadow: 0 0.5rem 1rem rgba(0, 119, 182, 0.05);
|
||||
transition: transform 0.2s ease-in-out;
|
||||
}
|
||||
.btn-primary {
|
||||
background-color: #0096c7;
|
||||
border-color: #0077b6;
|
||||
}
|
||||
.btn-primary:hover {
|
||||
background-color: #0077b6;
|
||||
border-color: #03045e;
|
||||
}
|
||||
.dropdown-menu {
|
||||
border-radius: 0.5rem;
|
||||
box-shadow: 0 0.5rem 1rem rgba(0,0,0,0.1);
|
||||
border: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
@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
|
||||
<!-- Lighter Sea Blue Gradient Navbar -->
|
||||
<nav class="navbar navbar-expand-lg navbar-sea py-3">
|
||||
<div class="container-fluid px-4">
|
||||
<a class="navbar-brand d-flex align-items-center gap-2" href="{{ url('/') }}">
|
||||
<i class="bi bi-layers-fill fs-4"></i>
|
||||
<span>CML Subscriptions</span>
|
||||
</a>
|
||||
<button class="navbar-toggler border-0 text-white" 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" style="filter: invert(1);"></span>
|
||||
</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 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="/">
|
||||
<i class="bi bi-house-door me-1"></i> Home
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-light text-primary fw-bold dropdown-toggle px-3 py-2 rounded-pill shadow-sm" type="button" id="userDropdown" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<i class="bi bi-person-circle me-1"></i> User Profile
|
||||
</button>
|
||||
<ul class="dropdown-menu dropdown-menu-end mt-2 p-2" aria-labelledby="userDropdown">
|
||||
<li><a class="dropdown-item rounded py-2" href="#"><i class="bi bi-person me-2 text-muted"></i> My Profile</a></li>
|
||||
<li><a class="dropdown-item rounded py-2" href="#"><i class="bi bi-gear me-2 text-muted"></i> Settings</a></li>
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
<li>
|
||||
<a class="dropdown-item rounded py-2 text-danger" href="{{ route('logout') }}"
|
||||
onclick="event.preventDefault();
|
||||
document.getElementById('logout-form').submit();">
|
||||
<i class="bi bi-box-arrow-right me-2"></i> {{ __('Logout') }}
|
||||
</a>
|
||||
<form id="logout-form" action="{{ route('logout') }}" method="POST" class="d-none">
|
||||
@csrf
|
||||
</form>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</nav>
|
||||
|
||||
<main class="py-4">
|
||||
<main class="py-4 px-2">
|
||||
@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>
|
||||
|
||||
@@ -1,12 +1,19 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
use Illuminate\Support\Facades\Artisan;
|
||||
// Route::get('/', function () {
|
||||
// return view('welcome');
|
||||
// });
|
||||
|
||||
Auth::routes();
|
||||
Auth::routes([
|
||||
'register' => false, // Disables the registration routes
|
||||
'reset' => false, // Disables the password reset routes
|
||||
'verify' => false, // Disables the email verification routes
|
||||
]);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Route::get('/home', [App\Http\Controllers\DashboardController::class, 'index'])->name('home');
|
||||
@@ -15,18 +22,38 @@ Route::get('/', [App\Http\Controllers\DashboardController::class, 'index'])->nam
|
||||
|
||||
|
||||
|
||||
// The main dashboard view
|
||||
Route::get('/', [App\Http\Controllers\DashboardController::class, 'index'])->name('dashboard.index');
|
||||
Route::post('/api/subscriptions/sync', function () {
|
||||
try {
|
||||
// Run the artisan command programmatically
|
||||
Artisan::call('subscriptions:sync');
|
||||
|
||||
// The AJAX endpoint for jQuery to consume
|
||||
Route::get('/api/dashboard/stats', [App\Http\Controllers\DashboardController::class, 'getStats'])->name('api.dashboard.stats');
|
||||
return response()->json([
|
||||
'status' => 'success',
|
||||
'message' => 'Subscriptions synchronized successfully!'
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
return response()->json([
|
||||
'status' => 'error',
|
||||
'message' => 'Sync failed: ' . $e->getMessage()
|
||||
], 500);
|
||||
}
|
||||
})->name('api.subscriptions.sync');
|
||||
|
||||
|
||||
Route::get('/services/create', [App\Http\Controllers\SubscriptionServiceController::class, 'create'])->name('services.create');
|
||||
Route::post('/services', [App\Http\Controllers\SubscriptionServiceController::class, 'store'])->name('services.store');
|
||||
|
||||
Route::post('/services/test-connection', [App\Http\Controllers\SubscriptionServiceController::class, 'testConnection'])->name('services.test');
|
||||
Route::middleware(['auth'])->group(function () {
|
||||
Route::get('/', [App\Http\Controllers\DashboardController::class, 'index'])->name('dashboard.index');
|
||||
|
||||
// routes/web.php
|
||||
Route::get('/services/{id}/details', [App\Http\Controllers\DashboardController::class, 'details'])->name('services.details');
|
||||
Route::get('/services/{serviceId}/subscriber/{msisdn}', [App\Http\Controllers\DashboardController::class, 'subscriberProfile'])->name('services.subscriber.profile');
|
||||
|
||||
Route::get('/api/dashboard/stats', [App\Http\Controllers\DashboardController::class, 'getStats'])->name('api.dashboard.stats');
|
||||
|
||||
|
||||
Route::get('/services/create', [App\Http\Controllers\SubscriptionServiceController::class, 'create'])->name('services.create');
|
||||
Route::post('/services', [App\Http\Controllers\SubscriptionServiceController::class, 'store'])->name('services.store');
|
||||
|
||||
Route::post('/services/test-connection', [App\Http\Controllers\SubscriptionServiceController::class, 'testConnection'])->name('services.test');
|
||||
|
||||
Route::get('/services/{id}/details', [App\Http\Controllers\DashboardController::class, 'details'])->name('services.details');
|
||||
Route::get('/services/{serviceId}/subscriber/{msisdn}', [App\Http\Controllers\DashboardController::class, 'subscriberProfile'])->name('services.subscriber.profile');
|
||||
|
||||
});
|
||||
Reference in New Issue
Block a user