883 lines
36 KiB
HTML
883 lines
36 KiB
HTML
|
|
<!DOCTYPE html>
|
||
|
|
<!--
|
||
|
|
================================================================================
|
||
|
|
TEMPL MIGRATION GUIDE: activity.html → views/activity.templ
|
||
|
|
================================================================================
|
||
|
|
|
||
|
|
PAGE OVERVIEW:
|
||
|
|
- Activity dashboard showing sessions, apps, and security events
|
||
|
|
- Stats cards: Active Sessions, Connected Apps, Pending alerts, Last Active
|
||
|
|
- Pending Actions section with actionable items (review, sign, block)
|
||
|
|
- Active Sessions list with revoke capability
|
||
|
|
- Connected Apps list with permission management
|
||
|
|
- Activity Log with collapsible day groups and category filtering
|
||
|
|
|
||
|
|
MAIN TEMPL COMPONENT:
|
||
|
|
templ ActivityPage(data ActivityData) {
|
||
|
|
@layouts.DashboardLayout("activity") {
|
||
|
|
@PageHeader("Activity", "Sessions, apps, and recent actions")
|
||
|
|
@ActivityStatsGrid(data.Stats)
|
||
|
|
@PendingActionsCard(data.PendingActions)
|
||
|
|
<div class="content-grid">
|
||
|
|
@ActiveSessionsCard(data.Sessions)
|
||
|
|
@ConnectedAppsCard(data.Apps)
|
||
|
|
@ActivityLogCard(data.ActivityLog)
|
||
|
|
</div>
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
HTMX INTEGRATION:
|
||
|
|
- Refresh button: hx-get="/api/activity" hx-target="#activity-content" hx-indicator="#refresh-icon"
|
||
|
|
- Pending action buttons: hx-post="/api/activity/action" hx-vals='{"action":"approve","id":"123"}'
|
||
|
|
- Dismiss action: hx-delete="/api/activity/pending/123" hx-target="closest article" hx-swap="outerHTML"
|
||
|
|
- Revoke session: hx-delete="/api/sessions/abc" hx-target="closest article" hx-swap="delete"
|
||
|
|
- Disconnect app: hx-delete="/api/connections/xyz" hx-confirm="Disconnect this app?"
|
||
|
|
- Activity filter: hx-get="/api/activity/log?filter=security" hx-target="#activity-log"
|
||
|
|
- Load more: hx-get="/api/activity/log?offset=9" hx-target="#activity-log" hx-swap="beforeend"
|
||
|
|
- wa-details expansion: Client-side (no HTMX needed)
|
||
|
|
|
||
|
|
SUB-COMPONENTS TO EXTRACT:
|
||
|
|
- ActivityStatsGrid(stats ActivityStats)
|
||
|
|
- PendingActionsCard(actions []PendingAction)
|
||
|
|
- PendingActionItem(action PendingAction)
|
||
|
|
- ActiveSessionsCard(sessions []Session)
|
||
|
|
- SessionItem(session Session, isCurrent bool)
|
||
|
|
- ConnectedAppsCard(apps []ConnectedApp)
|
||
|
|
- AppItem(app ConnectedApp)
|
||
|
|
- ActivityLogCard(logs []ActivityLogGroup, filter string)
|
||
|
|
- ActivityLogGroup(date string, entries []ActivityEntry)
|
||
|
|
- ActivityEntry(entry ActivityEntry)
|
||
|
|
- ActivityFilterSelect(currentFilter string)
|
||
|
|
|
||
|
|
STATE/PROPS:
|
||
|
|
type ActivityData struct {
|
||
|
|
Stats ActivityStats
|
||
|
|
PendingActions []PendingAction
|
||
|
|
Sessions []Session
|
||
|
|
Apps []ConnectedApp
|
||
|
|
ActivityLog []ActivityLogGroup
|
||
|
|
}
|
||
|
|
|
||
|
|
type ActivityStats struct {
|
||
|
|
ActiveSessions int
|
||
|
|
ConnectedApps int
|
||
|
|
PendingCount int
|
||
|
|
LastActive time.Time
|
||
|
|
}
|
||
|
|
|
||
|
|
type PendingAction struct {
|
||
|
|
ID string
|
||
|
|
Type string // "security", "signature", "suspicious", "transaction"
|
||
|
|
Title string
|
||
|
|
Description string
|
||
|
|
Timestamp time.Time
|
||
|
|
Actions []ActionButton // Review, Dismiss, Sign, Block, etc.
|
||
|
|
}
|
||
|
|
|
||
|
|
type Session struct {
|
||
|
|
ID string
|
||
|
|
Device string
|
||
|
|
Browser string
|
||
|
|
Location string
|
||
|
|
IsCurrent bool
|
||
|
|
LastSeen time.Time
|
||
|
|
}
|
||
|
|
|
||
|
|
type ConnectedApp struct {
|
||
|
|
ID string
|
||
|
|
Name string
|
||
|
|
Domain string
|
||
|
|
IconColor string
|
||
|
|
Permissions []string
|
||
|
|
ConnectedAt time.Time
|
||
|
|
}
|
||
|
|
|
||
|
|
type ActivityLogGroup struct {
|
||
|
|
Date string
|
||
|
|
Entries []ActivityEntry
|
||
|
|
}
|
||
|
|
|
||
|
|
type ActivityEntry struct {
|
||
|
|
Type string // "signin", "auth", "send", "receive", "connect", "swap", "passkey"
|
||
|
|
Title string
|
||
|
|
Description string
|
||
|
|
Timestamp time.Time
|
||
|
|
Tag string
|
||
|
|
TagVariant string
|
||
|
|
}
|
||
|
|
|
||
|
|
HTMX PATTERNS:
|
||
|
|
// Refresh with loading indicator
|
||
|
|
<wa-button id="refresh-activity"
|
||
|
|
hx-get="/api/activity/refresh"
|
||
|
|
hx-target="#activity-content"
|
||
|
|
hx-indicator="find wa-icon">
|
||
|
|
|
||
|
|
// Pending action dismiss with animation
|
||
|
|
<wa-button hx-delete="/api/activity/pending/{action.ID}"
|
||
|
|
hx-target="closest article"
|
||
|
|
hx-swap="outerHTML swap:0.3s"
|
||
|
|
hx-on::before-request="this.closest('article').style.opacity='0.5'">
|
||
|
|
|
||
|
|
// Session revoke
|
||
|
|
<wa-button hx-delete="/api/sessions/{session.ID}"
|
||
|
|
hx-target="closest article"
|
||
|
|
hx-swap="delete"
|
||
|
|
hx-confirm="Revoke this session?">
|
||
|
|
|
||
|
|
// Activity log filtering
|
||
|
|
<wa-select hx-get="/api/activity/log"
|
||
|
|
hx-target="#activity-log-entries"
|
||
|
|
hx-include="this"
|
||
|
|
name="filter">
|
||
|
|
|
||
|
|
// Load more with append
|
||
|
|
<wa-button hx-get="/api/activity/log?offset=9"
|
||
|
|
hx-target="#activity-log-entries"
|
||
|
|
hx-swap="beforeend">
|
||
|
|
|
||
|
|
// Sign out all sessions
|
||
|
|
<wa-button hx-delete="/api/sessions/all"
|
||
|
|
hx-confirm="Sign out of all other sessions?"
|
||
|
|
hx-target="#sessions-list">
|
||
|
|
================================================================================
|
||
|
|
-->
|
||
|
|
<html lang="en" class="wa-cloak">
|
||
|
|
<head>
|
||
|
|
<meta charset="utf-8">
|
||
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
|
|
<title>Activity - 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);
|
||
|
|
}
|
||
|
|
|
||
|
|
.stats-grid {
|
||
|
|
--min-column-size: 180px;
|
||
|
|
margin-bottom: var(--wa-space-xl);
|
||
|
|
}
|
||
|
|
|
||
|
|
.stat-card {
|
||
|
|
background: var(--wa-color-surface);
|
||
|
|
}
|
||
|
|
|
||
|
|
.content-grid {
|
||
|
|
display: grid;
|
||
|
|
grid-template-columns: 1fr 1fr;
|
||
|
|
gap: var(--wa-space-xl);
|
||
|
|
}
|
||
|
|
|
||
|
|
@media (max-width: 1024px) {
|
||
|
|
.content-grid {
|
||
|
|
grid-template-columns: 1fr;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
.activity-card {
|
||
|
|
grid-column: 1 / -1;
|
||
|
|
}
|
||
|
|
|
||
|
|
wa-details::part(header) {
|
||
|
|
font-weight: 600;
|
||
|
|
}
|
||
|
|
|
||
|
|
wa-details::part(content) {
|
||
|
|
padding-top: var(--wa-space-s);
|
||
|
|
}
|
||
|
|
</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" class="active">
|
||
|
|
<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">
|
||
|
|
<wa-icon name="gear"></wa-icon>
|
||
|
|
Settings
|
||
|
|
</a>
|
||
|
|
</li>
|
||
|
|
</ul>
|
||
|
|
</aside>
|
||
|
|
|
||
|
|
<main class="main-content">
|
||
|
|
<header class="page-header">
|
||
|
|
<div class="wa-flank">
|
||
|
|
<div class="wa-stack wa-gap-2xs">
|
||
|
|
<span class="wa-heading-xl">Activity</span>
|
||
|
|
<span class="wa-caption-s" style="color: var(--wa-color-neutral-500);">Sessions, apps, and recent actions</span>
|
||
|
|
</div>
|
||
|
|
<wa-button variant="neutral" appearance="outlined" size="small" id="refresh-activity">
|
||
|
|
<wa-icon slot="start" name="rotate"></wa-icon>
|
||
|
|
Refresh
|
||
|
|
</wa-button>
|
||
|
|
</div>
|
||
|
|
</header>
|
||
|
|
|
||
|
|
<div class="wa-grid stats-grid">
|
||
|
|
<wa-card class="stat-card">
|
||
|
|
<div class="wa-flank">
|
||
|
|
<wa-avatar shape="rounded" style="background: var(--wa-color-primary-subtle);">
|
||
|
|
<wa-icon slot="icon" name="desktop" style="color: var(--wa-color-primary);"></wa-icon>
|
||
|
|
</wa-avatar>
|
||
|
|
<div class="wa-stack wa-gap-3xs">
|
||
|
|
<span class="wa-caption-xs" style="color: var(--wa-color-neutral-500);">Active Sessions</span>
|
||
|
|
<span class="wa-heading-2xl">3</span>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</wa-card>
|
||
|
|
|
||
|
|
<wa-card class="stat-card">
|
||
|
|
<div class="wa-flank">
|
||
|
|
<wa-avatar shape="rounded" style="background: var(--wa-color-success-subtle);">
|
||
|
|
<wa-icon slot="icon" name="plug" style="color: var(--wa-color-success);"></wa-icon>
|
||
|
|
</wa-avatar>
|
||
|
|
<div class="wa-stack wa-gap-3xs">
|
||
|
|
<span class="wa-caption-xs" style="color: var(--wa-color-neutral-500);">Connected Apps</span>
|
||
|
|
<span class="wa-heading-2xl">5</span>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</wa-card>
|
||
|
|
|
||
|
|
<wa-card class="stat-card">
|
||
|
|
<div class="wa-flank">
|
||
|
|
<wa-avatar shape="rounded" style="background: var(--wa-color-warning-subtle);">
|
||
|
|
<wa-icon slot="icon" name="bell" style="color: var(--wa-color-warning);"></wa-icon>
|
||
|
|
</wa-avatar>
|
||
|
|
<div class="wa-stack wa-gap-3xs">
|
||
|
|
<span class="wa-caption-xs" style="color: var(--wa-color-neutral-500);">Pending</span>
|
||
|
|
<span class="wa-cluster wa-gap-xs">
|
||
|
|
<span class="wa-heading-2xl">4</span>
|
||
|
|
<wa-badge variant="warning" attention="pulse" pill>!</wa-badge>
|
||
|
|
</span>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</wa-card>
|
||
|
|
|
||
|
|
<wa-card class="stat-card">
|
||
|
|
<div class="wa-flank">
|
||
|
|
<wa-avatar shape="rounded" style="background: var(--wa-color-neutral-100);">
|
||
|
|
<wa-icon slot="icon" name="clock-rotate-left" style="color: var(--wa-color-neutral-600);"></wa-icon>
|
||
|
|
</wa-avatar>
|
||
|
|
<div class="wa-stack wa-gap-3xs">
|
||
|
|
<span class="wa-caption-xs" style="color: var(--wa-color-neutral-500);">Last Active</span>
|
||
|
|
<wa-relative-time date="2026-01-03T01:49:00" class="wa-heading-s"></wa-relative-time>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</wa-card>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<wa-card style="margin-bottom: var(--wa-space-xl);">
|
||
|
|
<div slot="header" class="wa-flank">
|
||
|
|
<span class="wa-heading-m">Pending Actions</span>
|
||
|
|
<wa-button appearance="plain" size="small">Clear All</wa-button>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="wa-stack">
|
||
|
|
<article class="wa-flank:end wa-align-items-baseline" style="--flank-size: auto;">
|
||
|
|
<div class="wa-flank wa-gap-m" style="flex: 1;">
|
||
|
|
<wa-icon class="wa-font-size-l" name="shield-halved" style="color: var(--wa-color-warning);"></wa-icon>
|
||
|
|
<div class="wa-split">
|
||
|
|
<div class="wa-stack wa-gap-0">
|
||
|
|
<span class="wa-heading-s">Security Review Required</span>
|
||
|
|
<div class="wa-cluster wa-gap-2xs wa-caption-s" style="color: var(--wa-color-neutral-600);">
|
||
|
|
<span>New device signed in from</span>
|
||
|
|
<strong>San Francisco, CA</strong>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<wa-relative-time date="2026-01-03T01:40:00" class="wa-caption-s"></wa-relative-time>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<div class="wa-cluster wa-gap-xs">
|
||
|
|
<wa-button variant="brand" size="small">Review</wa-button>
|
||
|
|
<wa-button variant="neutral" appearance="outlined" size="small">Dismiss</wa-button>
|
||
|
|
</div>
|
||
|
|
</article>
|
||
|
|
|
||
|
|
<wa-divider></wa-divider>
|
||
|
|
|
||
|
|
<article class="wa-flank:end wa-align-items-baseline" style="--flank-size: auto;">
|
||
|
|
<div class="wa-flank wa-gap-m" style="flex: 1;">
|
||
|
|
<wa-icon class="wa-font-size-l" name="signature" style="color: var(--wa-color-primary);"></wa-icon>
|
||
|
|
<div class="wa-split">
|
||
|
|
<div class="wa-stack wa-gap-0">
|
||
|
|
<span class="wa-heading-s">Signature Request</span>
|
||
|
|
<div class="wa-cluster wa-gap-2xs wa-caption-s" style="color: var(--wa-color-neutral-600);">
|
||
|
|
<a href="#">DeFi Protocol</a>
|
||
|
|
<span>wants to verify wallet ownership</span>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<wa-relative-time date="2026-01-03T01:25:00" class="wa-caption-s"></wa-relative-time>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<div class="wa-cluster wa-gap-xs">
|
||
|
|
<wa-button variant="brand" size="small">Sign</wa-button>
|
||
|
|
<wa-button variant="danger" appearance="outlined" size="small">Reject</wa-button>
|
||
|
|
</div>
|
||
|
|
</article>
|
||
|
|
|
||
|
|
<wa-divider></wa-divider>
|
||
|
|
|
||
|
|
<article class="wa-flank:end wa-align-items-baseline" style="--flank-size: auto;">
|
||
|
|
<div class="wa-flank wa-gap-m" style="flex: 1;">
|
||
|
|
<wa-icon class="wa-font-size-l" name="triangle-exclamation" style="color: var(--wa-color-danger);"></wa-icon>
|
||
|
|
<div class="wa-split">
|
||
|
|
<div class="wa-stack wa-gap-0">
|
||
|
|
<span class="wa-heading-s">Suspicious Activity</span>
|
||
|
|
<div class="wa-cluster wa-gap-2xs wa-caption-s" style="color: var(--wa-color-neutral-600);">
|
||
|
|
<span>Multiple failed logins from IP</span>
|
||
|
|
<code style="font-size: var(--wa-font-size-xs);">185.234.xx.xx</code>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<wa-relative-time date="2026-01-03T00:50:00" class="wa-caption-s"></wa-relative-time>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<div class="wa-cluster wa-gap-xs">
|
||
|
|
<wa-button variant="danger" size="small">Block IP</wa-button>
|
||
|
|
<wa-button variant="neutral" appearance="outlined" size="small">Details</wa-button>
|
||
|
|
</div>
|
||
|
|
</article>
|
||
|
|
|
||
|
|
<wa-divider></wa-divider>
|
||
|
|
|
||
|
|
<article class="wa-flank:end wa-align-items-baseline" style="--flank-size: auto;">
|
||
|
|
<div class="wa-flank wa-gap-m" style="flex: 1;">
|
||
|
|
<wa-icon class="wa-font-size-l" name="arrow-down" style="color: var(--wa-color-success);"></wa-icon>
|
||
|
|
<div class="wa-split">
|
||
|
|
<div class="wa-stack wa-gap-0">
|
||
|
|
<span class="wa-heading-s">Incoming Transaction</span>
|
||
|
|
<div class="wa-cluster wa-gap-2xs wa-caption-s" style="color: var(--wa-color-neutral-600);">
|
||
|
|
<span>From</span>
|
||
|
|
<a href="#">sonr1k4m...9p3q</a>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<wa-relative-time date="2026-01-02T23:50:00" class="wa-caption-s"></wa-relative-time>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<wa-tag variant="success">+ 250 SNR</wa-tag>
|
||
|
|
</article>
|
||
|
|
</div>
|
||
|
|
</wa-card>
|
||
|
|
|
||
|
|
<div class="content-grid">
|
||
|
|
<wa-card>
|
||
|
|
<div slot="header" class="wa-flank">
|
||
|
|
<span class="wa-heading-m">Active Sessions</span>
|
||
|
|
<wa-button appearance="plain" size="small" variant="danger">Sign Out All</wa-button>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="wa-stack">
|
||
|
|
<article class="wa-flank wa-gap-m">
|
||
|
|
<wa-icon class="wa-font-size-l" name="desktop" style="color: var(--wa-color-primary);"></wa-icon>
|
||
|
|
<div class="wa-split">
|
||
|
|
<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>
|
||
|
|
<div class="wa-cluster wa-gap-2xs wa-caption-s" style="color: var(--wa-color-neutral-600);">
|
||
|
|
<span>Chrome 120</span>
|
||
|
|
<span>·</span>
|
||
|
|
<span>San Francisco, CA</span>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<span class="wa-caption-xs" style="color: var(--wa-color-success);">Active now</span>
|
||
|
|
</div>
|
||
|
|
</article>
|
||
|
|
|
||
|
|
<wa-divider></wa-divider>
|
||
|
|
|
||
|
|
<article class="wa-flank wa-gap-m">
|
||
|
|
<wa-icon class="wa-font-size-l" name="mobile" style="color: var(--wa-color-success);"></wa-icon>
|
||
|
|
<div class="wa-split">
|
||
|
|
<div class="wa-stack wa-gap-0">
|
||
|
|
<span class="wa-heading-s">iPhone 15 Pro</span>
|
||
|
|
<div class="wa-cluster wa-gap-2xs wa-caption-s" style="color: var(--wa-color-neutral-600);">
|
||
|
|
<span>Safari</span>
|
||
|
|
<span>·</span>
|
||
|
|
<span>San Francisco, CA</span>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<wa-relative-time date="2026-01-02T23:51:00" class="wa-caption-xs"></wa-relative-time>
|
||
|
|
</div>
|
||
|
|
<wa-button variant="danger" appearance="plain" size="small">Revoke</wa-button>
|
||
|
|
</article>
|
||
|
|
|
||
|
|
<wa-divider></wa-divider>
|
||
|
|
|
||
|
|
<article class="wa-flank wa-gap-m">
|
||
|
|
<wa-icon class="wa-font-size-l" name="tablet" style="color: var(--wa-color-warning);"></wa-icon>
|
||
|
|
<div class="wa-split">
|
||
|
|
<div class="wa-stack wa-gap-0">
|
||
|
|
<span class="wa-heading-s">iPad Air</span>
|
||
|
|
<div class="wa-cluster wa-gap-2xs wa-caption-s" style="color: var(--wa-color-neutral-600);">
|
||
|
|
<span>Safari</span>
|
||
|
|
<span>·</span>
|
||
|
|
<span>New York, NY</span>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<wa-relative-time date="2026-01-02T12:00:00" class="wa-caption-xs"></wa-relative-time>
|
||
|
|
</div>
|
||
|
|
<wa-button variant="danger" appearance="plain" size="small">Revoke</wa-button>
|
||
|
|
</article>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div slot="footer">
|
||
|
|
<a href="#" class="wa-cluster wa-gap-xs wa-caption-s" style="color: var(--wa-color-primary);">
|
||
|
|
<span>View session history</span>
|
||
|
|
<wa-icon name="arrow-right"></wa-icon>
|
||
|
|
</a>
|
||
|
|
</div>
|
||
|
|
</wa-card>
|
||
|
|
|
||
|
|
<wa-card>
|
||
|
|
<div slot="header" class="wa-flank">
|
||
|
|
<span class="wa-heading-m">Connected Apps</span>
|
||
|
|
<wa-button appearance="plain" size="small">Manage</wa-button>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="wa-stack">
|
||
|
|
<article class="wa-flank wa-gap-m">
|
||
|
|
<wa-avatar initials="D" style="--size: 36px; background: linear-gradient(135deg, #6366f1, #8b5cf6);"></wa-avatar>
|
||
|
|
<div class="wa-split">
|
||
|
|
<div class="wa-stack wa-gap-0">
|
||
|
|
<span class="wa-heading-s">DeFi Protocol</span>
|
||
|
|
<span class="wa-caption-xs" style="color: var(--wa-color-neutral-500);">defi.example.com</span>
|
||
|
|
</div>
|
||
|
|
<wa-relative-time date="2025-12-31T12:00:00" class="wa-caption-xs"></wa-relative-time>
|
||
|
|
</div>
|
||
|
|
<wa-dropdown>
|
||
|
|
<wa-icon-button slot="trigger" name="ellipsis-vertical" label="Actions"></wa-icon-button>
|
||
|
|
<wa-dropdown-item>View Permissions</wa-dropdown-item>
|
||
|
|
<wa-divider></wa-divider>
|
||
|
|
<wa-dropdown-item style="color: var(--wa-color-danger);">Disconnect</wa-dropdown-item>
|
||
|
|
</wa-dropdown>
|
||
|
|
</article>
|
||
|
|
|
||
|
|
<wa-divider></wa-divider>
|
||
|
|
|
||
|
|
<article class="wa-flank wa-gap-m">
|
||
|
|
<wa-avatar initials="N" style="--size: 36px; background: linear-gradient(135deg, #10b981, #059669);"></wa-avatar>
|
||
|
|
<div class="wa-split">
|
||
|
|
<div class="wa-stack wa-gap-0">
|
||
|
|
<div class="wa-cluster wa-gap-xs">
|
||
|
|
<span class="wa-heading-s">NFT Marketplace</span>
|
||
|
|
<wa-badge variant="warning" pill>transact</wa-badge>
|
||
|
|
</div>
|
||
|
|
<span class="wa-caption-xs" style="color: var(--wa-color-neutral-500);">nft.marketplace.io</span>
|
||
|
|
</div>
|
||
|
|
<wa-relative-time date="2025-12-27T12:00:00" class="wa-caption-xs"></wa-relative-time>
|
||
|
|
</div>
|
||
|
|
<wa-dropdown>
|
||
|
|
<wa-icon-button slot="trigger" name="ellipsis-vertical" label="Actions"></wa-icon-button>
|
||
|
|
<wa-dropdown-item>View Permissions</wa-dropdown-item>
|
||
|
|
<wa-divider></wa-divider>
|
||
|
|
<wa-dropdown-item style="color: var(--wa-color-danger);">Disconnect</wa-dropdown-item>
|
||
|
|
</wa-dropdown>
|
||
|
|
</article>
|
||
|
|
|
||
|
|
<wa-divider></wa-divider>
|
||
|
|
|
||
|
|
<article class="wa-flank wa-gap-m">
|
||
|
|
<wa-avatar initials="S" style="--size: 36px; background: linear-gradient(135deg, #f59e0b, #d97706);"></wa-avatar>
|
||
|
|
<div class="wa-split">
|
||
|
|
<div class="wa-stack wa-gap-0">
|
||
|
|
<span class="wa-heading-s">Staking Dashboard</span>
|
||
|
|
<span class="wa-caption-xs" style="color: var(--wa-color-neutral-500);">stake.sonr.io</span>
|
||
|
|
</div>
|
||
|
|
<wa-relative-time date="2025-12-20T12:00:00" class="wa-caption-xs"></wa-relative-time>
|
||
|
|
</div>
|
||
|
|
<wa-dropdown>
|
||
|
|
<wa-icon-button slot="trigger" name="ellipsis-vertical" label="Actions"></wa-icon-button>
|
||
|
|
<wa-dropdown-item>View Permissions</wa-dropdown-item>
|
||
|
|
<wa-divider></wa-divider>
|
||
|
|
<wa-dropdown-item style="color: var(--wa-color-danger);">Disconnect</wa-dropdown-item>
|
||
|
|
</wa-dropdown>
|
||
|
|
</article>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div slot="footer">
|
||
|
|
<a href="connections.html" class="wa-cluster wa-gap-xs wa-caption-s" style="color: var(--wa-color-primary);">
|
||
|
|
<span>Manage all connections</span>
|
||
|
|
<wa-icon name="arrow-right"></wa-icon>
|
||
|
|
</a>
|
||
|
|
</div>
|
||
|
|
</wa-card>
|
||
|
|
|
||
|
|
<wa-card class="activity-card">
|
||
|
|
<div slot="header" class="wa-flank">
|
||
|
|
<span class="wa-heading-m">Activity Log</span>
|
||
|
|
<div class="wa-cluster wa-gap-s">
|
||
|
|
<wa-select size="small" value="all" style="min-width: 130px;">
|
||
|
|
<wa-option value="all">All Activity</wa-option>
|
||
|
|
<wa-option value="security">Security</wa-option>
|
||
|
|
<wa-option value="transactions">Transactions</wa-option>
|
||
|
|
<wa-option value="connections">Connections</wa-option>
|
||
|
|
</wa-select>
|
||
|
|
<wa-button appearance="plain" size="small">
|
||
|
|
<wa-icon slot="start" name="download"></wa-icon>
|
||
|
|
Export
|
||
|
|
</wa-button>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="wa-stack">
|
||
|
|
<wa-details open>
|
||
|
|
<span class="wa-heading-m" slot="summary">Today</span>
|
||
|
|
<div class="wa-stack">
|
||
|
|
<article class="wa-flank:end wa-align-items-baseline" style="--flank-size: 10ch;">
|
||
|
|
<div class="wa-flank wa-gap-m" style="flex: 1;">
|
||
|
|
<wa-icon name="right-to-bracket" style="color: var(--wa-color-success);"></wa-icon>
|
||
|
|
<div class="wa-split">
|
||
|
|
<div class="wa-stack wa-gap-0">
|
||
|
|
<span class="wa-heading-s">Signed in</span>
|
||
|
|
<div class="wa-cluster wa-gap-2xs wa-caption-s" style="color: var(--wa-color-neutral-600);">
|
||
|
|
<a href="#">MacBook Pro</a>
|
||
|
|
<span>in San Francisco, CA</span>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<wa-format-date date="2026-01-03T01:51:00" hour="numeric" minute="numeric" class="wa-caption-s"></wa-format-date>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<wa-tag variant="success" pill>success</wa-tag>
|
||
|
|
</article>
|
||
|
|
|
||
|
|
<wa-divider></wa-divider>
|
||
|
|
|
||
|
|
<article class="wa-flank:end wa-align-items-baseline" style="--flank-size: 10ch;">
|
||
|
|
<div class="wa-flank wa-gap-m" style="flex: 1;">
|
||
|
|
<wa-icon name="shield-check" style="color: var(--wa-color-primary);"></wa-icon>
|
||
|
|
<div class="wa-split">
|
||
|
|
<div class="wa-stack wa-gap-0">
|
||
|
|
<span class="wa-heading-s">Passkey authenticated</span>
|
||
|
|
<span class="wa-caption-s" style="color: var(--wa-color-neutral-600);">WebAuthn verification</span>
|
||
|
|
</div>
|
||
|
|
<wa-format-date date="2026-01-03T01:51:00" hour="numeric" minute="numeric" class="wa-caption-s"></wa-format-date>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<wa-tag variant="neutral" pill>auth</wa-tag>
|
||
|
|
</article>
|
||
|
|
|
||
|
|
<wa-divider></wa-divider>
|
||
|
|
|
||
|
|
<article class="wa-flank:end wa-align-items-baseline" style="--flank-size: 10ch;">
|
||
|
|
<div class="wa-flank wa-gap-m" style="flex: 1;">
|
||
|
|
<wa-icon name="arrow-up" style="color: var(--wa-color-danger);"></wa-icon>
|
||
|
|
<div class="wa-split">
|
||
|
|
<div class="wa-stack wa-gap-0">
|
||
|
|
<span class="wa-heading-s">Sent tokens</span>
|
||
|
|
<div class="wa-cluster wa-gap-2xs wa-caption-s" style="color: var(--wa-color-neutral-600);">
|
||
|
|
<span>To</span>
|
||
|
|
<a href="#">sonr1k4m...9p3q</a>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<wa-format-date date="2026-01-03T11:22:00" hour="numeric" minute="numeric" class="wa-caption-s"></wa-format-date>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<wa-tag variant="danger">- 500 SNR</wa-tag>
|
||
|
|
</article>
|
||
|
|
|
||
|
|
<wa-divider></wa-divider>
|
||
|
|
|
||
|
|
<article class="wa-flank:end wa-align-items-baseline" style="--flank-size: 10ch;">
|
||
|
|
<div class="wa-flank wa-gap-m" style="flex: 1;">
|
||
|
|
<wa-icon name="plug" style="color: var(--wa-color-primary);"></wa-icon>
|
||
|
|
<div class="wa-split">
|
||
|
|
<div class="wa-stack wa-gap-0">
|
||
|
|
<span class="wa-heading-s">Connected app</span>
|
||
|
|
<div class="wa-cluster wa-gap-2xs wa-caption-s" style="color: var(--wa-color-neutral-600);">
|
||
|
|
<a href="#">DeFi Protocol</a>
|
||
|
|
<span>granted access</span>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<wa-format-date date="2026-01-03T10:15:00" hour="numeric" minute="numeric" class="wa-caption-s"></wa-format-date>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<wa-tag variant="brand" pill>connect</wa-tag>
|
||
|
|
</article>
|
||
|
|
</div>
|
||
|
|
</wa-details>
|
||
|
|
|
||
|
|
<wa-details>
|
||
|
|
<span class="wa-heading-m" slot="summary">Yesterday</span>
|
||
|
|
<div class="wa-stack">
|
||
|
|
<article class="wa-flank:end wa-align-items-baseline" style="--flank-size: 10ch;">
|
||
|
|
<div class="wa-flank wa-gap-m" style="flex: 1;">
|
||
|
|
<wa-icon name="arrow-down" style="color: var(--wa-color-success);"></wa-icon>
|
||
|
|
<div class="wa-split">
|
||
|
|
<div class="wa-stack wa-gap-0">
|
||
|
|
<span class="wa-heading-s">Received tokens</span>
|
||
|
|
<div class="wa-cluster wa-gap-2xs wa-caption-s" style="color: var(--wa-color-neutral-600);">
|
||
|
|
<span>From</span>
|
||
|
|
<a href="#">0x7d3e...9f7a</a>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<wa-format-date date="2026-01-02T16:48:00" hour="numeric" minute="numeric" class="wa-caption-s"></wa-format-date>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<wa-tag variant="success">+ 10 AVAX</wa-tag>
|
||
|
|
</article>
|
||
|
|
|
||
|
|
<wa-divider></wa-divider>
|
||
|
|
|
||
|
|
<article class="wa-flank:end wa-align-items-baseline" style="--flank-size: 10ch;">
|
||
|
|
<div class="wa-flank wa-gap-m" style="flex: 1;">
|
||
|
|
<wa-icon name="right-to-bracket" style="color: var(--wa-color-success);"></wa-icon>
|
||
|
|
<div class="wa-split">
|
||
|
|
<div class="wa-stack wa-gap-0">
|
||
|
|
<span class="wa-heading-s">Signed in</span>
|
||
|
|
<div class="wa-cluster wa-gap-2xs wa-caption-s" style="color: var(--wa-color-neutral-600);">
|
||
|
|
<a href="#">iPhone 15 Pro</a>
|
||
|
|
<span>in San Francisco, CA</span>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<wa-format-date date="2026-01-02T14:15:00" hour="numeric" minute="numeric" class="wa-caption-s"></wa-format-date>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<wa-tag variant="success" pill>success</wa-tag>
|
||
|
|
</article>
|
||
|
|
|
||
|
|
<wa-divider></wa-divider>
|
||
|
|
|
||
|
|
<article class="wa-flank:end wa-align-items-baseline" style="--flank-size: 10ch;">
|
||
|
|
<div class="wa-flank wa-gap-m" style="flex: 1;">
|
||
|
|
<wa-icon name="signature" style="color: var(--wa-color-neutral-500);"></wa-icon>
|
||
|
|
<div class="wa-split">
|
||
|
|
<div class="wa-stack wa-gap-0">
|
||
|
|
<span class="wa-heading-s">Signed message</span>
|
||
|
|
<div class="wa-cluster wa-gap-2xs wa-caption-s" style="color: var(--wa-color-neutral-600);">
|
||
|
|
<span>For</span>
|
||
|
|
<a href="#">NFT Marketplace</a>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<wa-format-date date="2026-01-02T09:30:00" hour="numeric" minute="numeric" class="wa-caption-s"></wa-format-date>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<wa-tag variant="neutral" pill>sign</wa-tag>
|
||
|
|
</article>
|
||
|
|
</div>
|
||
|
|
</wa-details>
|
||
|
|
|
||
|
|
<wa-details>
|
||
|
|
<span class="wa-heading-m" slot="summary">January 1, 2026</span>
|
||
|
|
<div class="wa-stack">
|
||
|
|
<article class="wa-flank:end wa-align-items-baseline" style="--flank-size: 10ch;">
|
||
|
|
<div class="wa-flank wa-gap-m" style="flex: 1;">
|
||
|
|
<wa-icon name="arrow-right-arrow-left" style="color: var(--wa-color-primary);"></wa-icon>
|
||
|
|
<div class="wa-split">
|
||
|
|
<div class="wa-stack wa-gap-0">
|
||
|
|
<span class="wa-heading-s">Swapped tokens</span>
|
||
|
|
<div class="wa-cluster wa-gap-2xs wa-caption-s" style="color: var(--wa-color-neutral-600);">
|
||
|
|
<span>0.5 ETH</span>
|
||
|
|
<wa-icon name="arrow-right" style="font-size: 10px;"></wa-icon>
|
||
|
|
<span>1,172.50 USDC</span>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<wa-format-date date="2026-01-01T09:15:00" hour="numeric" minute="numeric" class="wa-caption-s"></wa-format-date>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<wa-tag variant="brand" pill>swap</wa-tag>
|
||
|
|
</article>
|
||
|
|
|
||
|
|
<wa-divider></wa-divider>
|
||
|
|
|
||
|
|
<article class="wa-flank:end wa-align-items-baseline" style="--flank-size: 10ch;">
|
||
|
|
<div class="wa-flank wa-gap-m" style="flex: 1;">
|
||
|
|
<wa-icon name="key" style="color: var(--wa-color-warning);"></wa-icon>
|
||
|
|
<div class="wa-split">
|
||
|
|
<div class="wa-stack wa-gap-0">
|
||
|
|
<span class="wa-heading-s">Added passkey</span>
|
||
|
|
<div class="wa-cluster wa-gap-2xs wa-caption-s" style="color: var(--wa-color-neutral-600);">
|
||
|
|
<span>From</span>
|
||
|
|
<a href="#">iPad Air</a>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<wa-format-date date="2026-01-01T08:00:00" hour="numeric" minute="numeric" class="wa-caption-s"></wa-format-date>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<wa-tag variant="warning" pill>security</wa-tag>
|
||
|
|
</article>
|
||
|
|
</div>
|
||
|
|
</wa-details>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div slot="footer" class="wa-flank" style="width: 100%;">
|
||
|
|
<span class="wa-caption-s" style="color: var(--wa-color-neutral-500);">Showing last 9 activities</span>
|
||
|
|
<wa-button appearance="plain" size="small">
|
||
|
|
Load More
|
||
|
|
<wa-icon slot="end" name="chevron-down"></wa-icon>
|
||
|
|
</wa-button>
|
||
|
|
</div>
|
||
|
|
</wa-card>
|
||
|
|
</div>
|
||
|
|
</main>
|
||
|
|
</div>
|
||
|
|
</wa-page>
|
||
|
|
|
||
|
|
<script>
|
||
|
|
document.getElementById('refresh-activity').addEventListener('click', function() {
|
||
|
|
const icon = this.querySelector('wa-icon');
|
||
|
|
icon.style.animation = 'spin 0.5s linear';
|
||
|
|
setTimeout(() => icon.style.animation = '', 500);
|
||
|
|
});
|
||
|
|
|
||
|
|
document.querySelectorAll('[data-action="dismiss"]').forEach(btn => {
|
||
|
|
btn.addEventListener('click', function() {
|
||
|
|
const article = this.closest('article');
|
||
|
|
article.style.opacity = '0';
|
||
|
|
article.style.transition = 'opacity 0.3s';
|
||
|
|
setTimeout(() => {
|
||
|
|
const divider = article.nextElementSibling;
|
||
|
|
if (divider?.tagName === 'WA-DIVIDER') divider.remove();
|
||
|
|
article.remove();
|
||
|
|
}, 300);
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
const style = document.createElement('style');
|
||
|
|
style.textContent = '@keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }';
|
||
|
|
document.head.appendChild(style);
|
||
|
|
</script>
|
||
|
|
</body>
|
||
|
|
</html>
|