MiknasTrading/docs/superpowers/specs/2026-05-26-integrations-tabs-design.md
2026-05-26 17:17:05 +03:00

5.7 KiB

Integrations Page — Tabbed UI Design Spec

Date: 2026-05-26 Status: Approved


Overview

Redesign the Settings → Integrations page to use pill-style tabs — WhatsApp and Email — so both integrations are accessible from a single page. The Email tab exposes the Microsoft 365 Azure AD credentials (stored via the Setting model) and provides test connection + send test email actions via AJAX.


Goals

  • Add pill tabs (WhatsApp | Email) at the top of the integrations page
  • Keep the WhatsApp tab visually identical to the current design
  • Add a new Email tab for Microsoft 365 / Azure AD Mail configuration
  • Convert both tabs' save operations to AJAX (rule #11 — no page reloads on settings pages)
  • Use the existing design system: .card, .form-input, .form-label, .btn-primary, showToast()
  • All inline styles used for one-off values (Tailwind JIT rule)

UI Design

Tab bar

Pill-style tabs rendered above the card. Alpine.js x-data="{ tab: 'whatsapp' }" switches visibility.

  • Active tab: background:#1e293b; color:#fff; border-radius:999px; padding:7px 18px;
  • Inactive tab: background:#fff; color:#374151; border:1px solid #d1d5db; border-radius:999px; padding:7px 18px;

WhatsApp tab (unchanged fields, AJAX save)

Same fields as today: Enable toggle, Instance ID, API Token (masked), Webhook Secret (optional, masked), Webhook Path. Save button → AJAX POST. Test Connection and Send Test Message accordion remain unchanged.

Email tab (new)

Card header: envelope icon + "Microsoft 365 (Azure Mail)" title + subtitle.

Fields:

Field Input type Settings key Validation
Enable Email Notifications Toggle azure_mail_enabled boolean
Tenant ID text azure_mail_tenant_id required, max:100
Client ID text azure_mail_client_id required, max:100
Client Secret password (show/hide) azure_mail_client_secret required, max:500
From Address text azure_mail_from_address required, email, max:255

Actions:

  • Test Connection link → AJAX POST → calls TokenManager::getToken() to verify Azure AD responds
  • Save Settings button → AJAX POST → stores all 5 keys via Setting::set()

Send Test Email accordion (same expand/collapse pattern as WhatsApp):

  • To field (email address)
  • Subject field (pre-filled: "Test Email from SteelERP")
  • Send Email button → AJAX POST → Mail::mailer('azure')->raw(...)

Architecture

Files changed

File Change
resources/views/settings/integrations.blade.php Full rewrite — pill tabs, both tab panels, AJAX JS
app/Http/Controllers/SettingsController.php Add 3 new methods; update integrations() and updateWhatsapp()
routes/web.php Add 3 new POST routes for azure-mail

Controller changes

integrations(): View — pass both $whatsappSettings and $azureSettings to view.

updateWhatsapp(Request $request): JsonResponse — same validation, save logic unchanged, but return response()->json(['success' => true]) instead of redirect.

updateAzureMail(Request $request): JsonResponse

Validates: tenant_id (required, max:100), client_id (required, max:100),
           client_secret (required, max:500), from_address (required, email, max:255)
Saves: azure_mail_enabled, azure_mail_tenant_id, azure_mail_client_id,
       azure_mail_client_secret, azure_mail_from_address
Returns: JSON {success: true}

testAzureMailConnection(): JsonResponse

Reads azure_mail_* settings from DB
Instantiates TokenManager with those settings
Calls getToken() — success means Azure AD is reachable and credentials are valid
Returns: JSON {success: true} or {success: false, message: $e->getMessage()}

sendTestEmail(Request $request): JsonResponse

Validates: to (required, email, max:255), subject (required, max:255)
Uses Mail::mailer('azure')->raw('This is a test email from SteelERP.', fn($m) => $m->to($to)->subject($subject))
Returns: JSON {success: true} or {success: false, message: ...}

New routes

Route::post('settings/integrations/azure-mail', [SettingsController::class, 'updateAzureMail'])->name('settings.integrations.azure-mail');
Route::post('settings/integrations/test-azure-mail', [SettingsController::class, 'testAzureMailConnection'])->name('settings.integrations.test-azure-mail');
Route::post('settings/integrations/send-test-email', [SettingsController::class, 'sendTestEmail'])->name('settings.integrations.send-test-email');

AJAX Pattern

Both tabs use the global api() helper pattern from CLAUDE.md rule #11:

var CSRF = document.querySelector('meta[name="csrf-token"]').content;
function api(url, data) {
    return fetch(url, {
        method: 'POST',
        headers: { 'X-CSRF-TOKEN': CSRF, 'Accept': 'application/json', 'Content-Type': 'application/json' }
        body: JSON.stringify(data)
    }).then(function(r) {
        return r.json().then(function(body) {
            if (!r.ok) return Promise.reject(body);
            return body;
        });
    });
}

On success → showToast('Settings saved.', 'success') On error → showToast(err.message || 'Error', 'error')


Error Handling

  • Laravel validation failure (422) → Accept: application/json header ensures JSON error response → showToast() with first validation error message
  • Azure AD token fetch failure (AuthenticationException) → caught in testAzureMailConnection, message returned as JSON
  • Mail send failure → caught, message returned as JSON

Non-Goals

  • No URL changes — integrations page stays at /settings/integrations
  • No tab state persisted in URL
  • No changes to WhatsApp fields or business logic