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

150 lines
5.7 KiB
Markdown

# 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
```php
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:
```javascript
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