1339 lines
44 KiB
HTML
1339 lines
44 KiB
HTML
<!DOCTYPE html>
|
|
<!--
|
|
================================================================================
|
|
TEMPL MIGRATION GUIDE: service.html → views/service.templ
|
|
================================================================================
|
|
|
|
PAGE OVERVIEW:
|
|
- Connected service detail page showing comprehensive information about an OAuth client/dApp
|
|
- Hero section: Service logo, name, domain, verification status, connection date, action buttons
|
|
- Usage stats: Sessions, signatures, transactions approved, permissions used
|
|
- Permissions management: Granular scope controls with descriptions
|
|
- Session log: History of all authentication events and actions
|
|
- Security info: OAuth client ID, redirect URIs, token expiry
|
|
- Similar to Google Account connected apps or GitHub OAuth app detail pages
|
|
|
|
MAIN TEMPL COMPONENT:
|
|
templ ServicePage(service ServiceData) {
|
|
@layouts.DashboardLayout("connections") {
|
|
@ServiceHero(service.Info, service.Status)
|
|
@UsageStatsGrid(service.Stats)
|
|
<div class="wa-grid">
|
|
@PermissionsCard(service.Permissions)
|
|
@ServiceInfoCard(service.OAuth)
|
|
</div>
|
|
@SessionLogCard(service.Sessions)
|
|
@DataAccessCard(service.DataAccess)
|
|
}
|
|
}
|
|
|
|
HTMX INTEGRATION:
|
|
- Revoke access: hx-delete="/api/connections/{id}" hx-confirm="Disconnect this app?" hx-target="body" hx-push-url="/connections"
|
|
- Toggle permission: hx-post="/api/connections/{id}/scopes" hx-vals='{"scope":"wallet:sign","enabled":false}' hx-target="#permission-status"
|
|
- Revoke all permissions: hx-post="/api/connections/{id}/revoke-all" hx-target="#permissions-list"
|
|
- Refresh token: hx-post="/api/connections/{id}/refresh-token" hx-target="#token-info"
|
|
- Session log refresh: hx-get="/api/connections/{id}/sessions" hx-trigger="every 30s" hx-target="#session-log"
|
|
- Revoke session: hx-delete="/api/connections/{id}/sessions/{sessionId}" hx-target="closest .session-item" hx-swap="delete"
|
|
- Block service: hx-post="/api/connections/{id}/block" hx-target="#service-status"
|
|
|
|
SUB-COMPONENTS TO EXTRACT:
|
|
- ServiceHero(info ServiceInfo, status ServiceStatus)
|
|
- ServiceLogo(logoUrl string, name string, color string, size string)
|
|
- ServiceStatusBadge(isVerified bool, isActive bool)
|
|
- UsageStatsGrid(stats UsageStats)
|
|
- StatCard(label string, value string, icon string, trend string)
|
|
- PermissionsCard(permissions []Permission)
|
|
- PermissionItem(permission Permission)
|
|
- PermissionToggle(scope string, enabled bool, description string)
|
|
- ServiceInfoCard(oauth OAuthInfo)
|
|
- OAuthDisplay(clientId string, redirectUris []string)
|
|
- TokenInfo(issuedAt time.Time, expiresAt time.Time)
|
|
- SessionLogCard(sessions []SessionEvent)
|
|
- SessionLogItem(event SessionEvent)
|
|
- DataAccessCard(access DataAccess)
|
|
- DataAccessItem(type string, count int, lastAccessed time.Time)
|
|
|
|
STATE/PROPS:
|
|
type ServiceData struct {
|
|
Info ServiceInfo
|
|
Status ServiceStatus
|
|
Stats UsageStats
|
|
Permissions []Permission
|
|
OAuth OAuthInfo
|
|
Sessions []SessionEvent
|
|
DataAccess DataAccess
|
|
}
|
|
|
|
type ServiceInfo struct {
|
|
ID string
|
|
Name string
|
|
Domain string
|
|
Description string
|
|
LogoURL string
|
|
LogoColor string
|
|
IconName string
|
|
Category string // "defi", "nft", "social", "gaming", "utility"
|
|
Website string
|
|
PrivacyURL string
|
|
TermsURL string
|
|
SupportEmail string
|
|
}
|
|
|
|
type ServiceStatus struct {
|
|
IsVerified bool
|
|
IsActive bool
|
|
IsBlocked bool
|
|
ConnectedAt time.Time
|
|
LastUsed time.Time
|
|
ExpiresAt time.Time
|
|
}
|
|
|
|
type UsageStats struct {
|
|
TotalSessions int
|
|
ActiveSessions int
|
|
SignaturesTotal int
|
|
Signatures30d int
|
|
TxApproved int
|
|
TxRejected int
|
|
PermissionsUsed int
|
|
PermissionsTotal int
|
|
DataRequests int
|
|
}
|
|
|
|
type Permission struct {
|
|
Scope string // "openid", "profile", "wallet:read", "wallet:sign", "wallet:transact"
|
|
Name string
|
|
Description string
|
|
Type string // "required", "optional"
|
|
Enabled bool
|
|
GrantedAt time.Time
|
|
LastUsed time.Time
|
|
UsageCount int
|
|
}
|
|
|
|
type OAuthInfo struct {
|
|
ClientID string
|
|
RedirectURIs []string
|
|
TokenType string // "Bearer"
|
|
IssuedAt time.Time
|
|
ExpiresAt time.Time
|
|
RefreshToken bool
|
|
Scopes []string
|
|
}
|
|
|
|
type SessionEvent struct {
|
|
ID string
|
|
Type string // "auth", "sign", "transaction", "data_request", "token_refresh", "logout"
|
|
Action string
|
|
Timestamp time.Time
|
|
IP string
|
|
Location string
|
|
UserAgent string
|
|
Status string // "success", "failed", "pending"
|
|
Details string
|
|
TxHash string
|
|
}
|
|
|
|
type DataAccess struct {
|
|
ProfileAccessed int
|
|
BalanceChecks int
|
|
TransactionReads int
|
|
NFTReads int
|
|
LastDataRequest time.Time
|
|
}
|
|
|
|
HTMX PATTERNS:
|
|
// Permission toggle
|
|
<wa-switch hx-post="/api/connections/{id}/scopes"
|
|
hx-vals='{"scope": "wallet:sign", "enabled": !this.checked}'
|
|
hx-target="#permission-status"
|
|
hx-swap="innerHTML">
|
|
|
|
// Disconnect service
|
|
<wa-button hx-delete="/api/connections/{id}"
|
|
hx-confirm="Disconnect {service.Name}? This will revoke all permissions and end all sessions."
|
|
hx-target="body"
|
|
hx-push-url="/connections">
|
|
Disconnect
|
|
</wa-button>
|
|
|
|
// Revoke individual session
|
|
<wa-button hx-delete="/api/connections/{id}/sessions/{session.ID}"
|
|
hx-target="closest .session-item"
|
|
hx-swap="delete"
|
|
hx-confirm="End this session?">
|
|
Revoke
|
|
</wa-button>
|
|
|
|
// Session log auto-refresh
|
|
<div id="session-log"
|
|
hx-get="/api/connections/{id}/sessions"
|
|
hx-trigger="every 30s"
|
|
hx-swap="innerHTML">
|
|
|
|
// Reset all permissions to required-only
|
|
<wa-button hx-post="/api/connections/{id}/reset-permissions"
|
|
hx-target="#permissions-list"
|
|
hx-confirm="Reset to minimum permissions?">
|
|
Reset Permissions
|
|
</wa-button>
|
|
================================================================================
|
|
-->
|
|
<html lang="en" class="wa-cloak">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title>Uniswap - Sonr Motr Wallet</title>
|
|
<script src="https://cdn.sonr.org/wa/autoloader.js"></script>
|
|
<style>
|
|
:root {
|
|
--wa-color-primary: #17c2ff;
|
|
--service-color: #ff007a;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
/* Service Hero Section */
|
|
.service-hero {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
justify-content: space-between;
|
|
align-items: flex-start;
|
|
gap: var(--wa-space-xl);
|
|
margin-bottom: var(--wa-space-xl);
|
|
padding: var(--wa-space-xl);
|
|
background: var(--wa-color-surface);
|
|
border-radius: var(--wa-radius-l);
|
|
}
|
|
|
|
.service-identity {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--wa-space-l);
|
|
}
|
|
|
|
.service-logo {
|
|
width: 72px;
|
|
height: 72px;
|
|
border-radius: var(--wa-radius-l);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 32px;
|
|
color: white;
|
|
background: linear-gradient(135deg, var(--service-color) 0%, color-mix(in srgb, var(--service-color) 70%, black) 100%);
|
|
overflow: hidden;
|
|
}
|
|
|
|
.service-logo img {
|
|
width: 100%;
|
|
height: 100%;
|
|
object-fit: cover;
|
|
}
|
|
|
|
.service-name-block {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--wa-space-2xs);
|
|
}
|
|
|
|
.service-badges {
|
|
display: flex;
|
|
gap: var(--wa-space-xs);
|
|
margin-top: var(--wa-space-2xs);
|
|
}
|
|
|
|
.service-description {
|
|
font-size: var(--wa-font-size-s);
|
|
color: var(--wa-color-neutral-600);
|
|
max-width: 400px;
|
|
margin-top: var(--wa-space-s);
|
|
}
|
|
|
|
.status-block {
|
|
text-align: right;
|
|
}
|
|
|
|
.connection-info {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--wa-space-2xs);
|
|
font-size: var(--wa-font-size-xs);
|
|
color: var(--wa-color-neutral-500);
|
|
text-align: right;
|
|
margin-top: var(--wa-space-s);
|
|
}
|
|
|
|
.hero-actions {
|
|
display: flex;
|
|
gap: var(--wa-space-s);
|
|
width: 100%;
|
|
margin-top: var(--wa-space-l);
|
|
padding-top: var(--wa-space-l);
|
|
border-top: 1px solid var(--wa-color-neutral-100);
|
|
}
|
|
|
|
/* Stats Grid */
|
|
.stats-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
|
|
gap: var(--wa-space-m);
|
|
margin-bottom: var(--wa-space-xl);
|
|
}
|
|
|
|
.stat-item {
|
|
background: var(--wa-color-surface);
|
|
padding: var(--wa-space-m);
|
|
border-radius: var(--wa-radius-m);
|
|
}
|
|
|
|
.stat-icon {
|
|
width: 36px;
|
|
height: 36px;
|
|
border-radius: var(--wa-radius-s);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 16px;
|
|
margin-bottom: var(--wa-space-s);
|
|
}
|
|
|
|
.stat-icon.success { background: var(--wa-color-success-subtle); color: var(--wa-color-success); }
|
|
.stat-icon.danger { background: var(--wa-color-danger-subtle); color: var(--wa-color-danger); }
|
|
.stat-icon.warning { background: var(--wa-color-warning-subtle); color: var(--wa-color-warning); }
|
|
.stat-icon.info { background: var(--wa-color-primary-subtle); color: var(--wa-color-primary); }
|
|
.stat-icon.neutral { background: var(--wa-color-neutral-100); color: var(--wa-color-neutral-600); }
|
|
|
|
.stat-label {
|
|
font-size: var(--wa-font-size-xs);
|
|
color: var(--wa-color-neutral-500);
|
|
margin-bottom: var(--wa-space-2xs);
|
|
}
|
|
|
|
.stat-value {
|
|
font-size: var(--wa-font-size-l);
|
|
font-weight: 600;
|
|
}
|
|
|
|
.stat-trend {
|
|
font-size: var(--wa-font-size-xs);
|
|
margin-top: var(--wa-space-2xs);
|
|
}
|
|
|
|
.stat-trend.up { color: var(--wa-color-success); }
|
|
.stat-trend.down { color: var(--wa-color-danger); }
|
|
|
|
/* Content Grid */
|
|
.content-grid {
|
|
display: grid;
|
|
grid-template-columns: 1fr 1fr;
|
|
gap: var(--wa-space-xl);
|
|
margin-bottom: var(--wa-space-xl);
|
|
}
|
|
|
|
@media (max-width: 1024px) {
|
|
.content-grid {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
}
|
|
|
|
/* Permissions Card */
|
|
.permissions-list {
|
|
list-style: none;
|
|
padding: 0;
|
|
margin: 0;
|
|
}
|
|
|
|
.permission-item {
|
|
display: flex;
|
|
align-items: flex-start;
|
|
justify-content: space-between;
|
|
gap: var(--wa-space-m);
|
|
padding: var(--wa-space-m);
|
|
border-bottom: 1px solid var(--wa-color-neutral-100);
|
|
}
|
|
|
|
.permission-item:last-child {
|
|
border-bottom: none;
|
|
}
|
|
|
|
.permission-info {
|
|
flex: 1;
|
|
min-width: 0;
|
|
}
|
|
|
|
.permission-header {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--wa-space-xs);
|
|
margin-bottom: var(--wa-space-2xs);
|
|
}
|
|
|
|
.permission-name {
|
|
font-size: var(--wa-font-size-s);
|
|
font-weight: 500;
|
|
}
|
|
|
|
.permission-scope {
|
|
font-family: var(--wa-font-mono);
|
|
font-size: var(--wa-font-size-xs);
|
|
color: var(--wa-color-neutral-500);
|
|
background: var(--wa-color-surface-alt);
|
|
padding: var(--wa-space-3xs) var(--wa-space-xs);
|
|
border-radius: var(--wa-radius-s);
|
|
}
|
|
|
|
.permission-description {
|
|
font-size: var(--wa-font-size-xs);
|
|
color: var(--wa-color-neutral-600);
|
|
margin-bottom: var(--wa-space-xs);
|
|
}
|
|
|
|
.permission-usage {
|
|
font-size: var(--wa-font-size-xs);
|
|
color: var(--wa-color-neutral-500);
|
|
}
|
|
|
|
/* OAuth Info Card */
|
|
.oauth-display {
|
|
background: var(--wa-color-surface-alt);
|
|
padding: var(--wa-space-m);
|
|
border-radius: var(--wa-radius-m);
|
|
margin-bottom: var(--wa-space-m);
|
|
}
|
|
|
|
.oauth-label {
|
|
font-size: var(--wa-font-size-xs);
|
|
color: var(--wa-color-neutral-500);
|
|
margin-bottom: var(--wa-space-2xs);
|
|
display: block;
|
|
}
|
|
|
|
.oauth-value {
|
|
font-family: var(--wa-font-mono);
|
|
font-size: var(--wa-font-size-xs);
|
|
word-break: break-all;
|
|
}
|
|
|
|
.oauth-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(2, 1fr);
|
|
gap: var(--wa-space-m);
|
|
}
|
|
|
|
.oauth-item {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--wa-space-2xs);
|
|
}
|
|
|
|
.oauth-item-label {
|
|
font-size: var(--wa-font-size-xs);
|
|
color: var(--wa-color-neutral-500);
|
|
}
|
|
|
|
.oauth-item-value {
|
|
font-size: var(--wa-font-size-s);
|
|
font-weight: 500;
|
|
}
|
|
|
|
.redirect-uris {
|
|
list-style: none;
|
|
padding: 0;
|
|
margin: var(--wa-space-s) 0 0 0;
|
|
}
|
|
|
|
.redirect-uris li {
|
|
font-family: var(--wa-font-mono);
|
|
font-size: var(--wa-font-size-xs);
|
|
padding: var(--wa-space-xs);
|
|
background: var(--wa-color-surface-alt);
|
|
border-radius: var(--wa-radius-s);
|
|
margin-bottom: var(--wa-space-xs);
|
|
word-break: break-all;
|
|
}
|
|
|
|
/* Session Log */
|
|
.session-log-list {
|
|
list-style: none;
|
|
padding: 0;
|
|
margin: 0;
|
|
max-height: 400px;
|
|
overflow-y: auto;
|
|
}
|
|
|
|
.session-item {
|
|
display: flex;
|
|
gap: var(--wa-space-m);
|
|
padding: var(--wa-space-m);
|
|
border-bottom: 1px solid var(--wa-color-neutral-100);
|
|
}
|
|
|
|
.session-item:last-child {
|
|
border-bottom: none;
|
|
}
|
|
|
|
.session-icon {
|
|
width: 32px;
|
|
height: 32px;
|
|
border-radius: 50%;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 14px;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.session-icon.auth { background: var(--wa-color-primary-subtle); color: var(--wa-color-primary); }
|
|
.session-icon.sign { background: var(--wa-color-warning-subtle); color: var(--wa-color-warning); }
|
|
.session-icon.transaction { background: var(--wa-color-success-subtle); color: var(--wa-color-success); }
|
|
.session-icon.data { background: var(--wa-color-neutral-100); color: var(--wa-color-neutral-600); }
|
|
.session-icon.error { background: var(--wa-color-danger-subtle); color: var(--wa-color-danger); }
|
|
|
|
.session-content {
|
|
flex: 1;
|
|
min-width: 0;
|
|
}
|
|
|
|
.session-title {
|
|
font-size: var(--wa-font-size-s);
|
|
font-weight: 500;
|
|
margin-bottom: var(--wa-space-3xs);
|
|
}
|
|
|
|
.session-meta {
|
|
font-size: var(--wa-font-size-xs);
|
|
color: var(--wa-color-neutral-500);
|
|
}
|
|
|
|
.session-details {
|
|
font-size: var(--wa-font-size-xs);
|
|
color: var(--wa-color-neutral-600);
|
|
margin-top: var(--wa-space-2xs);
|
|
}
|
|
|
|
.session-tx-hash {
|
|
font-family: var(--wa-font-mono);
|
|
font-size: var(--wa-font-size-xs);
|
|
color: var(--wa-color-primary);
|
|
text-decoration: none;
|
|
}
|
|
|
|
.session-tx-hash:hover {
|
|
text-decoration: underline;
|
|
}
|
|
|
|
/* Data Access Card */
|
|
.data-access-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
|
|
gap: var(--wa-space-m);
|
|
}
|
|
|
|
.data-access-item {
|
|
background: var(--wa-color-surface-alt);
|
|
padding: var(--wa-space-m);
|
|
border-radius: var(--wa-radius-m);
|
|
text-align: center;
|
|
}
|
|
|
|
.data-access-count {
|
|
font-size: var(--wa-font-size-xl);
|
|
font-weight: 700;
|
|
color: var(--wa-color-neutral-700);
|
|
}
|
|
|
|
.data-access-label {
|
|
font-size: var(--wa-font-size-xs);
|
|
color: var(--wa-color-neutral-500);
|
|
margin-top: var(--wa-space-2xs);
|
|
}
|
|
|
|
/* Back link */
|
|
.back-link {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: var(--wa-space-xs);
|
|
color: var(--wa-color-neutral-500);
|
|
text-decoration: none;
|
|
font-size: var(--wa-font-size-s);
|
|
margin-bottom: var(--wa-space-m);
|
|
transition: color 0.15s;
|
|
}
|
|
|
|
.back-link:hover {
|
|
color: var(--wa-color-primary);
|
|
}
|
|
|
|
/* Resource links */
|
|
.resource-links {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: var(--wa-space-s);
|
|
margin-top: var(--wa-space-m);
|
|
}
|
|
|
|
.resource-link {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: var(--wa-space-2xs);
|
|
padding: var(--wa-space-xs) var(--wa-space-s);
|
|
background: var(--wa-color-surface-alt);
|
|
border-radius: var(--wa-radius-s);
|
|
font-size: var(--wa-font-size-xs);
|
|
color: var(--wa-color-neutral-600);
|
|
text-decoration: none;
|
|
transition: background 0.15s, color 0.15s;
|
|
}
|
|
|
|
.resource-link:hover {
|
|
background: var(--wa-color-primary-subtle);
|
|
color: var(--wa-color-primary);
|
|
}
|
|
|
|
/* Filter tabs */
|
|
.filter-tabs {
|
|
margin-bottom: var(--wa-space-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" class="active">
|
|
<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">
|
|
<a href="connections.html" class="back-link">
|
|
<wa-icon name="arrow-left"></wa-icon>
|
|
Back to Connections
|
|
</a>
|
|
|
|
<!-- Service Hero -->
|
|
<div class="service-hero">
|
|
<div class="service-identity">
|
|
<div class="service-logo" id="service-logo">
|
|
<wa-icon name="arrow-right-arrow-left"></wa-icon>
|
|
</div>
|
|
<div class="service-name-block">
|
|
<div class="wa-cluster wa-gap-s wa-align-items-center">
|
|
<span class="wa-heading-2xl" id="service-name">Uniswap</span>
|
|
</div>
|
|
<span class="wa-caption-m" style="color: var(--wa-color-neutral-500);" id="service-domain">app.uniswap.org</span>
|
|
<div class="service-badges">
|
|
<wa-badge variant="success" pill id="verified-badge">
|
|
<wa-icon name="badge-check"></wa-icon>
|
|
Verified
|
|
</wa-badge>
|
|
<wa-badge variant="primary" pill id="status-badge">
|
|
<wa-icon name="circle" variant="solid" style="font-size: 6px;"></wa-icon>
|
|
Active
|
|
</wa-badge>
|
|
<wa-badge variant="neutral" pill>
|
|
<wa-icon name="coins"></wa-icon>
|
|
DeFi
|
|
</wa-badge>
|
|
</div>
|
|
<p class="service-description">
|
|
Uniswap is a decentralized exchange protocol that allows users to swap ERC-20 tokens without intermediaries. Trade, earn, and build on the leading decentralized crypto trading protocol.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="status-block">
|
|
<wa-badge variant="success" size="large">Connected</wa-badge>
|
|
<div class="connection-info">
|
|
<span>Connected: <strong>Dec 20, 2024</strong></span>
|
|
<span>Last used: <strong>2 hours ago</strong></span>
|
|
<span>Expires: <strong>Never</strong></span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="hero-actions">
|
|
<wa-button variant="neutral" appearance="outlined" id="visit-site-btn" onclick="window.open('https://app.uniswap.org', '_blank')">
|
|
<wa-icon slot="start" name="arrow-up-right-from-square"></wa-icon>
|
|
Visit Site
|
|
</wa-button>
|
|
<wa-button variant="neutral" appearance="outlined" id="refresh-token-btn">
|
|
<wa-icon slot="start" name="rotate"></wa-icon>
|
|
Refresh Token
|
|
</wa-button>
|
|
<div style="flex: 1;"></div>
|
|
<wa-button variant="danger" appearance="outlined" id="disconnect-btn">
|
|
<wa-icon slot="start" name="plug-circle-xmark"></wa-icon>
|
|
Disconnect
|
|
</wa-button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Stats Grid -->
|
|
<div class="stats-grid">
|
|
<div class="stat-item">
|
|
<div class="stat-icon info">
|
|
<wa-icon name="clock"></wa-icon>
|
|
</div>
|
|
<div class="stat-label">Active Sessions</div>
|
|
<div class="stat-value">3</div>
|
|
</div>
|
|
|
|
<div class="stat-item">
|
|
<div class="stat-icon warning">
|
|
<wa-icon name="signature"></wa-icon>
|
|
</div>
|
|
<div class="stat-label">Signatures (30d)</div>
|
|
<div class="stat-value">47</div>
|
|
<div class="stat-trend up">
|
|
<wa-icon name="arrow-trend-up" style="font-size: 10px;"></wa-icon>
|
|
+12% vs last month
|
|
</div>
|
|
</div>
|
|
|
|
<div class="stat-item">
|
|
<div class="stat-icon success">
|
|
<wa-icon name="check"></wa-icon>
|
|
</div>
|
|
<div class="stat-label">TX Approved</div>
|
|
<div class="stat-value">89</div>
|
|
</div>
|
|
|
|
<div class="stat-item">
|
|
<div class="stat-icon danger">
|
|
<wa-icon name="xmark"></wa-icon>
|
|
</div>
|
|
<div class="stat-label">TX Rejected</div>
|
|
<div class="stat-value">3</div>
|
|
</div>
|
|
|
|
<div class="stat-item">
|
|
<div class="stat-icon neutral">
|
|
<wa-icon name="shield-check"></wa-icon>
|
|
</div>
|
|
<div class="stat-label">Permissions</div>
|
|
<div class="stat-value">4 / 6</div>
|
|
</div>
|
|
|
|
<div class="stat-item">
|
|
<div class="stat-icon info">
|
|
<wa-icon name="database"></wa-icon>
|
|
</div>
|
|
<div class="stat-label">Data Requests</div>
|
|
<div class="stat-value">234</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Permissions & OAuth Info Grid -->
|
|
<div class="content-grid">
|
|
<!-- Permissions Card -->
|
|
<wa-card>
|
|
<div slot="header" class="wa-flank">
|
|
<span class="wa-heading-m">Permissions</span>
|
|
<wa-button appearance="plain" size="small" id="reset-permissions-btn">Reset to Default</wa-button>
|
|
</div>
|
|
|
|
<ul class="permissions-list" id="permissions-list">
|
|
<li class="permission-item">
|
|
<div class="permission-info">
|
|
<div class="permission-header">
|
|
<span class="permission-name">View Profile</span>
|
|
<wa-badge variant="neutral" size="small">Required</wa-badge>
|
|
</div>
|
|
<span class="permission-scope">openid profile</span>
|
|
<p class="permission-description">Access your public profile information including display name and avatar.</p>
|
|
<div class="permission-usage">Used 45 times · Last used 2 hours ago</div>
|
|
</div>
|
|
<wa-switch checked disabled></wa-switch>
|
|
</li>
|
|
|
|
<li class="permission-item">
|
|
<div class="permission-info">
|
|
<div class="permission-header">
|
|
<span class="permission-name">View Wallet Balance</span>
|
|
<wa-badge variant="neutral" size="small">Required</wa-badge>
|
|
</div>
|
|
<span class="permission-scope">wallet:read</span>
|
|
<p class="permission-description">View your wallet balances and transaction history.</p>
|
|
<div class="permission-usage">Used 189 times · Last used 2 hours ago</div>
|
|
</div>
|
|
<wa-switch checked disabled></wa-switch>
|
|
</li>
|
|
|
|
<li class="permission-item">
|
|
<div class="permission-info">
|
|
<div class="permission-header">
|
|
<span class="permission-name">Request Signatures</span>
|
|
<wa-badge variant="primary" size="small">Optional</wa-badge>
|
|
</div>
|
|
<span class="permission-scope">wallet:sign</span>
|
|
<p class="permission-description">Request message signatures for authentication and verification.</p>
|
|
<div class="permission-usage">Used 47 times · Last used 5 hours ago</div>
|
|
</div>
|
|
<wa-switch checked id="perm-sign"></wa-switch>
|
|
</li>
|
|
|
|
<li class="permission-item">
|
|
<div class="permission-info">
|
|
<div class="permission-header">
|
|
<span class="permission-name">Submit Transactions</span>
|
|
<wa-badge variant="primary" size="small">Optional</wa-badge>
|
|
</div>
|
|
<span class="permission-scope">wallet:transact</span>
|
|
<p class="permission-description">Request approval to submit transactions on your behalf. Each transaction requires explicit approval.</p>
|
|
<div class="permission-usage">Used 92 times · Last used 1 day ago</div>
|
|
</div>
|
|
<wa-switch checked id="perm-transact"></wa-switch>
|
|
</li>
|
|
|
|
<li class="permission-item">
|
|
<div class="permission-info">
|
|
<div class="permission-header">
|
|
<span class="permission-name">View NFTs</span>
|
|
<wa-badge variant="primary" size="small">Optional</wa-badge>
|
|
</div>
|
|
<span class="permission-scope">wallet:nfts:read</span>
|
|
<p class="permission-description">View your NFT collection and metadata.</p>
|
|
<div class="permission-usage">Never used</div>
|
|
</div>
|
|
<wa-switch id="perm-nfts"></wa-switch>
|
|
</li>
|
|
|
|
<li class="permission-item">
|
|
<div class="permission-info">
|
|
<div class="permission-header">
|
|
<span class="permission-name">Notifications</span>
|
|
<wa-badge variant="primary" size="small">Optional</wa-badge>
|
|
</div>
|
|
<span class="permission-scope">notifications</span>
|
|
<p class="permission-description">Send you notifications about swaps, price alerts, and governance proposals.</p>
|
|
<div class="permission-usage">Never used</div>
|
|
</div>
|
|
<wa-switch id="perm-notifications"></wa-switch>
|
|
</li>
|
|
</ul>
|
|
|
|
<div slot="footer" id="permission-status"></div>
|
|
</wa-card>
|
|
|
|
<!-- OAuth Info Card -->
|
|
<wa-card>
|
|
<div slot="header">
|
|
<span class="wa-heading-m">Connection Details</span>
|
|
</div>
|
|
|
|
<div class="oauth-display">
|
|
<span class="oauth-label">Client ID</span>
|
|
<div class="oauth-value" id="client-id">uniswap_0x7a2b4c9d8e3f1a5b6c7d8e9f0a1b2c3d</div>
|
|
</div>
|
|
|
|
<div class="oauth-grid">
|
|
<div class="oauth-item">
|
|
<span class="oauth-item-label">Token Type</span>
|
|
<span class="oauth-item-value">Bearer</span>
|
|
</div>
|
|
<div class="oauth-item">
|
|
<span class="oauth-item-label">Issued At</span>
|
|
<span class="oauth-item-value">Dec 20, 2024</span>
|
|
</div>
|
|
<div class="oauth-item">
|
|
<span class="oauth-item-label">Expires</span>
|
|
<span class="oauth-item-value">Never (Refresh Token)</span>
|
|
</div>
|
|
<div class="oauth-item">
|
|
<span class="oauth-item-label">Granted Scopes</span>
|
|
<span class="oauth-item-value">4 of 6</span>
|
|
</div>
|
|
</div>
|
|
|
|
<wa-divider style="margin: var(--wa-space-m) 0;"></wa-divider>
|
|
|
|
<div class="wa-stack wa-gap-s">
|
|
<span class="wa-caption-s" style="color: var(--wa-color-neutral-500);">Redirect URIs</span>
|
|
<ul class="redirect-uris">
|
|
<li>https://app.uniswap.org/callback</li>
|
|
<li>https://app.uniswap.org/auth/callback</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<wa-divider style="margin: var(--wa-space-m) 0;"></wa-divider>
|
|
|
|
<div class="resource-links">
|
|
<a href="https://uniswap.org" target="_blank" class="resource-link">
|
|
<wa-icon name="globe"></wa-icon>
|
|
Website
|
|
</a>
|
|
<a href="https://uniswap.org/privacy-policy" target="_blank" class="resource-link">
|
|
<wa-icon name="shield-halved"></wa-icon>
|
|
Privacy Policy
|
|
</a>
|
|
<a href="https://uniswap.org/terms-of-service" target="_blank" class="resource-link">
|
|
<wa-icon name="file-contract"></wa-icon>
|
|
Terms of Service
|
|
</a>
|
|
<a href="mailto:support@uniswap.org" class="resource-link">
|
|
<wa-icon name="envelope"></wa-icon>
|
|
Support
|
|
</a>
|
|
</div>
|
|
</wa-card>
|
|
</div>
|
|
|
|
<!-- Session Log -->
|
|
<wa-card style="margin-bottom: var(--wa-space-xl);">
|
|
<div slot="header" class="wa-flank">
|
|
<span class="wa-heading-m">Activity Log</span>
|
|
<div class="wa-cluster wa-gap-s">
|
|
<wa-button appearance="plain" size="small" id="refresh-log-btn">
|
|
<wa-icon name="rotate"></wa-icon>
|
|
</wa-button>
|
|
<wa-button appearance="plain" size="small">Export</wa-button>
|
|
</div>
|
|
</div>
|
|
|
|
<wa-radio-group value="all" orientation="horizontal" class="filter-tabs" id="log-filter">
|
|
<wa-radio appearance="button" value="all">All</wa-radio>
|
|
<wa-radio appearance="button" value="auth">Auth</wa-radio>
|
|
<wa-radio appearance="button" value="signatures">Signatures</wa-radio>
|
|
<wa-radio appearance="button" value="transactions">Transactions</wa-radio>
|
|
</wa-radio-group>
|
|
|
|
<ul class="session-log-list" id="session-log">
|
|
<li class="session-item">
|
|
<div class="session-icon auth">
|
|
<wa-icon name="key"></wa-icon>
|
|
</div>
|
|
<div class="session-content">
|
|
<div class="session-title">Session started</div>
|
|
<div class="session-meta">2 hours ago · 192.168.1.42 · San Francisco, CA</div>
|
|
<div class="session-details">Chrome 120 on macOS</div>
|
|
</div>
|
|
<wa-badge variant="success" size="small">Active</wa-badge>
|
|
</li>
|
|
|
|
<li class="session-item">
|
|
<div class="session-icon sign">
|
|
<wa-icon name="signature"></wa-icon>
|
|
</div>
|
|
<div class="session-content">
|
|
<div class="session-title">Message signed</div>
|
|
<div class="session-meta">2 hours ago · 192.168.1.42</div>
|
|
<div class="session-details">Signed login message for Uniswap</div>
|
|
</div>
|
|
<wa-badge variant="success" size="small">Success</wa-badge>
|
|
</li>
|
|
|
|
<li class="session-item">
|
|
<div class="session-icon transaction">
|
|
<wa-icon name="arrow-right-arrow-left"></wa-icon>
|
|
</div>
|
|
<div class="session-content">
|
|
<div class="session-title">Swap executed</div>
|
|
<div class="session-meta">5 hours ago · 192.168.1.42</div>
|
|
<div class="session-details">
|
|
Swapped 0.5 ETH → 1,245.32 USDC
|
|
<br>
|
|
<a href="#" class="session-tx-hash">0x7a2b4c9d...8e3f1a5b</a>
|
|
</div>
|
|
</div>
|
|
<wa-badge variant="success" size="small">Confirmed</wa-badge>
|
|
</li>
|
|
|
|
<li class="session-item">
|
|
<div class="session-icon data">
|
|
<wa-icon name="database"></wa-icon>
|
|
</div>
|
|
<div class="session-content">
|
|
<div class="session-title">Balance checked</div>
|
|
<div class="session-meta">5 hours ago · 192.168.1.42</div>
|
|
<div class="session-details">Read wallet balances for swap interface</div>
|
|
</div>
|
|
</li>
|
|
|
|
<li class="session-item">
|
|
<div class="session-icon transaction">
|
|
<wa-icon name="arrow-right-arrow-left"></wa-icon>
|
|
</div>
|
|
<div class="session-content">
|
|
<div class="session-title">Swap executed</div>
|
|
<div class="session-meta">1 day ago · 192.168.1.42</div>
|
|
<div class="session-details">
|
|
Swapped 100 USDC → 0.042 ETH
|
|
<br>
|
|
<a href="#" class="session-tx-hash">0x9c8d7e6f...2a1b0c9d</a>
|
|
</div>
|
|
</div>
|
|
<wa-badge variant="success" size="small">Confirmed</wa-badge>
|
|
</li>
|
|
|
|
<li class="session-item">
|
|
<div class="session-icon error">
|
|
<wa-icon name="xmark"></wa-icon>
|
|
</div>
|
|
<div class="session-content">
|
|
<div class="session-title">Transaction rejected</div>
|
|
<div class="session-meta">2 days ago · 192.168.1.42</div>
|
|
<div class="session-details">User declined swap request: 1 ETH → USDT</div>
|
|
</div>
|
|
<wa-badge variant="danger" size="small">Rejected</wa-badge>
|
|
</li>
|
|
|
|
<li class="session-item">
|
|
<div class="session-icon sign">
|
|
<wa-icon name="signature"></wa-icon>
|
|
</div>
|
|
<div class="session-content">
|
|
<div class="session-title">Permit signature</div>
|
|
<div class="session-meta">3 days ago · 192.168.1.42</div>
|
|
<div class="session-details">Signed EIP-2612 permit for USDC spending</div>
|
|
</div>
|
|
<wa-badge variant="success" size="small">Success</wa-badge>
|
|
</li>
|
|
|
|
<li class="session-item">
|
|
<div class="session-icon auth">
|
|
<wa-icon name="plug"></wa-icon>
|
|
</div>
|
|
<div class="session-content">
|
|
<div class="session-title">App connected</div>
|
|
<div class="session-meta">Dec 20, 2024 · 192.168.1.42 · San Francisco, CA</div>
|
|
<div class="session-details">Initial connection with 4 permissions granted</div>
|
|
</div>
|
|
<wa-badge variant="primary" size="small">Connected</wa-badge>
|
|
</li>
|
|
</ul>
|
|
|
|
<div slot="footer">
|
|
<a href="#" class="wa-cluster wa-gap-xs wa-caption-s" style="color: var(--wa-color-primary);">
|
|
<span>View full history</span>
|
|
<wa-icon name="arrow-right"></wa-icon>
|
|
</a>
|
|
</div>
|
|
</wa-card>
|
|
|
|
<!-- Data Access Summary -->
|
|
<wa-card>
|
|
<div slot="header">
|
|
<span class="wa-heading-m">Data Access Summary</span>
|
|
</div>
|
|
|
|
<div class="data-access-grid">
|
|
<div class="data-access-item">
|
|
<div class="data-access-count">45</div>
|
|
<div class="data-access-label">Profile Reads</div>
|
|
</div>
|
|
<div class="data-access-item">
|
|
<div class="data-access-count">189</div>
|
|
<div class="data-access-label">Balance Checks</div>
|
|
</div>
|
|
<div class="data-access-item">
|
|
<div class="data-access-count">56</div>
|
|
<div class="data-access-label">Transaction Reads</div>
|
|
</div>
|
|
<div class="data-access-item">
|
|
<div class="data-access-count">0</div>
|
|
<div class="data-access-label">NFT Reads</div>
|
|
</div>
|
|
<div class="data-access-item">
|
|
<div class="data-access-count">47</div>
|
|
<div class="data-access-label">Signature Requests</div>
|
|
</div>
|
|
<div class="data-access-item">
|
|
<div class="data-access-count">92</div>
|
|
<div class="data-access-label">TX Requests</div>
|
|
</div>
|
|
</div>
|
|
|
|
<wa-divider style="margin: var(--wa-space-m) 0;"></wa-divider>
|
|
|
|
<div class="wa-cluster wa-gap-m wa-caption-s" style="color: var(--wa-color-neutral-500);">
|
|
<span>Last data request: <strong>2 hours ago</strong></span>
|
|
<span>Total requests: <strong>429</strong></span>
|
|
</div>
|
|
</wa-card>
|
|
</main>
|
|
</div>
|
|
</wa-page>
|
|
|
|
<!-- Disconnect Confirmation Dialog -->
|
|
<wa-dialog id="disconnect-dialog" label="Disconnect Uniswap">
|
|
<div class="wa-stack wa-gap-m">
|
|
<wa-callout variant="warning">
|
|
<wa-icon slot="icon" name="triangle-exclamation"></wa-icon>
|
|
<strong>This will revoke all access</strong>
|
|
<p style="margin: var(--wa-space-xs) 0 0 0;">
|
|
Uniswap will no longer be able to view your wallet or request signatures and transactions.
|
|
</p>
|
|
</wa-callout>
|
|
<p class="wa-caption-s">
|
|
You can reconnect at any time by visiting Uniswap and authorizing again. Your transaction history will be preserved.
|
|
</p>
|
|
</div>
|
|
<div slot="footer" class="wa-cluster wa-gap-s wa-justify-content-end">
|
|
<wa-button variant="neutral" appearance="outlined" id="disconnect-cancel-btn">Cancel</wa-button>
|
|
<wa-button variant="danger" id="disconnect-confirm-btn">Disconnect</wa-button>
|
|
</div>
|
|
</wa-dialog>
|
|
|
|
<!-- Reset Permissions Dialog -->
|
|
<wa-dialog id="reset-dialog" label="Reset Permissions">
|
|
<div class="wa-stack wa-gap-m">
|
|
<p class="wa-caption-s">
|
|
This will revoke all optional permissions and keep only the required ones (profile and wallet:read).
|
|
</p>
|
|
<p class="wa-caption-s" style="color: var(--wa-color-neutral-500);">
|
|
The following permissions will be revoked:
|
|
</p>
|
|
<ul style="margin: 0; padding-left: var(--wa-space-l); font-size: var(--wa-font-size-xs); color: var(--wa-color-neutral-600);">
|
|
<li>Request Signatures (wallet:sign)</li>
|
|
<li>Submit Transactions (wallet:transact)</li>
|
|
</ul>
|
|
</div>
|
|
<div slot="footer" class="wa-cluster wa-gap-s wa-justify-content-end">
|
|
<wa-button variant="neutral" appearance="outlined" id="reset-cancel-btn">Cancel</wa-button>
|
|
<wa-button variant="warning" id="reset-confirm-btn">Reset Permissions</wa-button>
|
|
</div>
|
|
</wa-dialog>
|
|
|
|
<script>
|
|
// Disconnect dialog handlers
|
|
const disconnectDialog = document.getElementById('disconnect-dialog');
|
|
const disconnectBtn = document.getElementById('disconnect-btn');
|
|
const disconnectCancelBtn = document.getElementById('disconnect-cancel-btn');
|
|
const disconnectConfirmBtn = document.getElementById('disconnect-confirm-btn');
|
|
|
|
disconnectBtn?.addEventListener('click', () => {
|
|
disconnectDialog.open = true;
|
|
});
|
|
|
|
disconnectCancelBtn?.addEventListener('click', () => {
|
|
disconnectDialog.open = false;
|
|
});
|
|
|
|
disconnectConfirmBtn?.addEventListener('click', () => {
|
|
window.location.href = 'connections.html';
|
|
});
|
|
|
|
// Reset permissions dialog handlers
|
|
const resetDialog = document.getElementById('reset-dialog');
|
|
const resetBtn = document.getElementById('reset-permissions-btn');
|
|
const resetCancelBtn = document.getElementById('reset-cancel-btn');
|
|
const resetConfirmBtn = document.getElementById('reset-confirm-btn');
|
|
|
|
resetBtn?.addEventListener('click', () => {
|
|
resetDialog.open = true;
|
|
});
|
|
|
|
resetCancelBtn?.addEventListener('click', () => {
|
|
resetDialog.open = false;
|
|
});
|
|
|
|
resetConfirmBtn?.addEventListener('click', () => {
|
|
document.getElementById('perm-sign').checked = false;
|
|
document.getElementById('perm-transact').checked = false;
|
|
document.getElementById('perm-nfts').checked = false;
|
|
document.getElementById('perm-notifications').checked = false;
|
|
resetDialog.open = false;
|
|
});
|
|
|
|
// Permission toggles
|
|
document.querySelectorAll('.permission-item wa-switch:not([disabled])').forEach(toggle => {
|
|
toggle.addEventListener('wa-change', function() {
|
|
const status = document.getElementById('permission-status');
|
|
status.innerHTML = `
|
|
<wa-callout variant="success" style="margin-top: var(--wa-space-s);">
|
|
<wa-icon slot="icon" name="check"></wa-icon>
|
|
Permission ${this.checked ? 'granted' : 'revoked'} successfully
|
|
</wa-callout>
|
|
`;
|
|
setTimeout(() => {
|
|
status.innerHTML = '';
|
|
}, 3000);
|
|
});
|
|
});
|
|
|
|
// Refresh token handler
|
|
const refreshTokenBtn = document.getElementById('refresh-token-btn');
|
|
refreshTokenBtn?.addEventListener('click', function() {
|
|
const icon = this.querySelector('wa-icon');
|
|
icon.style.animation = 'spin 0.5s linear';
|
|
setTimeout(() => {
|
|
icon.style.animation = '';
|
|
}, 500);
|
|
});
|
|
|
|
// Refresh log handler
|
|
const refreshLogBtn = document.getElementById('refresh-log-btn');
|
|
refreshLogBtn?.addEventListener('click', function() {
|
|
const icon = this.querySelector('wa-icon');
|
|
icon.style.animation = 'spin 0.5s linear';
|
|
setTimeout(() => {
|
|
icon.style.animation = '';
|
|
}, 500);
|
|
});
|
|
|
|
// Log filter
|
|
document.getElementById('log-filter')?.addEventListener('wa-change', function(e) {
|
|
console.log('Filter changed to:', e.target.value);
|
|
});
|
|
|
|
// Sidebar navigation
|
|
document.querySelectorAll('.sidebar-nav a').forEach(link => {
|
|
link.addEventListener('click', function(e) {
|
|
if (this.getAttribute('href') === '#') {
|
|
e.preventDefault();
|
|
}
|
|
});
|
|
});
|
|
|
|
// Add spin animation
|
|
const style = document.createElement('style');
|
|
style.textContent = `
|
|
@keyframes spin {
|
|
from { transform: rotate(0deg); }
|
|
to { transform: rotate(360deg); }
|
|
}
|
|
`;
|
|
document.head.appendChild(style);
|
|
</script>
|
|
</body>
|
|
</html>
|