Skip to content

Commit 18293aa

Browse files
authored
Merge pull request #412 from johnsmccain/master
Improve Frontend Accessibility, Internationalization, and Wallet-Submission UX
2 parents fd50ca3 + 44b397f commit 18293aa

7 files changed

Lines changed: 278 additions & 50 deletions

File tree

CHANGELOG.md

Lines changed: 87 additions & 0 deletions
Large diffs are not rendered by default.

frontend/src/app/create/create-page-client.tsx

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -336,7 +336,7 @@ export default function CreatePolicyPageClient() {
336336
const [isEstimating, setIsEstimating] = useState(false);
337337
const [estimationError, setEstimationError] = useState(false);
338338
const [validationErrors, setValidationErrors] = useState<ValidationError[]>([]);
339-
const { isConnected, message: walletMessage, status: walletStatus } = useWallet();
339+
const { isConnected, message: walletMessage, status: walletStatus, connect } = useWallet();
340340

341341
function updateDraft<K extends keyof PolicyDraft>(field: K, value: PolicyDraft[K]) {
342342
setDraft({ ...draft, [field]: value });
@@ -753,15 +753,44 @@ export default function CreatePolicyPageClient() {
753753
</div>
754754

755755
{!isWalletReady ? (
756-
<p className="form-status" role="status" aria-live="polite">
757-
{walletStatus === "unsupported"
758-
? "Wallet not supported in this browser. Install a compatible wallet extension to continue."
759-
: walletStatus === "checking"
760-
? "Checking wallet availability..."
761-
: walletStatus === "connecting"
762-
? "Complete wallet connection to submit this policy."
763-
: walletMessage}
764-
</p>
756+
<div className="wallet-readiness-alert" role="alert" aria-live="polite">
757+
<div className="wallet-readiness-alert__icon" aria-hidden="true">
758+
<Icon name="alert-circle" size="md" tone="warning" />
759+
</div>
760+
<div className="wallet-readiness-alert__content">
761+
<h3 className="wallet-readiness-alert__title">
762+
{walletStatus === "unsupported"
763+
? "Wallet Not Supported"
764+
: walletStatus === "checking"
765+
? "Checking Wallet Availability"
766+
: walletStatus === "connecting"
767+
? "Connecting Wallet"
768+
: "Wallet Connection Required"}
769+
</h3>
770+
<p className="wallet-readiness-alert__message">
771+
{walletStatus === "unsupported"
772+
? "Install a compatible wallet extension (Freighter, Lobstr, or xBull) to continue."
773+
: walletStatus === "checking"
774+
? "Verifying wallet availability..."
775+
: walletStatus === "connecting"
776+
? "Please approve the connection request in your wallet extension."
777+
: walletMessage || "Connect your wallet to sign and submit this policy."}
778+
</p>
779+
{walletStatus === "disconnected" || walletStatus === "error" ? (
780+
<div className="wallet-readiness-alert__actions">
781+
<button
782+
className="cta-primary cta-primary--small"
783+
type="button"
784+
onClick={connect}
785+
aria-label="Connect wallet to continue"
786+
>
787+
<Icon name="wallet" size="sm" aria-hidden="true" />
788+
Connect Wallet
789+
</button>
790+
</div>
791+
) : null}
792+
</div>
793+
</div>
765794
) : null}
766795
</section>
767796
)}

frontend/src/app/globals.css

Lines changed: 53 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,7 @@ h1 {
434434
var(--text),
435435
var(--accent-strong)
436436
);
437+
background-clip: text;
437438
-webkit-background-clip: text;
438439
-webkit-text-fill-color: transparent;
439440
}
@@ -814,10 +815,6 @@ dt,
814815
vertical-align: middle;
815816
}
816817

817-
.tx-row {
818-
cursor: pointer;
819-
}
820-
821818
.tx-row:hover {
822819
background: rgba(16, 37, 66, 0.02);
823820
}
@@ -1141,6 +1138,58 @@ dt,
11411138
margin: 0;
11421139
}
11431140

