sms and update admin
changes
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
if (! function_exists('sendSMS')) {
|
||||
function sendSMS(string $destination, string $message): bool
|
||||
{
|
||||
$key = env('SMS_API') ?: '';
|
||||
$sender = env('SMS_SENDER') ?: 'SMSBit';
|
||||
$url = env('SMS_URL') ?: 'https://sms.bibhutisolutions.com/smsapi/index';
|
||||
|
||||
$params = http_build_query([
|
||||
'key' => $key,
|
||||
'contacts' => $destination,
|
||||
'senderid' => $sender,
|
||||
'msg' => $message,
|
||||
'responsetype' => 'json',
|
||||
]);
|
||||
|
||||
$separator = parse_url($url, PHP_URL_QUERY) ? '&' : '?';
|
||||
$url_final = $url . $separator . $params;
|
||||
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, $url_final);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
|
||||
$response = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
|
||||
if ($response === false || $httpCode >= 400) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -71,6 +71,7 @@ class HomeController extends Controller
|
||||
$data['registrations'] = $query;
|
||||
$data['search'] = $search;
|
||||
$data['total'] = Registration::count();
|
||||
$data['todayTotal'] = $query->total();
|
||||
$data['played'] = $registrations->filter(fn($r) => !empty($r['today_goals']) && count($r['today_goals']) >= 3)->count();
|
||||
$data['topScore'] = $registrations->max('total_score') ?? 0;
|
||||
$data['countries'] = Country::where('status', 1)->orderBy('title')->get(['id','title','country_flag']);
|
||||
|
||||
@@ -8,6 +8,8 @@ use App\Models\GameShot;
|
||||
use App\Models\Registration;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
|
||||
|
||||
class RegistrationController extends Controller
|
||||
{
|
||||
@@ -64,6 +66,15 @@ class RegistrationController extends Controller
|
||||
'score' => 0,
|
||||
]);
|
||||
|
||||
if ($request->filled('email')) {
|
||||
dispatch(function () use ($registration) {
|
||||
Mail::send('emails.welcome', ['registration' => $registration], function ($message) use ($registration) {
|
||||
$message->to($registration->email)
|
||||
->subject('Welcome to the Score & Win Challenge 2026! ⚽');
|
||||
});
|
||||
})->afterResponse();
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'status' => 'session_created',
|
||||
'registration' => [
|
||||
@@ -95,9 +106,11 @@ class RegistrationController extends Controller
|
||||
|
||||
Cache::put("otp_$phone", $otp, now()->addMinutes(2));
|
||||
|
||||
sendSMS($phone, "Your OTP for Score & Win Challenge 2026 is: $otp. It is valid for 2 minutes.");
|
||||
|
||||
return response()->json([
|
||||
'status' => true,
|
||||
'otp' => $otp // remove in production
|
||||
// 'otp' => $otp // remove in production
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -263,6 +276,16 @@ class RegistrationController extends Controller
|
||||
$registration = $session->registration;
|
||||
$registration->update(['total_score' => $registration->sessions()->sum('score')]);
|
||||
|
||||
if ($registration->email && $request->shot_number == 3) {
|
||||
dispatch(function () use ($registration) {
|
||||
Mail::send('emails.score', ['registration' => $registration], function ($message) use ($registration) {
|
||||
$message->to($registration->email)
|
||||
->subject('Your Score Update for the Score & Win Challenge 2026! ⚽');
|
||||
});
|
||||
})->afterResponse();
|
||||
}
|
||||
|
||||
|
||||
return response()->json([
|
||||
'status' => 'ok',
|
||||
'total_score' => $registration->fresh()->total_score,
|
||||
|
||||
+4
-1
@@ -26,7 +26,10 @@
|
||||
"App\\": "app/",
|
||||
"Database\\Factories\\": "database/factories/",
|
||||
"Database\\Seeders\\": "database/seeders/"
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
"app/Helpers/Helper.php"
|
||||
]
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
|
||||
@@ -97,7 +97,7 @@
|
||||
</svg>
|
||||
Today's Sessions
|
||||
</div>
|
||||
<span class="text-xs font-medium text-slate-400 bg-slate-100 px-2 py-0.5 rounded-full">{{ $total }} students</span>
|
||||
<span class="text-xs font-medium text-slate-400 bg-slate-100 px-2 py-0.5 rounded-full">{{ $todayTotal }} students</span>
|
||||
</div>
|
||||
<table class="w-full text-sm">
|
||||
<thead>
|
||||
@@ -178,7 +178,7 @@
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="px-5 py-3 border-t border-slate-100 flex items-center justify-between">
|
||||
<p class="text-xs text-slate-400">Showing <span class="font-medium text-slate-600">{{ $total }}</span> students</p>
|
||||
<p class="text-xs text-slate-400">Showing <span class="font-medium text-slate-600">{{ $todayTotal }}</span> students</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -293,10 +293,10 @@
|
||||
</div>
|
||||
|
||||
<!-- Dev OTP preview -->
|
||||
<div id="otpPreview" class="hidden bg-amber-50 border border-amber-200 rounded-xl px-4 py-2.5 mb-4 flex items-center gap-2">
|
||||
{{-- <div id="otpPreview" class="hidden bg-amber-50 border border-amber-200 rounded-xl px-4 py-2.5 mb-4 flex items-center gap-2">
|
||||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="#d97706" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>
|
||||
<p class="text-xs font-semibold text-amber-700">Dev — OTP: <span id="otpValue" class="font-mono tracking-widest"></span></p>
|
||||
</div>
|
||||
</div> --}}
|
||||
|
||||
<label class="block text-xs font-semibold text-slate-600 mb-1.5 uppercase tracking-wide">Enter OTP</label>
|
||||
<input type="text" id="otp" maxlength="6" placeholder="— — — — — —"
|
||||
@@ -527,10 +527,10 @@ function refreshPanel() {
|
||||
el.innerHTML = '✕ Miss';
|
||||
}
|
||||
} else if (i === currentShot) {
|
||||
el.classList.add('border-indigo-400', 'bg-indigo-50', 'text-indigo-600', 'animate-pulse');
|
||||
el.classList.add('border-indigo-400', 'bg-indigo-50', 'text-indigo-600', 'animate-pulse', 'cursor-pointer');
|
||||
el.innerHTML = 'Shot ' + i;
|
||||
} else {
|
||||
el.classList.add('border-slate-200', 'bg-slate-50', 'text-slate-400');
|
||||
el.classList.add('border-slate-200', 'bg-slate-50', 'text-slate-400', 'cursor-pointer');
|
||||
el.innerHTML = 'Shot ' + i;
|
||||
}
|
||||
}
|
||||
@@ -827,10 +827,10 @@ document.getElementById('sendOtpBtn').addEventListener('click', function () {
|
||||
})
|
||||
.done(res => {
|
||||
document.getElementById('phoneSentTo').textContent = phone;
|
||||
if (res.otp) {
|
||||
document.getElementById('otpPreview').classList.remove('hidden');
|
||||
document.getElementById('otpValue').textContent = res.otp;
|
||||
}
|
||||
// if (res.otp) {
|
||||
// document.getElementById('otpPreview').classList.remove('hidden');
|
||||
// document.getElementById('otpValue').textContent = res.otp;
|
||||
// }
|
||||
goToStep(2);
|
||||
})
|
||||
.fail(() => showError('Failed to send OTP. Please try again.'))
|
||||
|
||||
@@ -0,0 +1,233 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Keep Climbing the Leaderboard!</title>
|
||||
|
||||
<style>
|
||||
body{
|
||||
margin:0;
|
||||
padding:0;
|
||||
background:#f5f8fc;
|
||||
font-family:Arial, Helvetica, sans-serif;
|
||||
color:#333333;
|
||||
}
|
||||
|
||||
.wrapper{
|
||||
width:100%;
|
||||
padding:30px 15px;
|
||||
}
|
||||
|
||||
.container{
|
||||
max-width:600px;
|
||||
margin:0 auto;
|
||||
background:#ffffff;
|
||||
border-radius:10px;
|
||||
overflow:hidden;
|
||||
box-shadow:0 3px 15px rgba(0,0,0,0.08);
|
||||
}
|
||||
|
||||
.header{
|
||||
background:#115AAA;
|
||||
padding:35px 20px;
|
||||
text-align:center;
|
||||
}
|
||||
|
||||
.header h1{
|
||||
margin:0;
|
||||
color:#ffffff;
|
||||
font-size:28px;
|
||||
}
|
||||
|
||||
.content{
|
||||
padding:35px 30px;
|
||||
}
|
||||
|
||||
.greeting{
|
||||
font-size:18px;
|
||||
margin-bottom:20px;
|
||||
}
|
||||
|
||||
.score-box{
|
||||
background:#f0f8ff;
|
||||
border-left:5px solid #00AEEF;
|
||||
padding:20px;
|
||||
text-align:center;
|
||||
border-radius:8px;
|
||||
margin:25px 0;
|
||||
}
|
||||
|
||||
.score-label{
|
||||
font-size:15px;
|
||||
color:#666;
|
||||
}
|
||||
|
||||
.score{
|
||||
font-size:42px;
|
||||
font-weight:bold;
|
||||
color:#115AAA;
|
||||
margin-top:10px;
|
||||
}
|
||||
|
||||
.highlight{
|
||||
color:#115AAA;
|
||||
font-weight:bold;
|
||||
}
|
||||
|
||||
.cta{
|
||||
text-align:center;
|
||||
margin:35px 0;
|
||||
}
|
||||
|
||||
.btn{
|
||||
display:inline-block;
|
||||
padding:14px 32px;
|
||||
background:#00AEEF;
|
||||
color:#ffffff !important;
|
||||
text-decoration:none;
|
||||
border-radius:5px;
|
||||
font-weight:bold;
|
||||
}
|
||||
|
||||
.services{
|
||||
background:#f7fbff;
|
||||
padding:20px;
|
||||
border-radius:8px;
|
||||
margin-top:25px;
|
||||
}
|
||||
|
||||
.services h3{
|
||||
margin-top:0;
|
||||
color:#115AAA;
|
||||
}
|
||||
|
||||
.services ul{
|
||||
padding-left:20px;
|
||||
margin-bottom:0;
|
||||
}
|
||||
|
||||
.footer{
|
||||
background:#f4f7fa;
|
||||
padding:25px;
|
||||
text-align:center;
|
||||
font-size:13px;
|
||||
color:#777;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div class="wrapper">
|
||||
|
||||
<div class="container">
|
||||
|
||||
<div class="header">
|
||||
<h1>Score & Win Challenge</h1>
|
||||
</div>
|
||||
<div style="background:#ffffff;padding:25px;text-align:center;">
|
||||
<img src="https://rosettaeducation.com/storage/rosetta/files/1/rosetta-logo.png"
|
||||
alt="Rosetta Education International"
|
||||
style="max-width:200px;width:100%;height:auto;">
|
||||
</div>
|
||||
|
||||
<div style="height:6px;background:#00AEEF;"></div>
|
||||
|
||||
<div class="content">
|
||||
|
||||
<div class="greeting">
|
||||
Hi {{ $registration->name }},
|
||||
</div>
|
||||
|
||||
<p>
|
||||
Thank you for participating in today's <span class="highlight">Score & Win Challenge</span> at Rosetta Education Consultancy!
|
||||
</p>
|
||||
|
||||
<div class="score-box">
|
||||
<div class="score-label">Your Current Score</div>
|
||||
<div class="score">{{ $registration->total_score}} Points</div>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
Great job! Every point counts, and the competition is just getting started.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Remember, you can return <strong>tomorrow and play again</strong> to improve your score and climb higher on the leaderboard.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The participant with the highest score at the end of the campaign will be crowned the <strong>Ultimate Winner</strong> and receive exciting prizes and rewards.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The more days you play, the better your chances of winning.
|
||||
</p>
|
||||
|
||||
|
||||
<div class="services">
|
||||
<h3>While You're Here, Explore Your Study Abroad Opportunities</h3>
|
||||
|
||||
<ul>
|
||||
<li>Free education counseling & career guidance</li>
|
||||
<li>Study destination support – UK, Australia, Canada & USA</li>
|
||||
<li>University selection tailored to your profile</li>
|
||||
<li>SOP (Statement of Purpose) preparation</li>
|
||||
<li>Scholarship assistance</li>
|
||||
<li>Admission processing & documentation support
|
||||
</li>
|
||||
<li>Student visa guidance</li>
|
||||
<li>IELTS & PTE preparation classes</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<p style="margin-top:25px;">
|
||||
We look forward to seeing you again tomorrow. Keep playing, keep scoring, and keep moving closer to your dream destination.
|
||||
</p>
|
||||
|
||||
</div>
|
||||
<div style="
|
||||
margin-top:40px;
|
||||
padding:25px;
|
||||
border-top:1px solid #e5e5e5;
|
||||
">
|
||||
|
||||
<p style="margin:0 0 15px 0;">
|
||||
Best Regards,
|
||||
</p>
|
||||
|
||||
<img src="https://rosettaeducation.com/storage/rosetta/files/1/rosetta-logo.png"
|
||||
alt="Rosetta Education International"
|
||||
style="max-width:200px;height:auto;margin-bottom:15px;">
|
||||
|
||||
<p style="margin:0;font-weight:bold;color:#115AAA;">
|
||||
Rosetta Education International
|
||||
</p>
|
||||
|
||||
<p style="margin:8px 0;color:#666;font-size:14px;">
|
||||
Your Trusted Study Abroad Partner
|
||||
</p>
|
||||
|
||||
<p style="margin:5px 0;color:#666;font-size:14px;">
|
||||
📧 info@rosettaeducation.com
|
||||
</p>
|
||||
|
||||
<p style="margin:5px 0;color:#666;font-size:14px;">
|
||||
📞 +977-1-4560413
|
||||
</p>
|
||||
|
||||
<p style="margin:5px 0;color:#666;font-size:14px;">
|
||||
🌐 https://rosettaeducation.com/
|
||||
</p>
|
||||
|
||||
<div class="footer">
|
||||
© 2026 Rosetta Education Consultancy<br>
|
||||
Helping Students Reach Global Opportunities
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,211 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Welcome to Score & Win</title>
|
||||
|
||||
<style>
|
||||
body{
|
||||
margin:0;
|
||||
padding:0;
|
||||
background:#f5f8fc;
|
||||
font-family:Arial, Helvetica, sans-serif;
|
||||
color:#333333;
|
||||
}
|
||||
|
||||
.wrapper{
|
||||
width:100%;
|
||||
padding:30px 15px;
|
||||
}
|
||||
|
||||
.container{
|
||||
max-width:600px;
|
||||
margin:0 auto;
|
||||
background:#ffffff;
|
||||
border-radius:10px;
|
||||
overflow:hidden;
|
||||
box-shadow:0 3px 15px rgba(0,0,0,0.08);
|
||||
}
|
||||
|
||||
.header{
|
||||
background:#115AAA;
|
||||
padding:35px 20px;
|
||||
text-align:center;
|
||||
}
|
||||
|
||||
.header h1{
|
||||
margin:0;
|
||||
color:#ffffff;
|
||||
font-size:28px;
|
||||
}
|
||||
|
||||
.content{
|
||||
padding:35px 30px;
|
||||
}
|
||||
|
||||
.welcome-box{
|
||||
background:#f0f8ff;
|
||||
border-left:5px solid #00AEEF;
|
||||
padding:20px;
|
||||
border-radius:8px;
|
||||
margin:25px 0;
|
||||
}
|
||||
|
||||
.cta{
|
||||
text-align:center;
|
||||
margin:35px 0;
|
||||
}
|
||||
|
||||
.btn{
|
||||
display:inline-block;
|
||||
padding:14px 32px;
|
||||
background:#00AEEF;
|
||||
color:#ffffff !important;
|
||||
text-decoration:none;
|
||||
border-radius:5px;
|
||||
font-weight:bold;
|
||||
}
|
||||
|
||||
.services{
|
||||
background:#f7fbff;
|
||||
padding:20px;
|
||||
border-radius:8px;
|
||||
margin-top:25px;
|
||||
}
|
||||
|
||||
.services h3{
|
||||
margin-top:0;
|
||||
color:#115AAA;
|
||||
}
|
||||
|
||||
.footer{
|
||||
background:#f4f7fa;
|
||||
padding:25px;
|
||||
text-align:center;
|
||||
font-size:13px;
|
||||
color:#777;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div class="wrapper">
|
||||
|
||||
<div class="container">
|
||||
|
||||
<div class="header">
|
||||
<h1>Welcome to Score & Win</h1>
|
||||
</div>
|
||||
<div style="background:#ffffff;padding:25px;text-align:center;">
|
||||
<img src="https://rosettaeducation.com/storage/rosetta/files/1/rosetta-logo.png"
|
||||
alt="Rosetta Education International"
|
||||
style="max-width:200px;width:100%;height:auto;">
|
||||
</div>
|
||||
<div class="content">
|
||||
|
||||
<h2 style="color:#115AAA;">Hi {{$registration->name}},</h2>
|
||||
|
||||
<p>
|
||||
Welcome to the exciting <strong>Score & Win Challenge</strong> by Rosetta Education Consultancy.
|
||||
</p>
|
||||
|
||||
<div class="welcome-box">
|
||||
<strong>Your registration has been successfully completed.</strong>
|
||||
<br><br>
|
||||
You're now officially part of the competition and eligible to compete for exciting prizes.
|
||||
</div>
|
||||
|
||||
<p>
|
||||
Here's how it works:
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
<li>Free education counseling & career guidance</li>
|
||||
<li>Study destination support – UK, Australia, Canada & USA</li>
|
||||
<li>University selection tailored to your profile</li>
|
||||
<li>SOP (Statement of Purpose) preparation</li>
|
||||
<li>Scholarship assistance</li>
|
||||
<li>Admission processing & documentation support
|
||||
</li>
|
||||
<li>Student visa guidance</li>
|
||||
<li>IELTS & PTE preparation classes</li>
|
||||
</ul>
|
||||
|
||||
<p>
|
||||
Consistent participation increases your chances of securing a top position and winning amazing rewards.
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
<div class="services">
|
||||
<h3>Why Students Choose Rosetta Education Consultancy</h3>
|
||||
|
||||
<ul>
|
||||
<li>Expert Study Abroad Counseling</li>
|
||||
<li>University Application Assistance</li>
|
||||
<li>Scholarship & Financial Guidance</li>
|
||||
<li>Visa Processing Support</li>
|
||||
<li>Career-Oriented Course Selection</li>
|
||||
<li>End-to-End Student Assistance</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<p style="margin-top:25px;">
|
||||
While you're competing for prizes, don't forget that our team is here to help you achieve your international education goals.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Best of luck, and let the challenge begin!
|
||||
</p>
|
||||
|
||||
</div>
|
||||
<div style="
|
||||
margin-top:40px;
|
||||
padding:25px;
|
||||
border-top:1px solid #e5e5e5;
|
||||
">
|
||||
|
||||
<p style="margin:0 0 15px 0;">
|
||||
Best Regards,
|
||||
</p>
|
||||
|
||||
<img src="https://rosettaeducation.com/storage/rosetta/files/1/rosetta-logo.png"
|
||||
alt="Rosetta Education International"
|
||||
style="max-width:200px;height:auto;margin-bottom:15px;">
|
||||
|
||||
<p style="margin:0;font-weight:bold;color:#115AAA;">
|
||||
Rosetta Education International
|
||||
</p>
|
||||
|
||||
<p style="margin:8px 0;color:#666;font-size:14px;">
|
||||
Your Trusted Study Abroad Partner
|
||||
</p>
|
||||
|
||||
<p style="margin:5px 0;color:#666;font-size:14px;">
|
||||
📧 info@rosettaeducation.com
|
||||
</p>
|
||||
|
||||
<p style="margin:5px 0;color:#666;font-size:14px;">
|
||||
📞 +977-1-4560413
|
||||
</p>
|
||||
|
||||
<p style="margin:5px 0;color:#666;font-size:14px;">
|
||||
🌐 https://rosettaeducation.com/
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<div class="footer">
|
||||
© 2026 Rosetta Education Consultancy<br>
|
||||
Your Trusted Study Abroad Partner
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -295,7 +295,7 @@
|
||||
}
|
||||
|
||||
p.shots.forEach((result, i) => {
|
||||
if (lastShots[i] === undefined) renderShot(i, result);
|
||||
if (lastShots[i] === undefined || lastShots[i] !== result) renderShot(i, result);
|
||||
});
|
||||
lastShots = [...p.shots];
|
||||
|
||||
|
||||
Reference in New Issue
Block a user