feat: move send-test sections inline — WhatsApp right panel, Email side card with account selector

This commit is contained in:
Ghassan Yusuf 2026-05-26 15:05:24 +03:00
parent b3650489af
commit 303cdf6d6e
3 changed files with 241 additions and 153 deletions

View File

@ -5,6 +5,7 @@ namespace App\Http\Controllers;
use App\Models\MailAccount;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Mail;
use PromoSeven\AzureMailer\Graph\TokenManager;
class MailAccountController extends Controller
@ -64,6 +65,24 @@ class MailAccountController extends Controller
}
}
public function sendTestEmail(Request $request, MailAccount $mailAccount): JsonResponse
{
$request->validate(['to' => ['required', 'email', 'max:255']]);
try {
Mail::mailer($mailAccount->name)->raw(
'This is a test email from SteelERP. Your mail account "' . $mailAccount->label . '" is working correctly.',
function ($message) use ($request, $mailAccount) {
$message->to($request->to)
->from($mailAccount->from_address, $mailAccount->from_name ?: 'SteelERP')
->subject('Test Email from SteelERP');
}
);
return response()->json(['success' => true]);
} catch (\Exception $e) {
return response()->json(['success' => false, 'message' => $e->getMessage()]);
}
}
public function toggleEnabled(MailAccount $mailAccount): JsonResponse
{
$mailAccount->update(['enabled' => ! $mailAccount->enabled]);

View File

@ -8,7 +8,7 @@
<p class="page-subtitle">Configure third-party service integrations.</p>
</div>
<div style="max-width:680px;" x-data="{ tab: 'whatsapp' }">
<div style="max-width:900px;" x-data="{ tab: 'whatsapp' }">
{{-- Pill tabs --}}
<div style="display:flex;gap:8px;margin-bottom:20px;">
@ -35,7 +35,11 @@
<h3 style="font-size:16px;font-weight:600;color:#111827;margin:0;">WhatsApp (UltraMSG)</h3>
</div>
<div style="padding:24px;">
{{-- Two-column body --}}
<div style="display:flex;gap:0;align-items:stretch;">
{{-- Left: Settings form --}}
<div style="flex:1;padding:24px;border-right:1px solid #e5e7eb;">
{{-- Enable toggle --}}
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:20px;">
@ -146,44 +150,37 @@
</div>
</div>
{{-- Right: Send Test Message --}}
<div style="width:300px;flex-shrink:0;padding:24px;background:#f9fafb;">
<div style="display:flex;align-items:center;gap:8px;margin-bottom:16px;">
<svg style="width:16px;height:16px;color:#22c55e;flex-shrink:0;" fill="currentColor" viewBox="0 0 24 24">
<path d="M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 012.893 6.994c-.003 5.45-4.437 9.884-9.885 9.884m8.413-18.297A11.815 11.815 0 0012.05 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.882 11.882 0 005.683 1.448h.005c6.554 0 11.89-5.335 11.893-11.893a11.821 11.821 0 00-3.48-8.413z"/>
</svg>
<span style="font-size:13px;font-weight:600;color:#111827;">Send Test Message</span>
</div>
<div style="font-size:12px;color:#6b7280;margin-bottom:16px;">Verify the connection works end-to-end by sending a real message.</div>
<div style="margin-bottom:12px;">
<label style="display:block;font-size:12px;font-weight:600;color:#374151;margin-bottom:5px;">Phone Number</label>
<input type="text" id="wa-test-to" placeholder="+97333165444" class="form-input">
</div>
<div style="margin-bottom:14px;">
<label style="display:block;font-size:12px;font-weight:600;color:#374151;margin-bottom:5px;">Message</label>
<textarea id="wa-test-body" rows="4" class="form-input" style="resize:vertical;">Test message from SteelERP WhatsApp integration is working!</textarea>
</div>
<div style="display:flex;align-items:center;gap:10px;">
<button type="button" id="btn-wa-send" onclick="sendWaTestMessage()"
style="display:inline-flex;align-items:center;gap:7px;padding:9px 16px;font-size:13px;font-weight:600;color:#fff;background:#22c55e;border:none;border-radius:8px;cursor:pointer;"
onmouseover="this.style.background='#16a34a'" onmouseout="this.style.background='#22c55e'">
<svg style="width:14px;height:14px;" fill="currentColor" viewBox="0 0 24 24">
<path d="M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 012.893 6.994c-.003 5.45-4.437 9.884-9.885 9.884m8.413-18.297A11.815 11.815 0 0012.05 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.882 11.882 0 005.683 1.448h.005c6.554 0 11.89-5.335 11.893-11.893a11.821 11.821 0 00-3.48-8.413z"/>
</svg>
Send
</button>
<span id="wa-send-status" style="font-size:12px;display:none;"></span>
</div>
</div>
{{-- Send Test Message accordion --}}
<div class="card" style="margin-top:16px;">
<button type="button" onclick="toggleWaMsg()"
style="width:100%;display:flex;align-items:center;justify-content:space-between;padding:16px 24px;background:none;border:none;cursor:pointer;text-align:left;">
<div style="display:flex;align-items:center;gap:10px;">
<svg style="width:18px;height:18px;color:#22c55e;flex-shrink:0;" fill="currentColor" viewBox="0 0 24 24">
<path d="M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 012.893 6.994c-.003 5.45-4.437 9.884-9.885 9.884m8.413-18.297A11.815 11.815 0 0012.05 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.882 11.882 0 005.683 1.448h.005c6.554 0 11.89-5.335 11.893-11.893a11.821 11.821 0 00-3.48-8.413z"/>
</svg>
<span style="font-size:14px;font-weight:600;color:#111827;">Send Test Message</span>
<span style="font-size:12px;color:#6b7280;"> verify the connection works end-to-end</span>
</div>
<svg id="wa-msg-chevron" style="width:16px;height:16px;color:#9ca3af;transition:transform .2s;" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/>
</svg>
</button>
<div id="wa-msg-body" style="display:none;padding:0 24px 24px;">
<hr style="border:none;border-top:1px solid #e5e7eb;margin:0 0 20px;">
<div style="margin-bottom:14px;">
<label style="display:block;font-size:12px;font-weight:600;color:#374151;margin-bottom:5px;">Phone Number</label>
<input type="text" id="wa-test-to" placeholder="+97333165444" class="form-input" style="width:100%;">
</div>
<div style="margin-bottom:16px;">
<label style="display:block;font-size:12px;font-weight:600;color:#374151;margin-bottom:5px;">Message</label>
<textarea id="wa-test-body" rows="3" class="form-input" style="width:100%;resize:vertical;">Test message from SteelERP WhatsApp integration is working!</textarea>
</div>
<div style="display:flex;align-items:center;gap:14px;">
<button type="button" id="btn-wa-send" onclick="sendWaTestMessage()"
style="display:inline-flex;align-items:center;gap:7px;padding:9px 18px;font-size:13px;font-weight:600;color:#fff;background:#22c55e;border:none;border-radius:8px;cursor:pointer;"
onmouseover="this.style.background='#16a34a'" onmouseout="this.style.background='#22c55e'">
<svg style="width:15px;height:15px;" fill="currentColor" viewBox="0 0 24 24">
<path d="M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 012.893 6.994c-.003 5.45-4.437 9.884-9.885 9.884m8.413-18.297A11.815 11.815 0 0012.05 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.882 11.882 0 005.683 1.448h.005c6.554 0 11.89-5.335 11.893-11.893a11.821 11.821 0 00-3.48-8.413z"/>
</svg>
Send Message
</button>
<span id="wa-send-status" style="font-size:13px;display:none;"></span>
</div>
</div>
</div>
@ -192,6 +189,12 @@
{{-- ===== Email tab ===== --}}
<div x-show="tab==='email'" style="display:none;" id="email-tab-panel">
{{-- Two-column layout: account list left, send test right --}}
<div style="display:flex;gap:16px;align-items:flex-start;">
{{-- Left: account list --}}
<div style="flex:1;min-width:0;">
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:14px;">
<div>
<div style="font-size:15px;font-weight:600;color:#111827;">Mail Accounts</div>
@ -210,6 +213,39 @@
<div style="font-size:13px;color:#6b7280;">Click <strong>Add Account</strong> to get started.</div>
</div>
</div>
{{-- Right: Send Test Email --}}
<div style="width:280px;flex-shrink:0;background:#fff;border:1px solid #e5e7eb;border-radius:12px;overflow:hidden;">
<div style="padding:16px 20px;border-bottom:1px solid #e5e7eb;display:flex;align-items:center;gap:8px;">
<span style="font-size:15px;">📧</span>
<span style="font-size:13px;font-weight:600;color:#111827;">Send Test Email</span>
</div>
<div style="padding:16px 20px;">
<div style="font-size:12px;color:#6b7280;margin-bottom:14px;">Send a test email via any configured account.</div>
<div style="margin-bottom:12px;">
<label style="display:block;font-size:12px;font-weight:600;color:#374151;margin-bottom:5px;">Account</label>
<select id="em-test-account" class="form-input">
<option value=""> select account </option>
</select>
</div>
<div style="margin-bottom:14px;">
<label style="display:block;font-size:12px;font-weight:600;color:#374151;margin-bottom:5px;">To</label>
<input type="text" id="em-test-to" placeholder="recipient@example.com" class="form-input">
</div>
<div style="display:flex;align-items:center;gap:10px;">
<button type="button" id="btn-em-send" onclick="sendTestEmail()"
style="display:inline-flex;align-items:center;gap:6px;padding:9px 16px;font-size:13px;font-weight:600;color:#fff;background:#2563eb;border:none;border-radius:8px;cursor:pointer;"
onmouseover="this.style.background='#1d4ed8'" onmouseout="this.style.background='#2563eb'">
✉️ Send
</button>
<span id="em-send-status" style="font-size:12px;display:none;"></span>
</div>
</div>
</div>
</div>
</div>{{-- end Email tab --}}
{{-- Mail Accounts Modal --}}
@ -378,13 +414,6 @@ function toggleWaSwitch() {
thumb.style.left = on ? '2px' : '22px';
}
var _waMsgOpen = false;
function toggleWaMsg() {
_waMsgOpen = !_waMsgOpen;
document.getElementById('wa-msg-body').style.display = _waMsgOpen ? 'block' : 'none';
document.getElementById('wa-msg-chevron').style.transform = _waMsgOpen ? 'rotate(180deg)' : '';
}
function saveWhatsapp() {
var btn = document.getElementById('btn-wa-save');
btn.disabled = true; btn.style.opacity = '.6';
@ -447,9 +476,22 @@ function loadMailAccounts() {
.then(function(data) {
_maAccounts = data.accounts;
renderMailAccounts();
refreshEmailAccountSelect();
});
}
function refreshEmailAccountSelect() {
var sel = document.getElementById('em-test-account');
if (!sel) return;
var cur = sel.value;
sel.innerHTML = '<option value="">— select account —</option>' +
_maAccounts.map(function(a) {
return '<option value="' + a.id + '"' + (String(a.id) === cur ? ' selected' : '') + '>' +
escHtml(a.label) + ' (' + escHtml(a.name) + ')' +
'</option>';
}).join('');
}
function renderMailAccounts() {
var list = document.getElementById('ma-list');
var empty = document.getElementById('ma-empty');
@ -640,5 +682,31 @@ function maTest() {
}
}).catch(function() { statusEl.textContent = 'Request failed.'; statusEl.style.color = '#dc2626'; });
}
function sendTestEmail() {
var accountId = document.getElementById('em-test-account').value;
var to = document.getElementById('em-test-to').value.trim();
if (!accountId) { showToast('Select a mail account.', 'warn'); return; }
if (!to) { showToast('Enter a recipient email.', 'warn'); return; }
var btn = document.getElementById('btn-em-send');
var statusEl = document.getElementById('em-send-status');
btn.disabled = true; btn.style.opacity = '.6';
statusEl.textContent = 'Sending…'; statusEl.style.display = ''; statusEl.style.color = '#6b7280';
fetch(MA_BASE + '/' + accountId + '/send-test', {
method: 'POST',
headers: { 'X-CSRF-TOKEN': CSRF, 'Accept': 'application/json', 'Content-Type': 'application/json' },
body: JSON.stringify({ to: to })
}).then(function(r) { return r.json(); }).then(function(data) {
if (data.success) {
statusEl.textContent = 'Sent ✓'; statusEl.style.color = '#16a34a';
showToast('Test email sent!', 'success');
} else {
statusEl.textContent = 'Failed.'; statusEl.style.color = '#dc2626';
showToast(data.message || 'Failed to send.', 'error');
}
}).catch(function() {
statusEl.textContent = 'Request failed.'; statusEl.style.color = '#dc2626';
}).finally(function() { btn.disabled = false; btn.style.opacity = '1'; });
}
</script>
@endsection

View File

@ -130,6 +130,7 @@ Route::middleware(['auth', 'verified'])->group(function () {
Route::put('settings/integrations/mail-accounts/{mailAccount}', [MailAccountController::class, 'update'])->name('settings.mail-accounts.update');
Route::delete('settings/integrations/mail-accounts/{mailAccount}', [MailAccountController::class, 'destroy'])->name('settings.mail-accounts.destroy');
Route::post('settings/integrations/mail-accounts/{mailAccount}/test', [MailAccountController::class, 'testConnection'])->name('settings.mail-accounts.test');
Route::post('settings/integrations/mail-accounts/{mailAccount}/send-test', [MailAccountController::class, 'sendTestEmail'])->name('settings.mail-accounts.send-test');
Route::patch('settings/integrations/mail-accounts/{mailAccount}/toggle', [MailAccountController::class, 'toggleEnabled'])->name('settings.mail-accounts.toggle');
// Projects settings