1141+
.wallet-readiness-alert {
1142+
display: grid;
1143+
grid-template-columns: auto 1fr;
1144+
gap: var(--space-3);
1145+
padding: var(--space-4);
1146+
margin-top: var(--space-4);
1147+
border: 1px solid var(--warning-strong);
1148+
border-radius: var(--radius-md);
1149+
background: var(--warning-soft);
1150+
}
1151+
1152+
.wallet-readiness-alert__icon {
1153+
display: inline-flex;
1154+
align-items: center;
1155+
justify-content: center;
1156+
width: 2.5rem;
1157+
height: 2.5rem;
1158+
border-radius: var(--radius-pill);
1159+
background: rgba(176, 107, 0, 0.12);
1160+
color: var(--warning-strong);
1161+
}
1162+
1163+
.wallet-readiness-alert__content {
1164+
display: grid;
1165+
gap: var(--space-2);
1166+
}
1167+
1168+
.wallet-readiness-alert__title {
1169+
margin: 0;
1170+
font-size: var(--step-1);
1171+
font-weight: 700;
1172+
color: var(--warning-strong);
1173+
}
1174+
1175+
.wallet-readiness-alert__message {
1176+
margin: 0;
1177+
color: var(--text-muted);
1178+
font-size: var(--step-0);
1179+
}
1180+
1181+
.wallet-readiness-alert__actions {
1182+
display: flex;
1183+
gap: var(--space-2);
1184+
margin-top: var(--space-1);
1185+
}
1186+
1187+
.cta-primary--small {
1188+
min-height: 2.5rem;
1189+
padding: 0.6rem 1rem;
1190+
font-size: var(--step-0);
1191+
}
1192+
11441193
.ob-backdrop {
11451194
position: fixed;
11461195
inset: 0;

frontend/src/app/history/history-page-client.tsx

Lines changed: 32 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -211,9 +211,9 @@ export default function TransactionHistoryPage() {
211211
onChange={handleTypeChange}
212212
>
213213
<option value="all">{t('history.filters.allTypes')}</option>
214-
<option value="premium">Premium</option>
215-
<option value="payout">Payout</option>
216-
<option value="refund">Refund</option>
214+
<option value="premium">{t('history.filters.types.premium')}</option>
215+
<option value="payout">{t('history.filters.types.payout')}</option>
216+
<option value="refund">{t('history.filters.types.refund')}</option>
217217
</select>
218218
</div>
219219

@@ -228,9 +228,9 @@ export default function TransactionHistoryPage() {
228228
onChange={handleStatusChange}
229229
>
230230
<option value="all">{t('history.filters.allStatuses')}</option>
231-
<option value="successful">Successful</option>
232-
<option value="pending">Pending</option>
233-
<option value="failed">Failed</option>
231+
<option value="successful">{t('history.filters.statuses.successful')}</option>
232+
<option value="pending">{t('history.filters.statuses.pending')}</option>
233+
<option value="failed">{t('history.filters.statuses.failed')}</option>
234234
</select>
235235
</div>
236236

@@ -329,16 +329,16 @@ export default function TransactionHistoryPage() {
329329
}}
330330
>
331331
<Icon name="refresh" size="sm" aria-hidden="true" />
332-
Clear filters
332+
{t('history.empty.clearFilters')}
333333
</button>
334334
) : (
335335
<Link href="/create" className="cta-primary">
336336
<Icon name="plus" size="sm" aria-hidden="true" />
337-
Create your first policy
337+
{t('history.empty.createFirstPolicy')}
338338
</Link>
339339
)}
340340
<Link href="/policies" className="cta-secondary">
341-
View policies
341+
{t('history.empty.viewPolicies')}
342342
</Link>
343343
</div>
344344
</div>
@@ -421,11 +421,6 @@ export default function TransactionHistoryPage() {
421421
<React.Fragment key={tx.id}>
422422
<tr
423423
className={`tx-row ${expandedId === tx.id ? 'tx-row--expanded' : ''}`}
424-
onClick={() =>
425-
setExpandedId(expandedId === tx.id ? null : tx.id)
426-
}
427-
style={{ cursor: 'pointer' }}
428-
aria-expanded={expandedId === tx.id}
429424
>
430425
<td data-label="Date">{formatDate(tx.created_at)}</td>
431426
<td data-label="Type">
@@ -446,7 +441,6 @@ export default function TransactionHistoryPage() {
446441
target="_blank"
447442
rel="noopener noreferrer"
448443
className="tx-hash-link"
449-
onClick={(e) => e.stopPropagation()}
450444
aria-label={`View transaction ${shortenHash(tx.transaction_hash)} on Stellar Explorer`}
451445
>
452446
<span>{shortenHash(tx.transaction_hash)}</span>
@@ -462,10 +456,15 @@ export default function TransactionHistoryPage() {
462456
<td data-label="Details">
463457
<button
464458
className="tx-expand-btn"
459+
aria-expanded={expandedId === tx.id}
460+
aria-controls={`tx-detail-${tx.id}`}
465461
aria-label={`${expandedId === tx.id ? 'Collapse' : 'Expand'} transaction ${tx.id} details`}
466-
onClick={(e) => {
467-
e.stopPropagation();
468-
setExpandedId(expandedId === tx.id ? null : tx.id);
462+
onClick={() => setExpandedId(expandedId === tx.id ? null : tx.id)}
463+
onKeyDown={(e) => {
464+
if (e.key === 'Enter' || e.key === ' ') {
465+
e.preventDefault();
466+
setExpandedId(expandedId === tx.id ? null : tx.id);
467+
}
469468
}}
470469
>
471470
<Icon
@@ -483,6 +482,8 @@ export default function TransactionHistoryPage() {
483482
{expandedId === tx.id && (
484483
<tr
485484
className="tx-detail-row"
485+
id={`tx-detail-${tx.id}`}
486+
role="region"
486487
aria-label="Transaction detail"
487488
>
488489
<td colSpan={6}>
@@ -514,8 +515,7 @@ export default function TransactionHistoryPage() {
514515
/>
515516
<div>
516517
<p className="tx-detail-retry-message">
517-
This transaction failed. Would you like
518-
to retry it?
518+
{t('history.details.retryPrompt')}
519519
</p>
520520
</div>
521521
</div>
@@ -531,22 +531,22 @@ export default function TransactionHistoryPage() {
531531
size="sm"
532532
aria-hidden="true"
533533
/>
534-
Start Retry
534+
{t('history.details.startRetry')}
535535
</button>
536536
</div>
537537
)}
538538
<div className="tx-detail-grid">
539539
<div>
540540
<span className="tx-detail-label">
541-
Transaction ID
541+
{t('history.details.transactionId')}
542542
</span>
543543
<span className="tx-detail-value">
544544
#{tx.id}
545545
</span>
546546
</div>
547547
<div>
548548
<span className="tx-detail-label">
549-
Full Hash
549+
{t('history.details.fullHash')}
550550
</span>
551551
<span className="tx-detail-value tx-detail-hash">
552552
{tx.transaction_hash}
@@ -555,7 +555,7 @@ export default function TransactionHistoryPage() {
555555
{tx.policy_id && (
556556
<div>
557557
<span className="tx-detail-label">
558-
Policy ID
558+
{t('history.details.policyId')}
559559
</span>
560560
<Link
561561
className="tx-detail-link"
@@ -564,14 +564,14 @@ export default function TransactionHistoryPage() {
564564
event: React.MouseEvent<HTMLAnchorElement>
565565
) => event.stopPropagation()}
566566
>
567-
#{tx.policy_id} policy snapshot
567+
#{tx.policy_id} {t('history.details.policySnapshot')}
568568
</Link>
569569
</div>
570570
)}
571571
{tx.claim_id && (
572572
<div>
573573
<span className="tx-detail-label">
574-
Claim ID
574+
{t('history.details.claimId')}
575575
</span>
576576
<span className="tx-detail-value">
577577
#{tx.claim_id}
@@ -580,15 +580,15 @@ export default function TransactionHistoryPage() {
580580
)}
581581
<div>
582582
<span className="tx-detail-label">
583-
Explorer
583+
{t('history.details.explorer')}
584584
</span>
585585
<a
586586
href={`${STELLAR_EXPLORER_BASE}${tx.transaction_hash}`}
587587
target="_blank"
588588
rel="noopener noreferrer"
589589
className="tx-detail-link tx-detail-link--with-icon"
590590
>
591-
<span>View on Stellar Expert</span>
591+
<span>{t('history.details.viewOnStellarExpert')}</span>
592592
<Icon
593593
name="arrow-up-right"
594594
size="sm"
@@ -620,7 +620,7 @@ export default function TransactionHistoryPage() {
620620
disabled={page === 1}
621621
aria-label="Previous page"
622622
>
623-
Prev
623+
{t('history.pagination.prev')}
624624
</button>
625625

626626
<div className="tx-page-numbers" role="list">
@@ -664,14 +664,14 @@ export default function TransactionHistoryPage() {
664664
disabled={page === totalPages}
665665
aria-label="Next page"
666666
>
667-
Next
667+
{t('history.pagination.next')}
668668
</button>
669669
</nav>
670670
)}
671671

672672
<p className="tx-pagination-info" aria-live="polite">
673-
Showing {Math.min((page - 1) * PER_PAGE + 1, total)}-
674-
{Math.min(page * PER_PAGE, total)} of {total}
673+
{t('history.pagination.showing')} {Math.min((page - 1) * PER_PAGE + 1, total)}-
674+
{Math.min(page * PER_PAGE, total)} {t('history.pagination.of')} {total}
675675
</p>
676676
</main>
677677
);

frontend/src/app/tokens.css

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
--color-sand-300: #e6ddce;
99
--color-ink-900: #102542;
1010
--color-ink-700: #334255;
11-
--color-ink-500: #556274;
11+
--color-ink-500: #4a5563;
1212
--color-ink-100: #d7dde6;
1313
--color-sky-500: #1f7a8c;
1414
--color-sky-100: #dbeef2;
@@ -128,7 +128,7 @@
128128
--surface-wash: rgba(19, 30, 46, 0.85);
129129
--card-border: rgba(255, 255, 255, 0.09);
130130
--text: #dce5f0;
131-
--text-muted: #7d95b0;
131+
--text-muted: #8fa6be;
132132
/* Accent — lightened to meet WCAG AA 4.5:1 on dark surfaces (#323) */
133133
--accent: #f0935f;
134134
--accent-strong: #f7b899;

0 commit comments

Comments
 (0)