1636 lines
71 KiB
HTML
1636 lines
71 KiB
HTML
<!DOCTYPE html>
|
|
<!--
|
|
================================================================================
|
|
TEMPL MIGRATION GUIDE: settings.html → views/settings.templ
|
|
================================================================================
|
|
|
|
PAGE OVERVIEW:
|
|
- Multi-tab settings page with 7 sections:
|
|
1. Profile: Avatar, display name, username, bio, social links, visibility
|
|
2. Devices: Linked passkeys and security keys management
|
|
3. OAuth: Default scopes, session duration, consent prompts, authorized clients
|
|
4. Notifications: Security alerts, transaction, app, marketing preferences
|
|
5. Emails: Email address management with verification status
|
|
6. Phones: Phone number management with SMS 2FA settings
|
|
7. Developer: API keys, webhooks, OAuth application config
|
|
|
|
MAIN TEMPL COMPONENT:
|
|
templ SettingsPage(data SettingsData, activeTab string) {
|
|
@layouts.DashboardLayout("settings") {
|
|
@PageHeader("Settings", "Manage your account preferences and security")
|
|
@SettingsTabGroup(activeTab) {
|
|
@ProfileTab(data.Profile)
|
|
@DevicesTab(data.Devices)
|
|
@OAuthTab(data.OAuth)
|
|
@NotificationsTab(data.Notifications)
|
|
@EmailsTab(data.Emails)
|
|
@PhonesTab(data.Phones)
|
|
@DeveloperTab(data.Developer)
|
|
}
|
|
}
|
|
@AddEmailDialog()
|
|
@AddPhoneDialog()
|
|
@AddDeviceDialog()
|
|
@GenerateKeyDialog()
|
|
@AddWebhookDialog()
|
|
}
|
|
|
|
HTMX INTEGRATION:
|
|
- Tab switching: hx-get="/settings?tab=devices" hx-target="#tab-content" hx-push-url="true"
|
|
- Profile save: hx-post="/api/settings/profile" hx-include="[name^='profile-']"
|
|
- Avatar upload: hx-post="/api/settings/avatar" hx-encoding="multipart/form-data"
|
|
- Add device: hx-post="/api/auth/register-device" (triggers WebAuthn)
|
|
- Remove device: hx-delete="/api/devices/{id}" hx-target="closest .device-item" hx-swap="delete"
|
|
- Add email: hx-post="/api/settings/emails" hx-target="#emails-list"
|
|
- Verify email: hx-post="/api/settings/emails/{id}/resend"
|
|
- Toggle notification: hx-post="/api/settings/notifications" hx-vals='{"key":"...", "value": true}'
|
|
- Generate API key: hx-post="/api/settings/api-keys" hx-target="#api-keys-list"
|
|
- Add webhook: hx-post="/api/settings/webhooks" hx-target="#webhooks-list"
|
|
|
|
SUB-COMPONENTS TO EXTRACT:
|
|
- SettingsTabGroup(activeTab string, children templ.Component)
|
|
- ProfileTab(profile ProfileSettings)
|
|
- AvatarUpload(avatarURL string, initials string)
|
|
- SocialLinksInput(links SocialLinks)
|
|
- VisibilityRadioGroup(visibility string)
|
|
- DevicesTab(devices []Device)
|
|
- DeviceItem(device Device, isCurrent bool)
|
|
- OAuthTab(oauth OAuthSettings)
|
|
- ScopeCheckboxList(scopes []OAuthScope)
|
|
- AuthorizedClientCard(client AuthorizedClient)
|
|
- NotificationsTab(prefs NotificationPrefs)
|
|
- NotificationSection(title string, items []NotificationItem)
|
|
- NotificationRow(item NotificationItem)
|
|
- EmailsTab(emails []Email)
|
|
- EmailItem(email Email)
|
|
- PhonesTab(phones []Phone, smsSettings SMSSettings)
|
|
- PhoneItem(phone Phone)
|
|
- DeveloperTab(dev DeveloperSettings)
|
|
- APIKeyCard(key APIKey)
|
|
- WebhookCard(webhook Webhook)
|
|
- OAuthAppConfig(config OAuthAppConfig)
|
|
- AddEmailDialog()
|
|
- AddPhoneDialog()
|
|
- AddDeviceDialog()
|
|
- GenerateKeyDialog()
|
|
- AddWebhookDialog()
|
|
|
|
STATE/PROPS:
|
|
type SettingsData struct {
|
|
Profile ProfileSettings
|
|
Devices []Device
|
|
OAuth OAuthSettings
|
|
Notifications NotificationPrefs
|
|
Emails []Email
|
|
Phones []Phone
|
|
Developer DeveloperSettings
|
|
}
|
|
|
|
type ProfileSettings struct {
|
|
AvatarURL string
|
|
Initials string
|
|
DisplayName string
|
|
Username string
|
|
Bio string
|
|
Website string
|
|
SocialLinks SocialLinks
|
|
Visibility string // "public", "connections", "private"
|
|
}
|
|
|
|
type Device struct {
|
|
ID string
|
|
Name string
|
|
Type string // "laptop", "mobile", "key", "desktop"
|
|
Browser string
|
|
OS string
|
|
AuthType string // "Touch ID", "Face ID", "FIDO2", "Windows Hello"
|
|
AddedAt time.Time
|
|
LastUsed time.Time
|
|
IsCurrent bool
|
|
}
|
|
|
|
type OAuthSettings struct {
|
|
DefaultScopes []OAuthScope
|
|
SessionDuration string
|
|
ConsentPrompt string // "always", "first", "scope"
|
|
Clients []AuthorizedClient
|
|
}
|
|
|
|
type NotificationPrefs struct {
|
|
SecurityAlerts []NotificationItem
|
|
Transactions []NotificationItem
|
|
Apps []NotificationItem
|
|
Marketing []NotificationItem
|
|
}
|
|
|
|
type Email struct {
|
|
ID string
|
|
Address string
|
|
IsPrimary bool
|
|
IsVerified bool
|
|
AddedAt time.Time
|
|
}
|
|
|
|
type Phone struct {
|
|
ID string
|
|
Number string
|
|
IsPrimary bool
|
|
IsVerified bool
|
|
AddedAt time.Time
|
|
Use2FA bool
|
|
}
|
|
|
|
type DeveloperSettings struct {
|
|
APIKeys []APIKey
|
|
Webhooks []Webhook
|
|
OAuthApp OAuthAppConfig
|
|
DebugMode bool
|
|
TestMode bool
|
|
ShowRaw bool
|
|
}
|
|
|
|
HTMX PATTERNS:
|
|
// Tab switching with URL push
|
|
<wa-tab-group hx-on:wa-tab-show="
|
|
htmx.ajax('GET', '/settings?tab=' + event.detail.name, {target: '#tab-content', pushUrl: true})
|
|
">
|
|
|
|
// Profile form submission
|
|
<form hx-post="/api/settings/profile"
|
|
hx-target="#profile-status"
|
|
hx-indicator="#save-spinner">
|
|
|
|
// Device removal with confirmation
|
|
<wa-dropdown-item hx-delete="/api/devices/{device.ID}"
|
|
hx-target="closest .device-item"
|
|
hx-swap="delete"
|
|
hx-confirm="Remove this device?">
|
|
|
|
// Notification toggle
|
|
<wa-switch hx-post="/api/settings/notifications"
|
|
hx-vals='{"key": "security.new_device", "value": !this.checked}'
|
|
hx-swap="none">
|
|
|
|
// Email verification resend
|
|
<wa-dropdown-item hx-post="/api/settings/emails/{email.ID}/resend"
|
|
hx-swap="none"
|
|
hx-on::after-request="showToast('Verification sent')">
|
|
|
|
// API key generation
|
|
<form hx-post="/api/settings/api-keys"
|
|
hx-target="#new-key-display"
|
|
hx-swap="innerHTML">
|
|
|
|
// Webhook test
|
|
<wa-dropdown-item hx-post="/api/webhooks/{webhook.ID}/test"
|
|
hx-swap="none">
|
|
================================================================================
|
|
-->
|
|
<html lang="en" class="wa-cloak">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title>Settings - Sonr Motr Wallet</title>
|
|
<script src="https://cdn.sonr.org/wa/autoloader.js"></script>
|
|
<style>
|
|
:root {
|
|
--wa-color-primary: #17c2ff;
|
|
}
|
|
|
|
html, body {
|
|
min-height: 100%;
|
|
padding: 0;
|
|
margin: 0;
|
|
}
|
|
|
|
.dashboard-layout {
|
|
display: grid;
|
|
grid-template-columns: 240px 1fr;
|
|
min-height: 100vh;
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.dashboard-layout {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
.sidebar {
|
|
display: none;
|
|
}
|
|
}
|
|
|
|
.sidebar {
|
|
border-right: 1px solid var(--wa-color-neutral-200);
|
|
padding: var(--wa-space-m);
|
|
background: var(--wa-color-surface);
|
|
}
|
|
|
|
.sidebar-header {
|
|
padding: var(--wa-space-s) var(--wa-space-xs);
|
|
margin-bottom: var(--wa-space-m);
|
|
}
|
|
|
|
.sidebar-nav {
|
|
list-style: none;
|
|
padding: 0;
|
|
margin: 0;
|
|
}
|
|
|
|
.sidebar-nav li {
|
|
margin-bottom: var(--wa-space-2xs);
|
|
}
|
|
|
|
.sidebar-nav a {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--wa-space-s);
|
|
padding: var(--wa-space-s) var(--wa-space-m);
|
|
border-radius: var(--wa-radius-m);
|
|
text-decoration: none;
|
|
color: var(--wa-color-neutral-700);
|
|
font-size: var(--wa-font-size-s);
|
|
transition: background 0.15s;
|
|
}
|
|
|
|
.sidebar-nav a:hover {
|
|
background: var(--wa-color-surface-alt);
|
|
}
|
|
|
|
.sidebar-nav a.active {
|
|
background: var(--wa-color-primary-subtle);
|
|
color: var(--wa-color-primary);
|
|
font-weight: 500;
|
|
}
|
|
|
|
.main-content {
|
|
padding: var(--wa-space-xl);
|
|
background: var(--wa-color-surface-alt);
|
|
overflow-y: auto;
|
|
}
|
|
|
|
.page-header {
|
|
margin-bottom: var(--wa-space-xl);
|
|
}
|
|
|
|
.settings-container {
|
|
background: var(--wa-color-surface);
|
|
border-radius: var(--wa-radius-l);
|
|
overflow: hidden;
|
|
}
|
|
|
|
.settings-tabs {
|
|
--indicator-color: var(--wa-color-primary);
|
|
}
|
|
|
|
.settings-tabs wa-tab-panel {
|
|
padding: var(--wa-space-xl);
|
|
}
|
|
|
|
.settings-tabs wa-tab-group::part(base) {
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
.settings-tabs wa-tab-group::part(nav) {
|
|
background: var(--wa-color-surface);
|
|
border-bottom: 1px solid var(--wa-color-neutral-200);
|
|
padding: 0 var(--wa-space-m);
|
|
}
|
|
|
|
.form-section {
|
|
padding: var(--wa-space-l) 0;
|
|
border-bottom: 1px solid var(--wa-color-neutral-100);
|
|
}
|
|
|
|
.form-section:first-child {
|
|
padding-top: 0;
|
|
}
|
|
|
|
.form-section:last-child {
|
|
border-bottom: none;
|
|
padding-bottom: 0;
|
|
}
|
|
|
|
.form-row {
|
|
display: grid;
|
|
grid-template-columns: 200px 1fr;
|
|
gap: var(--wa-space-xl);
|
|
align-items: start;
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.form-row {
|
|
grid-template-columns: 1fr;
|
|
gap: var(--wa-space-s);
|
|
}
|
|
}
|
|
|
|
.form-label {
|
|
padding-top: var(--wa-space-xs);
|
|
}
|
|
|
|
.avatar-upload {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--wa-space-l);
|
|
}
|
|
|
|
.avatar-upload wa-avatar {
|
|
--size: 80px;
|
|
}
|
|
|
|
.device-item {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--wa-space-m);
|
|
padding: var(--wa-space-m);
|
|
background: var(--wa-color-surface-alt);
|
|
border-radius: var(--wa-radius-m);
|
|
}
|
|
|
|
.device-icon {
|
|
width: 48px;
|
|
height: 48px;
|
|
border-radius: var(--wa-radius-m);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
background: var(--wa-color-surface);
|
|
border: 1px solid var(--wa-color-neutral-200);
|
|
}
|
|
|
|
.device-info {
|
|
flex: 1;
|
|
}
|
|
|
|
.contact-item {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--wa-space-m);
|
|
padding: var(--wa-space-m);
|
|
background: var(--wa-color-surface-alt);
|
|
border-radius: var(--wa-radius-m);
|
|
}
|
|
|
|
.api-key-display {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--wa-space-s);
|
|
padding: var(--wa-space-m);
|
|
background: var(--wa-color-neutral-900);
|
|
color: var(--wa-color-neutral-100);
|
|
border-radius: var(--wa-radius-m);
|
|
font-family: var(--wa-font-mono);
|
|
font-size: var(--wa-font-size-s);
|
|
}
|
|
|
|
.api-key-display span {
|
|
flex: 1;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
|
|
.webhook-item {
|
|
padding: var(--wa-space-m);
|
|
background: var(--wa-color-surface-alt);
|
|
border-radius: var(--wa-radius-m);
|
|
}
|
|
|
|
.notification-row {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
padding: var(--wa-space-m) 0;
|
|
border-bottom: 1px solid var(--wa-color-neutral-100);
|
|
}
|
|
|
|
.notification-row:last-child {
|
|
border-bottom: none;
|
|
}
|
|
|
|
.oauth-client-card {
|
|
padding: var(--wa-space-m);
|
|
background: var(--wa-color-surface-alt);
|
|
border-radius: var(--wa-radius-m);
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<wa-page>
|
|
<div class="dashboard-layout">
|
|
<aside class="sidebar">
|
|
<div class="sidebar-header">
|
|
<div class="wa-cluster wa-gap-s">
|
|
<wa-avatar initials="S" style="--size: 32px; background: var(--wa-color-primary);"></wa-avatar>
|
|
<div class="wa-stack wa-gap-0">
|
|
<span class="wa-heading-s">Sonr Wallet</span>
|
|
<span class="wa-caption-xs" style="color: var(--wa-color-neutral-500);">sonr1x9f...7k2m</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<nav>
|
|
<ul class="sidebar-nav">
|
|
<li>
|
|
<a href="accounts.html">
|
|
<wa-icon name="wallet"></wa-icon>
|
|
Accounts
|
|
</a>
|
|
</li>
|
|
<li>
|
|
<a href="transactions.html">
|
|
<wa-icon name="arrow-right-arrow-left"></wa-icon>
|
|
Transactions
|
|
</a>
|
|
</li>
|
|
<li>
|
|
<a href="tokens.html">
|
|
<wa-icon name="coins"></wa-icon>
|
|
Tokens
|
|
</a>
|
|
</li>
|
|
<li>
|
|
<a href="nfts.html">
|
|
<wa-icon name="image"></wa-icon>
|
|
NFTs
|
|
</a>
|
|
</li>
|
|
<li>
|
|
<a href="activity.html">
|
|
<wa-icon name="chart-line"></wa-icon>
|
|
Activity
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
</nav>
|
|
|
|
<wa-divider style="margin: var(--wa-space-l) 0;"></wa-divider>
|
|
|
|
<ul class="sidebar-nav">
|
|
<li>
|
|
<a href="connections.html">
|
|
<wa-icon name="plug"></wa-icon>
|
|
Connections
|
|
</a>
|
|
</li>
|
|
<li>
|
|
<a href="device.html">
|
|
<wa-icon name="mobile"></wa-icon>
|
|
Devices
|
|
</a>
|
|
</li>
|
|
<li>
|
|
<a href="service.html">
|
|
<wa-icon name="server"></wa-icon>
|
|
Services
|
|
</a>
|
|
</li>
|
|
<li>
|
|
<a href="settings.html" class="active">
|
|
<wa-icon name="gear"></wa-icon>
|
|
Settings
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
</aside>
|
|
|
|
<main class="main-content">
|
|
<header class="page-header">
|
|
<div class="wa-stack wa-gap-2xs">
|
|
<span class="wa-heading-xl">Settings</span>
|
|
<span class="wa-caption-s" style="color: var(--wa-color-neutral-500);">Manage your account preferences and security</span>
|
|
</div>
|
|
</header>
|
|
|
|
<div class="settings-container">
|
|
<wa-tab-group class="settings-tabs">
|
|
<wa-tab panel="profile">
|
|
<wa-icon name="user"></wa-icon>
|
|
Profile
|
|
</wa-tab>
|
|
<wa-tab panel="devices">
|
|
<wa-icon name="laptop"></wa-icon>
|
|
Devices
|
|
</wa-tab>
|
|
<wa-tab panel="oauth">
|
|
<wa-icon name="key"></wa-icon>
|
|
OAuth
|
|
</wa-tab>
|
|
<wa-tab panel="notifications">
|
|
<wa-icon name="bell"></wa-icon>
|
|
Notifications
|
|
</wa-tab>
|
|
<wa-tab panel="emails">
|
|
<wa-icon name="envelope"></wa-icon>
|
|
Emails
|
|
</wa-tab>
|
|
<wa-tab panel="phones">
|
|
<wa-icon name="phone"></wa-icon>
|
|
Phone Numbers
|
|
</wa-tab>
|
|
<wa-tab panel="developer">
|
|
<wa-icon name="code"></wa-icon>
|
|
Developer
|
|
</wa-tab>
|
|
|
|
<wa-tab-panel name="profile">
|
|
<div class="wa-stack wa-gap-xl">
|
|
<div class="form-section">
|
|
<div class="form-row">
|
|
<div class="form-label">
|
|
<span class="wa-heading-s">Avatar</span>
|
|
<p class="wa-caption-s" style="color: var(--wa-color-neutral-500); margin: var(--wa-space-2xs) 0 0;">Your public profile image</p>
|
|
</div>
|
|
<div class="avatar-upload">
|
|
<wa-avatar initials="JD" style="background: linear-gradient(135deg, #17c2ff, #0090ff);"></wa-avatar>
|
|
<div class="wa-stack wa-gap-s">
|
|
<div class="wa-cluster wa-gap-s">
|
|
<wa-button size="small" variant="neutral" appearance="outlined">
|
|
<wa-icon slot="start" name="upload"></wa-icon>
|
|
Upload
|
|
</wa-button>
|
|
<wa-button size="small" variant="neutral" appearance="plain">Remove</wa-button>
|
|
</div>
|
|
<span class="wa-caption-xs" style="color: var(--wa-color-neutral-500);">JPG, PNG or GIF. Max 2MB.</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-section">
|
|
<div class="form-row">
|
|
<div class="form-label">
|
|
<span class="wa-heading-s">Display Name</span>
|
|
<p class="wa-caption-s" style="color: var(--wa-color-neutral-500); margin: var(--wa-space-2xs) 0 0;">Shown on your public profile</p>
|
|
</div>
|
|
<wa-input value="John Doe" placeholder="Enter display name"></wa-input>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-section">
|
|
<div class="form-row">
|
|
<div class="form-label">
|
|
<span class="wa-heading-s">Username</span>
|
|
<p class="wa-caption-s" style="color: var(--wa-color-neutral-500); margin: var(--wa-space-2xs) 0 0;">Your unique identifier</p>
|
|
</div>
|
|
<wa-input value="johndoe" placeholder="Enter username">
|
|
<span slot="prefix">@</span>
|
|
</wa-input>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-section">
|
|
<div class="form-row">
|
|
<div class="form-label">
|
|
<span class="wa-heading-s">Bio</span>
|
|
<p class="wa-caption-s" style="color: var(--wa-color-neutral-500); margin: var(--wa-space-2xs) 0 0;">Brief description for your profile</p>
|
|
</div>
|
|
<wa-textarea rows="3" placeholder="Tell us about yourself..." value="Web3 enthusiast and DeFi explorer. Building on Sonr."></wa-textarea>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-section">
|
|
<div class="form-row">
|
|
<div class="form-label">
|
|
<span class="wa-heading-s">Website</span>
|
|
<p class="wa-caption-s" style="color: var(--wa-color-neutral-500); margin: var(--wa-space-2xs) 0 0;">Your personal or project website</p>
|
|
</div>
|
|
<wa-input type="url" value="https://johndoe.dev" placeholder="https://example.com">
|
|
<wa-icon slot="prefix" name="globe"></wa-icon>
|
|
</wa-input>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-section">
|
|
<div class="form-row">
|
|
<div class="form-label">
|
|
<span class="wa-heading-s">Social Links</span>
|
|
<p class="wa-caption-s" style="color: var(--wa-color-neutral-500); margin: var(--wa-space-2xs) 0 0;">Connect your social accounts</p>
|
|
</div>
|
|
<div class="wa-stack wa-gap-m">
|
|
<wa-input placeholder="twitter.com/username">
|
|
<wa-icon slot="prefix" name="x-twitter" variant="brand"></wa-icon>
|
|
</wa-input>
|
|
<wa-input placeholder="github.com/username">
|
|
<wa-icon slot="prefix" name="github" variant="brand"></wa-icon>
|
|
</wa-input>
|
|
<wa-input placeholder="discord.gg/invite">
|
|
<wa-icon slot="prefix" name="discord" variant="brand"></wa-icon>
|
|
</wa-input>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-section">
|
|
<div class="form-row">
|
|
<div class="form-label">
|
|
<span class="wa-heading-s">Profile Visibility</span>
|
|
<p class="wa-caption-s" style="color: var(--wa-color-neutral-500); margin: var(--wa-space-2xs) 0 0;">Control who can see your profile</p>
|
|
</div>
|
|
<wa-radio-group value="public">
|
|
<wa-radio value="public">
|
|
<div class="wa-stack wa-gap-0">
|
|
<span>Public</span>
|
|
<span class="wa-caption-xs" style="color: var(--wa-color-neutral-500);">Anyone can view your profile</span>
|
|
</div>
|
|
</wa-radio>
|
|
<wa-radio value="connections">
|
|
<div class="wa-stack wa-gap-0">
|
|
<span>Connections Only</span>
|
|
<span class="wa-caption-xs" style="color: var(--wa-color-neutral-500);">Only connected apps and users</span>
|
|
</div>
|
|
</wa-radio>
|
|
<wa-radio value="private">
|
|
<div class="wa-stack wa-gap-0">
|
|
<span>Private</span>
|
|
<span class="wa-caption-xs" style="color: var(--wa-color-neutral-500);">Only you can see your profile</span>
|
|
</div>
|
|
</wa-radio>
|
|
</wa-radio-group>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="wa-cluster wa-gap-s" style="justify-content: flex-end;">
|
|
<wa-button variant="neutral" appearance="outlined">Cancel</wa-button>
|
|
<wa-button variant="brand">Save Changes</wa-button>
|
|
</div>
|
|
</div>
|
|
</wa-tab-panel>
|
|
|
|
<wa-tab-panel name="devices">
|
|
<div class="wa-stack wa-gap-xl">
|
|
<div class="wa-flank">
|
|
<div class="wa-stack wa-gap-2xs">
|
|
<span class="wa-heading-m">Linked Devices</span>
|
|
<span class="wa-caption-s" style="color: var(--wa-color-neutral-500);">Passkeys and security keys registered with your account</span>
|
|
</div>
|
|
<wa-button size="small" variant="brand">
|
|
<wa-icon slot="start" name="plus"></wa-icon>
|
|
Add Device
|
|
</wa-button>
|
|
</div>
|
|
|
|
<div class="wa-stack wa-gap-m">
|
|
<div class="device-item">
|
|
<div class="device-icon">
|
|
<wa-icon name="laptop" style="font-size: 24px;"></wa-icon>
|
|
</div>
|
|
<div class="device-info">
|
|
<div class="wa-stack wa-gap-0">
|
|
<div class="wa-cluster wa-gap-xs">
|
|
<span class="wa-heading-s">MacBook Pro</span>
|
|
<wa-badge variant="success" pill>Current</wa-badge>
|
|
</div>
|
|
<span class="wa-caption-s" style="color: var(--wa-color-neutral-500);">Chrome on macOS - Touch ID</span>
|
|
</div>
|
|
</div>
|
|
<div class="wa-stack wa-gap-0 wa-align-items-end">
|
|
<span class="wa-caption-s">Added Dec 15, 2025</span>
|
|
<span class="wa-caption-xs" style="color: var(--wa-color-neutral-500);">Last used: Just now</span>
|
|
</div>
|
|
<wa-dropdown>
|
|
<wa-icon-button slot="trigger" name="ellipsis-vertical" label="Options"></wa-icon-button>
|
|
<wa-dropdown-item>
|
|
<wa-icon slot="icon" name="pen"></wa-icon>
|
|
Rename
|
|
</wa-dropdown-item>
|
|
<wa-divider></wa-divider>
|
|
<wa-dropdown-item style="color: var(--wa-color-danger);">
|
|
<wa-icon slot="icon" name="trash"></wa-icon>
|
|
Remove
|
|
</wa-dropdown-item>
|
|
</wa-dropdown>
|
|
</div>
|
|
|
|
<div class="device-item">
|
|
<div class="device-icon">
|
|
<wa-icon name="mobile" style="font-size: 24px;"></wa-icon>
|
|
</div>
|
|
<div class="device-info">
|
|
<div class="wa-stack wa-gap-0">
|
|
<span class="wa-heading-s">iPhone 15 Pro</span>
|
|
<span class="wa-caption-s" style="color: var(--wa-color-neutral-500);">Safari on iOS - Face ID</span>
|
|
</div>
|
|
</div>
|
|
<div class="wa-stack wa-gap-0 wa-align-items-end">
|
|
<span class="wa-caption-s">Added Dec 10, 2025</span>
|
|
<span class="wa-caption-xs" style="color: var(--wa-color-neutral-500);">Last used: 2 hours ago</span>
|
|
</div>
|
|
<wa-dropdown>
|
|
<wa-icon-button slot="trigger" name="ellipsis-vertical" label="Options"></wa-icon-button>
|
|
<wa-dropdown-item>
|
|
<wa-icon slot="icon" name="pen"></wa-icon>
|
|
Rename
|
|
</wa-dropdown-item>
|
|
<wa-divider></wa-divider>
|
|
<wa-dropdown-item style="color: var(--wa-color-danger);">
|
|
<wa-icon slot="icon" name="trash"></wa-icon>
|
|
Remove
|
|
</wa-dropdown-item>
|
|
</wa-dropdown>
|
|
</div>
|
|
|
|
<div class="device-item">
|
|
<div class="device-icon">
|
|
<wa-icon name="key" style="font-size: 24px;"></wa-icon>
|
|
</div>
|
|
<div class="device-info">
|
|
<div class="wa-stack wa-gap-0">
|
|
<span class="wa-heading-s">YubiKey 5C</span>
|
|
<span class="wa-caption-s" style="color: var(--wa-color-neutral-500);">Security Key - FIDO2</span>
|
|
</div>
|
|
</div>
|
|
<div class="wa-stack wa-gap-0 wa-align-items-end">
|
|
<span class="wa-caption-s">Added Nov 28, 2025</span>
|
|
<span class="wa-caption-xs" style="color: var(--wa-color-neutral-500);">Last used: 5 days ago</span>
|
|
</div>
|
|
<wa-dropdown>
|
|
<wa-icon-button slot="trigger" name="ellipsis-vertical" label="Options"></wa-icon-button>
|
|
<wa-dropdown-item>
|
|
<wa-icon slot="icon" name="pen"></wa-icon>
|
|
Rename
|
|
</wa-dropdown-item>
|
|
<wa-divider></wa-divider>
|
|
<wa-dropdown-item style="color: var(--wa-color-danger);">
|
|
<wa-icon slot="icon" name="trash"></wa-icon>
|
|
Remove
|
|
</wa-dropdown-item>
|
|
</wa-dropdown>
|
|
</div>
|
|
|
|
<div class="device-item">
|
|
<div class="device-icon">
|
|
<wa-icon name="desktop" style="font-size: 24px;"></wa-icon>
|
|
</div>
|
|
<div class="device-info">
|
|
<div class="wa-stack wa-gap-0">
|
|
<span class="wa-heading-s">Windows Desktop</span>
|
|
<span class="wa-caption-s" style="color: var(--wa-color-neutral-500);">Firefox on Windows - Windows Hello</span>
|
|
</div>
|
|
</div>
|
|
<div class="wa-stack wa-gap-0 wa-align-items-end">
|
|
<span class="wa-caption-s">Added Oct 15, 2025</span>
|
|
<span class="wa-caption-xs" style="color: var(--wa-color-neutral-500);">Last used: 2 weeks ago</span>
|
|
</div>
|
|
<wa-dropdown>
|
|
<wa-icon-button slot="trigger" name="ellipsis-vertical" label="Options"></wa-icon-button>
|
|
<wa-dropdown-item>
|
|
<wa-icon slot="icon" name="pen"></wa-icon>
|
|
Rename
|
|
</wa-dropdown-item>
|
|
<wa-divider></wa-divider>
|
|
<wa-dropdown-item style="color: var(--wa-color-danger);">
|
|
<wa-icon slot="icon" name="trash"></wa-icon>
|
|
Remove
|
|
</wa-dropdown-item>
|
|
</wa-dropdown>
|
|
</div>
|
|
</div>
|
|
|
|
<wa-callout variant="neutral">
|
|
<wa-icon slot="icon" name="shield-check"></wa-icon>
|
|
<div class="wa-stack wa-gap-2xs">
|
|
<span class="wa-heading-xs">Security Recommendation</span>
|
|
<span class="wa-caption-s">Register at least 2 devices to ensure account recovery if one is lost.</span>
|
|
</div>
|
|
</wa-callout>
|
|
</div>
|
|
</wa-tab-panel>
|
|
|
|
<wa-tab-panel name="oauth">
|
|
<div class="wa-stack wa-gap-xl">
|
|
<div class="wa-flank">
|
|
<div class="wa-stack wa-gap-2xs">
|
|
<span class="wa-heading-m">OAuth Settings</span>
|
|
<span class="wa-caption-s" style="color: var(--wa-color-neutral-500);">Configure your identity provider settings</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-section">
|
|
<div class="form-row">
|
|
<div class="form-label">
|
|
<span class="wa-heading-s">Default Scopes</span>
|
|
<p class="wa-caption-s" style="color: var(--wa-color-neutral-500); margin: var(--wa-space-2xs) 0 0;">Permissions granted by default</p>
|
|
</div>
|
|
<div class="wa-stack wa-gap-s">
|
|
<wa-checkbox checked>
|
|
<div class="wa-stack wa-gap-0">
|
|
<span>openid</span>
|
|
<span class="wa-caption-xs" style="color: var(--wa-color-neutral-500);">Basic identity information (required)</span>
|
|
</div>
|
|
</wa-checkbox>
|
|
<wa-checkbox checked>
|
|
<div class="wa-stack wa-gap-0">
|
|
<span>profile</span>
|
|
<span class="wa-caption-xs" style="color: var(--wa-color-neutral-500);">Display name and avatar</span>
|
|
</div>
|
|
</wa-checkbox>
|
|
<wa-checkbox>
|
|
<div class="wa-stack wa-gap-0">
|
|
<span>email</span>
|
|
<span class="wa-caption-xs" style="color: var(--wa-color-neutral-500);">Email address access</span>
|
|
</div>
|
|
</wa-checkbox>
|
|
<wa-checkbox>
|
|
<div class="wa-stack wa-gap-0">
|
|
<span>wallet:read</span>
|
|
<span class="wa-caption-xs" style="color: var(--wa-color-neutral-500);">View wallet balances</span>
|
|
</div>
|
|
</wa-checkbox>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-section">
|
|
<div class="form-row">
|
|
<div class="form-label">
|
|
<span class="wa-heading-s">Session Duration</span>
|
|
<p class="wa-caption-s" style="color: var(--wa-color-neutral-500); margin: var(--wa-space-2xs) 0 0;">How long OAuth sessions remain valid</p>
|
|
</div>
|
|
<wa-select value="7d">
|
|
<wa-option value="1h">1 hour</wa-option>
|
|
<wa-option value="24h">24 hours</wa-option>
|
|
<wa-option value="7d">7 days</wa-option>
|
|
<wa-option value="30d">30 days</wa-option>
|
|
<wa-option value="never">Never expire</wa-option>
|
|
</wa-select>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-section">
|
|
<div class="form-row">
|
|
<div class="form-label">
|
|
<span class="wa-heading-s">Consent Prompt</span>
|
|
<p class="wa-caption-s" style="color: var(--wa-color-neutral-500); margin: var(--wa-space-2xs) 0 0;">When to show permission dialogs</p>
|
|
</div>
|
|
<wa-radio-group value="first">
|
|
<wa-radio value="always">Always prompt for consent</wa-radio>
|
|
<wa-radio value="first">Only on first connection</wa-radio>
|
|
<wa-radio value="scope">When new scopes requested</wa-radio>
|
|
</wa-radio-group>
|
|
</div>
|
|
</div>
|
|
|
|
<wa-divider></wa-divider>
|
|
|
|
<div class="wa-stack wa-gap-m">
|
|
<div class="wa-flank">
|
|
<span class="wa-heading-m">Authorized Clients</span>
|
|
<wa-button size="small" variant="neutral" appearance="outlined">View All</wa-button>
|
|
</div>
|
|
|
|
<div class="wa-stack wa-gap-s">
|
|
<div class="oauth-client-card">
|
|
<div class="wa-flank">
|
|
<div class="wa-cluster wa-gap-m">
|
|
<wa-avatar initials="U" style="--size: 40px; background: linear-gradient(135deg, #ff007a, #ff5ca0);"></wa-avatar>
|
|
<div class="wa-stack wa-gap-0">
|
|
<span class="wa-heading-s">Uniswap</span>
|
|
<span class="wa-caption-xs" style="color: var(--wa-color-neutral-500);">app.uniswap.org</span>
|
|
</div>
|
|
</div>
|
|
<div class="wa-cluster wa-gap-s">
|
|
<wa-badge variant="success" pill>Active</wa-badge>
|
|
<wa-button size="small" variant="danger" appearance="plain">Revoke</wa-button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="oauth-client-card">
|
|
<div class="wa-flank">
|
|
<div class="wa-cluster wa-gap-m">
|
|
<wa-avatar initials="O" style="--size: 40px; background: linear-gradient(135deg, #627eea, #4c63d2);"></wa-avatar>
|
|
<div class="wa-stack wa-gap-0">
|
|
<span class="wa-heading-s">OpenSea</span>
|
|
<span class="wa-caption-xs" style="color: var(--wa-color-neutral-500);">opensea.io</span>
|
|
</div>
|
|
</div>
|
|
<div class="wa-cluster wa-gap-s">
|
|
<wa-badge variant="neutral" pill>Idle</wa-badge>
|
|
<wa-button size="small" variant="danger" appearance="plain">Revoke</wa-button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="wa-cluster wa-gap-s" style="justify-content: flex-end;">
|
|
<wa-button variant="neutral" appearance="outlined">Cancel</wa-button>
|
|
<wa-button variant="brand">Save Changes</wa-button>
|
|
</div>
|
|
</div>
|
|
</wa-tab-panel>
|
|
|
|
<wa-tab-panel name="notifications">
|
|
<div class="wa-stack wa-gap-xl">
|
|
<div class="wa-stack wa-gap-2xs">
|
|
<span class="wa-heading-m">Notification Preferences</span>
|
|
<span class="wa-caption-s" style="color: var(--wa-color-neutral-500);">Choose how and when you receive notifications</span>
|
|
</div>
|
|
|
|
<wa-card>
|
|
<span slot="header" class="wa-heading-s">Security Alerts</span>
|
|
<div class="wa-stack">
|
|
<div class="notification-row">
|
|
<div class="wa-stack wa-gap-0">
|
|
<span class="wa-heading-xs">New device login</span>
|
|
<span class="wa-caption-s" style="color: var(--wa-color-neutral-500);">When your account is accessed from a new device</span>
|
|
</div>
|
|
<wa-switch checked></wa-switch>
|
|
</div>
|
|
<div class="notification-row">
|
|
<div class="wa-stack wa-gap-0">
|
|
<span class="wa-heading-xs">Suspicious activity</span>
|
|
<span class="wa-caption-s" style="color: var(--wa-color-neutral-500);">Unusual login attempts or access patterns</span>
|
|
</div>
|
|
<wa-switch checked></wa-switch>
|
|
</div>
|
|
<div class="notification-row">
|
|
<div class="wa-stack wa-gap-0">
|
|
<span class="wa-heading-xs">Password changes</span>
|
|
<span class="wa-caption-s" style="color: var(--wa-color-neutral-500);">When security credentials are modified</span>
|
|
</div>
|
|
<wa-switch checked></wa-switch>
|
|
</div>
|
|
</div>
|
|
</wa-card>
|
|
|
|
<wa-card>
|
|
<span slot="header" class="wa-heading-s">Transaction Notifications</span>
|
|
<div class="wa-stack">
|
|
<div class="notification-row">
|
|
<div class="wa-stack wa-gap-0">
|
|
<span class="wa-heading-xs">Incoming transfers</span>
|
|
<span class="wa-caption-s" style="color: var(--wa-color-neutral-500);">When you receive tokens or NFTs</span>
|
|
</div>
|
|
<wa-switch checked></wa-switch>
|
|
</div>
|
|
<div class="notification-row">
|
|
<div class="wa-stack wa-gap-0">
|
|
<span class="wa-heading-xs">Outgoing transfers</span>
|
|
<span class="wa-caption-s" style="color: var(--wa-color-neutral-500);">Confirmation of sent transactions</span>
|
|
</div>
|
|
<wa-switch checked></wa-switch>
|
|
</div>
|
|
<div class="notification-row">
|
|
<div class="wa-stack wa-gap-0">
|
|
<span class="wa-heading-xs">Failed transactions</span>
|
|
<span class="wa-caption-s" style="color: var(--wa-color-neutral-500);">When a transaction fails or is reverted</span>
|
|
</div>
|
|
<wa-switch checked></wa-switch>
|
|
</div>
|
|
<div class="notification-row">
|
|
<div class="wa-stack wa-gap-0">
|
|
<span class="wa-heading-xs">Large transactions</span>
|
|
<span class="wa-caption-s" style="color: var(--wa-color-neutral-500);">Transactions above threshold</span>
|
|
</div>
|
|
<div class="wa-cluster wa-gap-s">
|
|
<wa-input type="number" value="1000" size="small" style="width: 100px;">
|
|
<span slot="suffix">USD</span>
|
|
</wa-input>
|
|
<wa-switch checked></wa-switch>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</wa-card>
|
|
|
|
<wa-card>
|
|
<span slot="header" class="wa-heading-s">App Notifications</span>
|
|
<div class="wa-stack">
|
|
<div class="notification-row">
|
|
<div class="wa-stack wa-gap-0">
|
|
<span class="wa-heading-xs">New app connections</span>
|
|
<span class="wa-caption-s" style="color: var(--wa-color-neutral-500);">When a new app connects to your wallet</span>
|
|
</div>
|
|
<wa-switch checked></wa-switch>
|
|
</div>
|
|
<div class="notification-row">
|
|
<div class="wa-stack wa-gap-0">
|
|
<span class="wa-heading-xs">Signature requests</span>
|
|
<span class="wa-caption-s" style="color: var(--wa-color-neutral-500);">When an app requests a signature</span>
|
|
</div>
|
|
<wa-switch checked></wa-switch>
|
|
</div>
|
|
<div class="notification-row">
|
|
<div class="wa-stack wa-gap-0">
|
|
<span class="wa-heading-xs">Permission changes</span>
|
|
<span class="wa-caption-s" style="color: var(--wa-color-neutral-500);">When app permissions are modified</span>
|
|
</div>
|
|
<wa-switch></wa-switch>
|
|
</div>
|
|
</div>
|
|
</wa-card>
|
|
|
|
<wa-card>
|
|
<span slot="header" class="wa-heading-s">Marketing & Updates</span>
|
|
<div class="wa-stack">
|
|
<div class="notification-row">
|
|
<div class="wa-stack wa-gap-0">
|
|
<span class="wa-heading-xs">Product updates</span>
|
|
<span class="wa-caption-s" style="color: var(--wa-color-neutral-500);">New features and improvements</span>
|
|
</div>
|
|
<wa-switch></wa-switch>
|
|
</div>
|
|
<div class="notification-row">
|
|
<div class="wa-stack wa-gap-0">
|
|
<span class="wa-heading-xs">Newsletter</span>
|
|
<span class="wa-caption-s" style="color: var(--wa-color-neutral-500);">Weekly digest of Sonr ecosystem news</span>
|
|
</div>
|
|
<wa-switch></wa-switch>
|
|
</div>
|
|
</div>
|
|
</wa-card>
|
|
|
|
<div class="wa-cluster wa-gap-s" style="justify-content: flex-end;">
|
|
<wa-button variant="brand">Save Preferences</wa-button>
|
|
</div>
|
|
</div>
|
|
</wa-tab-panel>
|
|
|
|
<wa-tab-panel name="emails">
|
|
<div class="wa-stack wa-gap-xl">
|
|
<div class="wa-flank">
|
|
<div class="wa-stack wa-gap-2xs">
|
|
<span class="wa-heading-m">Email Addresses</span>
|
|
<span class="wa-caption-s" style="color: var(--wa-color-neutral-500);">Manage email addresses linked to your account</span>
|
|
</div>
|
|
<wa-button size="small" variant="brand" id="add-email-btn">
|
|
<wa-icon slot="start" name="plus"></wa-icon>
|
|
Add Email
|
|
</wa-button>
|
|
</div>
|
|
|
|
<div class="wa-stack wa-gap-m">
|
|
<div class="contact-item">
|
|
<wa-icon name="envelope" style="font-size: 20px; color: var(--wa-color-neutral-500);"></wa-icon>
|
|
<div class="wa-stack wa-gap-0" style="flex: 1;">
|
|
<div class="wa-cluster wa-gap-xs">
|
|
<span class="wa-heading-s">john.doe@example.com</span>
|
|
<wa-badge variant="success" pill>Primary</wa-badge>
|
|
<wa-badge variant="brand" pill>Verified</wa-badge>
|
|
</div>
|
|
<span class="wa-caption-s" style="color: var(--wa-color-neutral-500);">Added Dec 15, 2025</span>
|
|
</div>
|
|
<wa-dropdown>
|
|
<wa-icon-button slot="trigger" name="ellipsis-vertical" label="Options"></wa-icon-button>
|
|
<wa-dropdown-item disabled>
|
|
<wa-icon slot="icon" name="star"></wa-icon>
|
|
Set as Primary
|
|
</wa-dropdown-item>
|
|
<wa-dropdown-item>
|
|
<wa-icon slot="icon" name="paper-plane"></wa-icon>
|
|
Resend Verification
|
|
</wa-dropdown-item>
|
|
<wa-divider></wa-divider>
|
|
<wa-dropdown-item disabled style="color: var(--wa-color-danger);">
|
|
<wa-icon slot="icon" name="trash"></wa-icon>
|
|
Remove
|
|
</wa-dropdown-item>
|
|
</wa-dropdown>
|
|
</div>
|
|
|
|
<div class="contact-item">
|
|
<wa-icon name="envelope" style="font-size: 20px; color: var(--wa-color-neutral-500);"></wa-icon>
|
|
<div class="wa-stack wa-gap-0" style="flex: 1;">
|
|
<div class="wa-cluster wa-gap-xs">
|
|
<span class="wa-heading-s">johndoe.work@company.io</span>
|
|
<wa-badge variant="brand" pill>Verified</wa-badge>
|
|
</div>
|
|
<span class="wa-caption-s" style="color: var(--wa-color-neutral-500);">Added Nov 20, 2025</span>
|
|
</div>
|
|
<wa-dropdown>
|
|
<wa-icon-button slot="trigger" name="ellipsis-vertical" label="Options"></wa-icon-button>
|
|
<wa-dropdown-item>
|
|
<wa-icon slot="icon" name="star"></wa-icon>
|
|
Set as Primary
|
|
</wa-dropdown-item>
|
|
<wa-dropdown-item>
|
|
<wa-icon slot="icon" name="paper-plane"></wa-icon>
|
|
Resend Verification
|
|
</wa-dropdown-item>
|
|
<wa-divider></wa-divider>
|
|
<wa-dropdown-item style="color: var(--wa-color-danger);">
|
|
<wa-icon slot="icon" name="trash"></wa-icon>
|
|
Remove
|
|
</wa-dropdown-item>
|
|
</wa-dropdown>
|
|
</div>
|
|
|
|
<div class="contact-item">
|
|
<wa-icon name="envelope" style="font-size: 20px; color: var(--wa-color-warning);"></wa-icon>
|
|
<div class="wa-stack wa-gap-0" style="flex: 1;">
|
|
<div class="wa-cluster wa-gap-xs">
|
|
<span class="wa-heading-s">backup@gmail.com</span>
|
|
<wa-badge variant="warning" pill>Pending</wa-badge>
|
|
</div>
|
|
<span class="wa-caption-s" style="color: var(--wa-color-neutral-500);">Verification sent - check your inbox</span>
|
|
</div>
|
|
<wa-dropdown>
|
|
<wa-icon-button slot="trigger" name="ellipsis-vertical" label="Options"></wa-icon-button>
|
|
<wa-dropdown-item>
|
|
<wa-icon slot="icon" name="paper-plane"></wa-icon>
|
|
Resend Verification
|
|
</wa-dropdown-item>
|
|
<wa-divider></wa-divider>
|
|
<wa-dropdown-item style="color: var(--wa-color-danger);">
|
|
<wa-icon slot="icon" name="trash"></wa-icon>
|
|
Remove
|
|
</wa-dropdown-item>
|
|
</wa-dropdown>
|
|
</div>
|
|
</div>
|
|
|
|
<wa-callout variant="neutral">
|
|
<wa-icon slot="icon" name="circle-info"></wa-icon>
|
|
<span class="wa-caption-s">Your primary email is used for account recovery and important notifications. You must have at least one verified email.</span>
|
|
</wa-callout>
|
|
</div>
|
|
</wa-tab-panel>
|
|
|
|
<wa-tab-panel name="phones">
|
|
<div class="wa-stack wa-gap-xl">
|
|
<div class="wa-flank">
|
|
<div class="wa-stack wa-gap-2xs">
|
|
<span class="wa-heading-m">Phone Numbers</span>
|
|
<span class="wa-caption-s" style="color: var(--wa-color-neutral-500);">Manage phone numbers for 2FA and recovery</span>
|
|
</div>
|
|
<wa-button size="small" variant="brand" id="add-phone-btn">
|
|
<wa-icon slot="start" name="plus"></wa-icon>
|
|
Add Phone
|
|
</wa-button>
|
|
</div>
|
|
|
|
<div class="wa-stack wa-gap-m">
|
|
<div class="contact-item">
|
|
<wa-icon name="phone" style="font-size: 20px; color: var(--wa-color-neutral-500);"></wa-icon>
|
|
<div class="wa-stack wa-gap-0" style="flex: 1;">
|
|
<div class="wa-cluster wa-gap-xs">
|
|
<span class="wa-heading-s">+1 (555) 123-4567</span>
|
|
<wa-badge variant="success" pill>Primary</wa-badge>
|
|
<wa-badge variant="brand" pill>Verified</wa-badge>
|
|
</div>
|
|
<span class="wa-caption-s" style="color: var(--wa-color-neutral-500);">Added Dec 15, 2025 - Used for 2FA</span>
|
|
</div>
|
|
<wa-dropdown>
|
|
<wa-icon-button slot="trigger" name="ellipsis-vertical" label="Options"></wa-icon-button>
|
|
<wa-dropdown-item disabled>
|
|
<wa-icon slot="icon" name="star"></wa-icon>
|
|
Set as Primary
|
|
</wa-dropdown-item>
|
|
<wa-dropdown-item>
|
|
<wa-icon slot="icon" name="message"></wa-icon>
|
|
Send Test SMS
|
|
</wa-dropdown-item>
|
|
<wa-divider></wa-divider>
|
|
<wa-dropdown-item disabled style="color: var(--wa-color-danger);">
|
|
<wa-icon slot="icon" name="trash"></wa-icon>
|
|
Remove
|
|
</wa-dropdown-item>
|
|
</wa-dropdown>
|
|
</div>
|
|
|
|
<div class="contact-item">
|
|
<wa-icon name="phone" style="font-size: 20px; color: var(--wa-color-neutral-500);"></wa-icon>
|
|
<div class="wa-stack wa-gap-0" style="flex: 1;">
|
|
<div class="wa-cluster wa-gap-xs">
|
|
<span class="wa-heading-s">+44 7700 900123</span>
|
|
<wa-badge variant="brand" pill>Verified</wa-badge>
|
|
</div>
|
|
<span class="wa-caption-s" style="color: var(--wa-color-neutral-500);">Added Oct 5, 2025 - Backup number</span>
|
|
</div>
|
|
<wa-dropdown>
|
|
<wa-icon-button slot="trigger" name="ellipsis-vertical" label="Options"></wa-icon-button>
|
|
<wa-dropdown-item>
|
|
<wa-icon slot="icon" name="star"></wa-icon>
|
|
Set as Primary
|
|
</wa-dropdown-item>
|
|
<wa-dropdown-item>
|
|
<wa-icon slot="icon" name="message"></wa-icon>
|
|
Send Test SMS
|
|
</wa-dropdown-item>
|
|
<wa-divider></wa-divider>
|
|
<wa-dropdown-item style="color: var(--wa-color-danger);">
|
|
<wa-icon slot="icon" name="trash"></wa-icon>
|
|
Remove
|
|
</wa-dropdown-item>
|
|
</wa-dropdown>
|
|
</div>
|
|
</div>
|
|
|
|
<wa-card>
|
|
<span slot="header" class="wa-heading-s">SMS Authentication</span>
|
|
<div class="wa-stack">
|
|
<div class="notification-row">
|
|
<div class="wa-stack wa-gap-0">
|
|
<span class="wa-heading-xs">Enable SMS 2FA</span>
|
|
<span class="wa-caption-s" style="color: var(--wa-color-neutral-500);">Receive codes via SMS for high-risk actions</span>
|
|
</div>
|
|
<wa-switch checked></wa-switch>
|
|
</div>
|
|
<div class="notification-row">
|
|
<div class="wa-stack wa-gap-0">
|
|
<span class="wa-heading-xs">SMS recovery codes</span>
|
|
<span class="wa-caption-s" style="color: var(--wa-color-neutral-500);">Allow account recovery via SMS</span>
|
|
</div>
|
|
<wa-switch></wa-switch>
|
|
</div>
|
|
</div>
|
|
</wa-card>
|
|
|
|
<wa-callout variant="warning">
|
|
<wa-icon slot="icon" name="triangle-alert"></wa-icon>
|
|
<div class="wa-stack wa-gap-2xs">
|
|
<span class="wa-heading-xs">SMS Security Notice</span>
|
|
<span class="wa-caption-s">SMS-based authentication is less secure than passkeys. We recommend using passkeys as your primary authentication method.</span>
|
|
</div>
|
|
</wa-callout>
|
|
</div>
|
|
</wa-tab-panel>
|
|
|
|
<wa-tab-panel name="developer">
|
|
<div class="wa-stack wa-gap-xl">
|
|
<div class="wa-stack wa-gap-2xs">
|
|
<span class="wa-heading-m">Developer Settings</span>
|
|
<span class="wa-caption-s" style="color: var(--wa-color-neutral-500);">API access and integration options</span>
|
|
</div>
|
|
|
|
<wa-card>
|
|
<div slot="header" class="wa-flank">
|
|
<span class="wa-heading-s">API Keys</span>
|
|
<wa-button size="small" variant="neutral" appearance="outlined" id="generate-key-btn">
|
|
<wa-icon slot="start" name="plus"></wa-icon>
|
|
Generate Key
|
|
</wa-button>
|
|
</div>
|
|
<div class="wa-stack wa-gap-m">
|
|
<div class="wa-stack wa-gap-s">
|
|
<div class="wa-flank">
|
|
<div class="wa-stack wa-gap-0">
|
|
<span class="wa-heading-xs">Production Key</span>
|
|
<span class="wa-caption-xs" style="color: var(--wa-color-neutral-500);">Created Dec 1, 2025</span>
|
|
</div>
|
|
<wa-badge variant="success" pill>Active</wa-badge>
|
|
</div>
|
|
<div class="api-key-display">
|
|
<span>sk_live_••••••••••••••••••••••••3f8b</span>
|
|
<wa-copy-button value="sk_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6a7b8c9d0e1f23f8b"></wa-copy-button>
|
|
<wa-tooltip content="Reveal key">
|
|
<wa-icon-button name="eye" label="Reveal"></wa-icon-button>
|
|
</wa-tooltip>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="wa-stack wa-gap-s">
|
|
<div class="wa-flank">
|
|
<div class="wa-stack wa-gap-0">
|
|
<span class="wa-heading-xs">Test Key</span>
|
|
<span class="wa-caption-xs" style="color: var(--wa-color-neutral-500);">Created Dec 1, 2025</span>
|
|
</div>
|
|
<wa-badge variant="neutral" pill>Test Mode</wa-badge>
|
|
</div>
|
|
<div class="api-key-display">
|
|
<span>sk_test_••••••••••••••••••••••••9a2c</span>
|
|
<wa-copy-button value="sk_test_z9y8x7w6v5u4t3s2r1q0p9o8n7m6l5k4j3i2h1g0f9e8d7c6b5a4z3y2x1w0v9u89a2c"></wa-copy-button>
|
|
<wa-tooltip content="Reveal key">
|
|
<wa-icon-button name="eye" label="Reveal"></wa-icon-button>
|
|
</wa-tooltip>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</wa-card>
|
|
|
|
<wa-card>
|
|
<div slot="header" class="wa-flank">
|
|
<span class="wa-heading-s">Webhooks</span>
|
|
<wa-button size="small" variant="neutral" appearance="outlined" id="add-webhook-btn">
|
|
<wa-icon slot="start" name="plus"></wa-icon>
|
|
Add Endpoint
|
|
</wa-button>
|
|
</div>
|
|
<div class="wa-stack wa-gap-m">
|
|
<div class="webhook-item">
|
|
<div class="wa-flank">
|
|
<div class="wa-stack wa-gap-0">
|
|
<div class="wa-cluster wa-gap-xs">
|
|
<span class="wa-heading-xs">https://api.myapp.com/webhooks/sonr</span>
|
|
<wa-badge variant="success" pill>Active</wa-badge>
|
|
</div>
|
|
<span class="wa-caption-xs" style="color: var(--wa-color-neutral-500);">All events - Last triggered 2 hours ago</span>
|
|
</div>
|
|
<wa-dropdown>
|
|
<wa-icon-button slot="trigger" name="ellipsis-vertical" label="Options"></wa-icon-button>
|
|
<wa-dropdown-item>
|
|
<wa-icon slot="icon" name="pen"></wa-icon>
|
|
Edit
|
|
</wa-dropdown-item>
|
|
<wa-dropdown-item>
|
|
<wa-icon slot="icon" name="paper-plane"></wa-icon>
|
|
Send Test
|
|
</wa-dropdown-item>
|
|
<wa-dropdown-item>
|
|
<wa-icon slot="icon" name="clock-rotate-left"></wa-icon>
|
|
View Logs
|
|
</wa-dropdown-item>
|
|
<wa-divider></wa-divider>
|
|
<wa-dropdown-item style="color: var(--wa-color-danger);">
|
|
<wa-icon slot="icon" name="trash"></wa-icon>
|
|
Delete
|
|
</wa-dropdown-item>
|
|
</wa-dropdown>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="webhook-item">
|
|
<div class="wa-flank">
|
|
<div class="wa-stack wa-gap-0">
|
|
<div class="wa-cluster wa-gap-xs">
|
|
<span class="wa-heading-xs">https://hooks.slack.com/services/...</span>
|
|
<wa-badge variant="warning" pill>Failing</wa-badge>
|
|
</div>
|
|
<span class="wa-caption-xs" style="color: var(--wa-color-danger);">transaction.* events - 3 failures in last hour</span>
|
|
</div>
|
|
<wa-dropdown>
|
|
<wa-icon-button slot="trigger" name="ellipsis-vertical" label="Options"></wa-icon-button>
|
|
<wa-dropdown-item>
|
|
<wa-icon slot="icon" name="pen"></wa-icon>
|
|
Edit
|
|
</wa-dropdown-item>
|
|
<wa-dropdown-item>
|
|
<wa-icon slot="icon" name="arrow-rotate-right"></wa-icon>
|
|
Retry Failed
|
|
</wa-dropdown-item>
|
|
<wa-dropdown-item>
|
|
<wa-icon slot="icon" name="clock-rotate-left"></wa-icon>
|
|
View Logs
|
|
</wa-dropdown-item>
|
|
<wa-divider></wa-divider>
|
|
<wa-dropdown-item style="color: var(--wa-color-danger);">
|
|
<wa-icon slot="icon" name="trash"></wa-icon>
|
|
Delete
|
|
</wa-dropdown-item>
|
|
</wa-dropdown>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</wa-card>
|
|
|
|
<wa-card>
|
|
<span slot="header" class="wa-heading-s">OAuth Application</span>
|
|
<div class="wa-stack wa-gap-l">
|
|
<div class="form-row" style="grid-template-columns: 140px 1fr;">
|
|
<span class="wa-caption-s" style="color: var(--wa-color-neutral-500);">Client ID</span>
|
|
<div class="wa-cluster wa-gap-s">
|
|
<code style="font-family: var(--wa-font-mono); font-size: var(--wa-font-size-s);">sonr_client_a1b2c3d4e5f6</code>
|
|
<wa-copy-button value="sonr_client_a1b2c3d4e5f6"></wa-copy-button>
|
|
</div>
|
|
</div>
|
|
<div class="form-row" style="grid-template-columns: 140px 1fr;">
|
|
<span class="wa-caption-s" style="color: var(--wa-color-neutral-500);">Client Secret</span>
|
|
<div class="wa-cluster wa-gap-s">
|
|
<code style="font-family: var(--wa-font-mono); font-size: var(--wa-font-size-s);">••••••••••••••••</code>
|
|
<wa-copy-button value="sonr_secret_x9y8z7w6v5u4t3s2r1"></wa-copy-button>
|
|
<wa-button size="small" appearance="plain">Regenerate</wa-button>
|
|
</div>
|
|
</div>
|
|
<wa-divider></wa-divider>
|
|
<div class="wa-stack wa-gap-s">
|
|
<span class="wa-heading-xs">Redirect URIs</span>
|
|
<wa-input value="https://myapp.com/auth/callback" placeholder="https://example.com/callback">
|
|
<wa-icon-button slot="suffix" name="trash" label="Remove"></wa-icon-button>
|
|
</wa-input>
|
|
<wa-input value="http://localhost:3000/auth/callback" placeholder="https://example.com/callback">
|
|
<wa-icon-button slot="suffix" name="trash" label="Remove"></wa-icon-button>
|
|
</wa-input>
|
|
<wa-button size="small" appearance="plain">
|
|
<wa-icon slot="start" name="plus"></wa-icon>
|
|
Add URI
|
|
</wa-button>
|
|
</div>
|
|
</div>
|
|
</wa-card>
|
|
|
|
<wa-card>
|
|
<span slot="header" class="wa-heading-s">Developer Mode</span>
|
|
<div class="wa-stack">
|
|
<div class="notification-row">
|
|
<div class="wa-stack wa-gap-0">
|
|
<span class="wa-heading-xs">Enable debug logging</span>
|
|
<span class="wa-caption-s" style="color: var(--wa-color-neutral-500);">Log detailed API requests and responses</span>
|
|
</div>
|
|
<wa-switch></wa-switch>
|
|
</div>
|
|
<div class="notification-row">
|
|
<div class="wa-stack wa-gap-0">
|
|
<span class="wa-heading-xs">Test mode</span>
|
|
<span class="wa-caption-s" style="color: var(--wa-color-neutral-500);">Use testnet for all transactions</span>
|
|
</div>
|
|
<wa-switch checked></wa-switch>
|
|
</div>
|
|
<div class="notification-row">
|
|
<div class="wa-stack wa-gap-0">
|
|
<span class="wa-heading-xs">Show raw responses</span>
|
|
<span class="wa-caption-s" style="color: var(--wa-color-neutral-500);">Display JSON in UI for debugging</span>
|
|
</div>
|
|
<wa-switch></wa-switch>
|
|
</div>
|
|
</div>
|
|
</wa-card>
|
|
|
|
<wa-callout variant="neutral">
|
|
<wa-icon slot="icon" name="book"></wa-icon>
|
|
<div class="wa-stack wa-gap-2xs">
|
|
<span class="wa-heading-xs">API Documentation</span>
|
|
<span class="wa-caption-s">
|
|
View our <a href="#" style="color: var(--wa-color-primary);">API reference</a> and
|
|
<a href="#" style="color: var(--wa-color-primary);">integration guides</a> to get started.
|
|
</span>
|
|
</div>
|
|
</wa-callout>
|
|
|
|
<div class="wa-cluster wa-gap-s" style="justify-content: flex-end;">
|
|
<wa-button variant="neutral" appearance="outlined">Cancel</wa-button>
|
|
<wa-button variant="brand">Save Changes</wa-button>
|
|
</div>
|
|
</div>
|
|
</wa-tab-panel>
|
|
</wa-tab-group>
|
|
</div>
|
|
</main>
|
|
</div>
|
|
|
|
<wa-dialog label="Add Email Address" id="add-email-dialog" style="--width: 420px;">
|
|
<div class="wa-stack wa-gap-l">
|
|
<wa-input type="email" label="Email Address" placeholder="you@example.com" autofocus>
|
|
<wa-icon slot="prefix" name="envelope"></wa-icon>
|
|
</wa-input>
|
|
<wa-callout variant="neutral">
|
|
<wa-icon slot="icon" name="circle-info"></wa-icon>
|
|
<span class="wa-caption-s">We'll send a verification link to this address.</span>
|
|
</wa-callout>
|
|
</div>
|
|
<div slot="footer" class="wa-cluster wa-gap-s" style="justify-content: flex-end; width: 100%;">
|
|
<wa-button variant="neutral" appearance="outlined" data-dialog="close">Cancel</wa-button>
|
|
<wa-button variant="brand">Send Verification</wa-button>
|
|
</div>
|
|
</wa-dialog>
|
|
|
|
<wa-dialog label="Add Phone Number" id="add-phone-dialog" style="--width: 420px;">
|
|
<div class="wa-stack wa-gap-l">
|
|
<div class="wa-cluster wa-gap-s">
|
|
<wa-select value="+1" style="width: 100px;">
|
|
<wa-option value="+1">+1</wa-option>
|
|
<wa-option value="+44">+44</wa-option>
|
|
<wa-option value="+49">+49</wa-option>
|
|
<wa-option value="+33">+33</wa-option>
|
|
<wa-option value="+81">+81</wa-option>
|
|
</wa-select>
|
|
<wa-input type="tel" label="Phone Number" placeholder="(555) 123-4567" style="flex: 1;">
|
|
<wa-icon slot="prefix" name="phone"></wa-icon>
|
|
</wa-input>
|
|
</div>
|
|
<wa-callout variant="neutral">
|
|
<wa-icon slot="icon" name="circle-info"></wa-icon>
|
|
<span class="wa-caption-s">We'll send a verification code via SMS.</span>
|
|
</wa-callout>
|
|
</div>
|
|
<div slot="footer" class="wa-cluster wa-gap-s" style="justify-content: flex-end; width: 100%;">
|
|
<wa-button variant="neutral" appearance="outlined" data-dialog="close">Cancel</wa-button>
|
|
<wa-button variant="brand">Send Code</wa-button>
|
|
</div>
|
|
</wa-dialog>
|
|
|
|
<wa-dialog label="Add Device" id="add-device-dialog" style="--width: 480px;">
|
|
<div class="wa-stack wa-gap-l" style="text-align: center;">
|
|
<wa-icon name="fingerprint" style="font-size: 64px; color: var(--wa-color-primary);"></wa-icon>
|
|
<div class="wa-stack wa-gap-xs">
|
|
<span class="wa-heading-m">Register a Passkey</span>
|
|
<span class="wa-caption-m" style="color: var(--wa-color-neutral-500);">
|
|
Use your device's built-in authenticator (Face ID, Touch ID, Windows Hello) or a security key.
|
|
</span>
|
|
</div>
|
|
<wa-button variant="brand" size="large" style="width: 100%;">
|
|
<wa-icon slot="start" name="plus"></wa-icon>
|
|
Register Device
|
|
</wa-button>
|
|
<wa-divider>or</wa-divider>
|
|
<wa-button variant="neutral" appearance="outlined" style="width: 100%;">
|
|
<wa-icon slot="start" name="key"></wa-icon>
|
|
Use Security Key
|
|
</wa-button>
|
|
</div>
|
|
<div slot="footer" class="wa-cluster" style="justify-content: center; width: 100%;">
|
|
<wa-button variant="neutral" appearance="plain" data-dialog="close">Cancel</wa-button>
|
|
</div>
|
|
</wa-dialog>
|
|
|
|
<wa-dialog label="Generate API Key" id="generate-key-dialog" style="--width: 420px;">
|
|
<div class="wa-stack wa-gap-l">
|
|
<wa-input label="Key Name" placeholder="e.g., Production Server" autofocus>
|
|
<wa-icon slot="prefix" name="tag"></wa-icon>
|
|
</wa-input>
|
|
<wa-select label="Environment" value="test">
|
|
<wa-option value="test">Test Mode</wa-option>
|
|
<wa-option value="live">Production</wa-option>
|
|
</wa-select>
|
|
<wa-select label="Permissions" value="full">
|
|
<wa-option value="full">Full Access</wa-option>
|
|
<wa-option value="read">Read Only</wa-option>
|
|
<wa-option value="write">Write Only</wa-option>
|
|
</wa-select>
|
|
<wa-callout variant="warning">
|
|
<wa-icon slot="icon" name="triangle-alert"></wa-icon>
|
|
<span class="wa-caption-s">Your secret key will only be shown once. Store it securely.</span>
|
|
</wa-callout>
|
|
</div>
|
|
<div slot="footer" class="wa-cluster wa-gap-s" style="justify-content: flex-end; width: 100%;">
|
|
<wa-button variant="neutral" appearance="outlined" data-dialog="close">Cancel</wa-button>
|
|
<wa-button variant="brand">Generate Key</wa-button>
|
|
</div>
|
|
</wa-dialog>
|
|
|
|
<wa-dialog label="Add Webhook Endpoint" id="add-webhook-dialog" style="--width: 480px;">
|
|
<div class="wa-stack wa-gap-l">
|
|
<wa-input label="Endpoint URL" placeholder="https://api.example.com/webhooks" type="url">
|
|
<wa-icon slot="prefix" name="globe"></wa-icon>
|
|
</wa-input>
|
|
<wa-input label="Description" placeholder="Optional description">
|
|
<wa-icon slot="prefix" name="align-left"></wa-icon>
|
|
</wa-input>
|
|
<div class="wa-stack wa-gap-s">
|
|
<span class="wa-heading-xs">Events to Subscribe</span>
|
|
<wa-checkbox checked>
|
|
<div class="wa-stack wa-gap-0">
|
|
<span>transaction.*</span>
|
|
<span class="wa-caption-xs" style="color: var(--wa-color-neutral-500);">All transaction events</span>
|
|
</div>
|
|
</wa-checkbox>
|
|
<wa-checkbox checked>
|
|
<div class="wa-stack wa-gap-0">
|
|
<span>connection.*</span>
|
|
<span class="wa-caption-xs" style="color: var(--wa-color-neutral-500);">App connection events</span>
|
|
</div>
|
|
</wa-checkbox>
|
|
<wa-checkbox>
|
|
<div class="wa-stack wa-gap-0">
|
|
<span>signature.*</span>
|
|
<span class="wa-caption-xs" style="color: var(--wa-color-neutral-500);">Signature request events</span>
|
|
</div>
|
|
</wa-checkbox>
|
|
<wa-checkbox>
|
|
<div class="wa-stack wa-gap-0">
|
|
<span>account.*</span>
|
|
<span class="wa-caption-xs" style="color: var(--wa-color-neutral-500);">Account update events</span>
|
|
</div>
|
|
</wa-checkbox>
|
|
</div>
|
|
</div>
|
|
<div slot="footer" class="wa-cluster wa-gap-s" style="justify-content: flex-end; width: 100%;">
|
|
<wa-button variant="neutral" appearance="outlined" data-dialog="close">Cancel</wa-button>
|
|
<wa-button variant="brand">Create Webhook</wa-button>
|
|
</div>
|
|
</wa-dialog>
|
|
</wa-page>
|
|
|
|
<script>
|
|
const addEmailDialog = document.getElementById('add-email-dialog');
|
|
const addPhoneDialog = document.getElementById('add-phone-dialog');
|
|
const addDeviceDialog = document.getElementById('add-device-dialog');
|
|
const generateKeyDialog = document.getElementById('generate-key-dialog');
|
|
const addWebhookDialog = document.getElementById('add-webhook-dialog');
|
|
|
|
document.getElementById('add-email-btn')?.addEventListener('click', () => {
|
|
addEmailDialog.open = true;
|
|
});
|
|
|
|
document.getElementById('add-phone-btn')?.addEventListener('click', () => {
|
|
addPhoneDialog.open = true;
|
|
});
|
|
|
|
document.querySelectorAll('wa-button').forEach(btn => {
|
|
if (btn.textContent.includes('Add Device')) {
|
|
btn.addEventListener('click', () => {
|
|
addDeviceDialog.open = true;
|
|
});
|
|
}
|
|
});
|
|
|
|
document.getElementById('generate-key-btn')?.addEventListener('click', () => {
|
|
generateKeyDialog.open = true;
|
|
});
|
|
|
|
document.getElementById('add-webhook-btn')?.addEventListener('click', () => {
|
|
addWebhookDialog.open = true;
|
|
});
|
|
|
|
document.querySelectorAll('.sidebar-nav a').forEach(link => {
|
|
link.addEventListener('click', function(e) {
|
|
if (this.getAttribute('href') === '#') {
|
|
e.preventDefault();
|
|
}
|
|
document.querySelectorAll('.sidebar-nav a').forEach(l => l.classList.remove('active'));
|
|
this.classList.add('active');
|
|
});
|
|
});
|
|
</script>
|
|
</body>
|
|
</html>
|