amanpay / web /src /pages /ActivityPage.tsx
MHamdan's picture
CI deploy 895c0ed
adb91eb verified
Raw
History Blame Contribute Delete
3.49 kB
import { useEffect, useState } from 'react'
import { useI18n } from '../i18n'
import { Callout } from '../components/ui'
import { listActivity, subscribeActivity, type ActivityEvent, type ActivityKind } from '../activity/store'
import { formatDateTime } from '../utils/format'
const TABS: { key: string; kind?: ActivityKind; label: string }[] = [
{ key: 'all', label: 'activity.tab.all' },
{ key: 'payments', kind: 'payment', label: 'activity.tab.payments' },
{ key: 'confirmations', kind: 'confirmation', label: 'activity.tab.confirmations' },
{ key: 'biometrics', kind: 'biometrics', label: 'activity.tab.biometrics' },
{ key: 'security', kind: 'security-demo', label: 'activity.tab.security' },
]
const KIND_LABEL: Record<ActivityKind, string> = {
payment: 'activity.kind.payment', confirmation: 'activity.kind.confirmation',
biometrics: 'activity.kind.biometrics', 'security-demo': 'activity.kind.securityDemo',
}
/** Session-only activity. Explicitly labelled as NOT permanent account history. Never renders
* raw biometrics, full IBANs, capabilities, signatures or thresholds. */
export function ActivityPage() {
const { t, locale } = useI18n()
const [tab, setTab] = useState('all')
const [, force] = useState(0)
useEffect(() => subscribeActivity(() => force((n) => n + 1)), [])
const kind = TABS.find((x) => x.key === tab)?.kind
const events: ActivityEvent[] = listActivity(kind)
return (
<section className="page activity" aria-labelledby="act-h">
<div className="row-between">
<h2 id="act-h">{t('activity.title')}</h2>
<span className="badge tone-info">{t('activity.sessionOnly')}</span>
</div>
<p className="muted small">{t('activity.sessionNote')}</p>
<div className="tabs" role="tablist" aria-label={t('activity.title')}>
{TABS.map((x) => (
<button key={x.key} role="tab" aria-selected={tab === x.key}
className={`tab ${tab === x.key ? 'active' : ''}`} onClick={() => setTab(x.key)}>
{t(x.label as never)}
</button>
))}
</div>
{events.length === 0 ? (
<Callout tone="info"><span data-testid="activity-empty">{t('activity.empty')}</span></Callout>
) : (
<ul className="activity-list">
{events.map((e) => (
<li key={e.id} className="activity-item" data-testid="activity-item">
<div className="row-between">
<span className="badge tone-info">{t(KIND_LABEL[e.kind] as never)}</span>
<span className="muted small">{formatDateTime(e.ts / 1000, locale)}</span>
</div>
<div className="row-between">
<strong>{e.title}</strong>
<span className="badge tone-info">{e.status}</span>
</div>
{e.amount && <div className="muted small">{e.amount}</div>}
{e.reference && <div className="muted small mono">{e.reference}</div>}
{e.reasonCodes && e.reasonCodes.length > 0 && (
<div className="muted small">{e.reasonCodes.join(', ')}</div>
)}
{typeof e.auditVerified === 'boolean' && (
<span className={`badge ${e.auditVerified ? 'tone-ok' : 'tone-bad'}`}>
{e.auditVerified ? t('agentic.result.auditVerified') : t('agentic.result.auditFailed')}
</span>
)}
</li>
))}
</ul>
)}
</section>
)
}