Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
"zustand": "^5.0.12"
},
"devDependencies": {
"@axe-core/cli": "^4.11.1",
"@playwright/test": "^1.58.2",
"@types/canvas-confetti": "^1.9.0",
"@types/event-source-polyfill": "^1.0.5",
Expand All @@ -57,6 +58,7 @@
"autoprefixer": "^10.4.19",
"eslint": "^8.57.0",
"eslint-config-next": "^14.2.5",
"lighthouse": "^13.0.3",
"postcss": "^8.4.38",
"tailwindcss": "^3.4.7",
"typescript": "5.9.3"
Expand Down
15 changes: 11 additions & 4 deletions frontend/src/app/(authenticated)/dashboard/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export default function DashboardPage() {
<div className="flex flex-col gap-10 animate-in fade-in duration-500">
<header className="flex flex-col gap-4">
<h1 className="text-4xl font-bold text-white">{t("title")}</h1>
<p className="max-w-2xl text-slate-400">{t("description")}</p>
<p className="max-w-2xl text-slate-300">{t("description")}</p>
</header>

<div className="grid gap-10 lg:grid-cols-3">
Expand Down Expand Up @@ -96,6 +96,7 @@ export default function DashboardPage() {
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
aria-hidden="true"
>
<path
strokeLinecap="round"
Expand All @@ -107,14 +108,17 @@ export default function DashboardPage() {
{t("createPaymentLink")}
</Link>
<button
type="button"
onClick={() => setIsWithdrawOpen(true)}
className="flex items-center gap-3 rounded-xl border border-white/10 bg-white/5 px-4 py-3 text-sm font-medium text-slate-300 transition-all hover:bg-white/10 hover:text-white"
className="flex items-center gap-3 rounded-xl border border-white/10 bg-white/5 px-4 py-3 text-sm font-medium text-slate-200 transition-all hover:bg-white/10 hover:text-white"
aria-label={t("withdrawFunds")}
>
<svg
className="h-5 w-5"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
aria-hidden="true"
>
<path
strokeLinecap="round"
Expand All @@ -134,6 +138,7 @@ export default function DashboardPage() {
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
aria-hidden="true"
>
<path
strokeLinecap="round"
Expand All @@ -154,13 +159,15 @@ export default function DashboardPage() {
href="/api-docs"
target="_blank"
rel="noopener noreferrer"
className="flex items-center gap-3 rounded-xl border border-white/10 bg-white/5 px-4 py-3 text-sm font-medium text-slate-300 transition-all hover:bg-white/10 hover:text-white"
className="flex items-center gap-3 rounded-xl border border-white/10 bg-white/5 px-4 py-3 text-sm font-medium text-slate-200 transition-all hover:bg-white/10 hover:text-white"
aria-label="View API documentation in a new tab"
>
<svg
className="h-5 w-5"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
aria-hidden="true"
>
<path
strokeLinecap="round"
Expand All @@ -178,7 +185,7 @@ export default function DashboardPage() {
<h3 className="mb-4 text-lg font-semibold text-white">
{t("development")}
</h3>
<div className="space-y-4 text-sm text-slate-400">
<div className="space-y-4 text-sm text-slate-300">
<div className="flex items-start gap-3">
<div className="mt-1 h-1.5 w-1.5 rounded-full bg-mint" />
<p>{t("apiKeysTip")}</p>
Expand Down
5 changes: 4 additions & 1 deletion frontend/src/app/(authenticated)/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export default function AuthenticatedLayout({

return (
<AuthGuard>
<div className="flex min-h-screen overflow-x-hidden bg-black">
<div className="dashboard-shell flex min-h-screen overflow-x-hidden bg-black">
<Sidebar
mobileOpen={mobileSidebarOpen}
onMobileOpenChange={setMobileSidebarOpen}
Expand All @@ -38,12 +38,15 @@ export default function AuthenticatedLayout({
onClick={() => setMobileSidebarOpen(true)}
className="inline-flex h-11 w-11 items-center justify-center rounded-2xl border border-white/10 bg-white/5 text-slate-300 transition-colors hover:bg-white/10 hover:text-white lg:hidden"
aria-label="Open navigation menu"
aria-controls="dashboard-sidebar-mobile"
aria-expanded={mobileSidebarOpen}
>
<svg
className="h-5 w-5"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
aria-hidden="true"
>
<path
strokeLinecap="round"
Expand Down
1 change: 0 additions & 1 deletion frontend/src/app/(public)/pay/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -415,7 +415,6 @@ export default function PaymentPage() {
useState<NetworkFeeResponse["network_fee"] | null>(null);
const [networkFeeLoading, setNetworkFeeLoading] = useState(false);
const [networkFeeError, setNetworkFeeError] = useState<string | null>(null);
const paymentStatus = payment?.status;

useEffect(() => {
if (
Expand Down
62 changes: 62 additions & 0 deletions frontend/src/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,68 @@ body {
background-color: rgba(15, 23, 42, 0.08);
}

/*
* Authenticated screens intentionally use a dark dashboard chrome even when the
* app is otherwise in light mode. These overrides preserve readable muted text
* and panel contrast inside that shell.
*/
:root:not(.dark) .dashboard-shell {
color: #f8fafc;
}

:root:not(.dark) .dashboard-shell .text-white,
:root:not(.dark) .dashboard-shell .text-slate-100,
:root:not(.dark) .dashboard-shell .text-slate-200,
:root:not(.dark) .dashboard-shell .text-slate-300 {
color: #e2e8f0;
}

:root:not(.dark) .dashboard-shell .text-slate-400 {
color: #cbd5e1;
}

:root:not(.dark) .dashboard-shell .text-slate-500,
:root:not(.dark) .dashboard-shell .text-slate-600 {
color: #94a3b8;
}

:root:not(.dark) .dashboard-shell .placeholder\:text-slate-600::placeholder {
color: #94a3b8;
}

:root:not(.dark) .dashboard-shell .border-white\/10,
:root:not(.dark) .dashboard-shell .border-white\/20 {
border-color: rgba(226, 232, 240, 0.14);
}

:root:not(.dark) .dashboard-shell .bg-black\/30,
:root:not(.dark) .dashboard-shell .bg-black\/40,
:root:not(.dark) .dashboard-shell .bg-black\/50,
:root:not(.dark) .dashboard-shell .bg-black\/60,
:root:not(.dark) .dashboard-shell .bg-black\/70,
:root:not(.dark) .dashboard-shell .bg-white\/5 {
background-color: rgba(15, 23, 42, 0.72);
}

:root:not(.dark) .dashboard-shell .hover\:bg-white\/10:hover,
:root:not(.dark) .dashboard-shell .hover\:bg-white\/5:hover {
background-color: rgba(148, 163, 184, 0.16);
}

@layer base {
a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
[role="button"]:focus-visible,
[role="dialog"]:focus-visible,
[tabindex]:focus-visible {
outline: 2px solid #5ef2c0;
outline-offset: 2px;
}
}

.docs-prose {
color: #cbd5e1;
line-height: 1.8;
Expand Down
5 changes: 4 additions & 1 deletion frontend/src/components/CopyButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -132,9 +132,10 @@ export default function CopyButton({ text, className = "" }: CopyButtonProps) {
return (
<div className="relative inline-flex items-center">
<motion.button
type="button"
onClick={handleCopy}
aria-label={t("ariaLabel")}
className={`relative overflow-hidden rounded-lg border border-white/10 bg-black/20 p-1.5 text-slate-400 transition-all hover:border-mint/40 hover:text-mint active:scale-95 ${className}`}
className={`relative overflow-hidden rounded-lg border border-white/10 bg-black/20 p-1.5 text-slate-300 transition-all hover:border-mint/40 hover:text-mint active:scale-95 ${className}`}
whileTap={{ scale: 0.95 }}
whileHover={{ scale: 1.05 }}
variants={glitchButtonVariants}
Expand Down Expand Up @@ -181,6 +182,7 @@ export default function CopyButton({ text, className = "" }: CopyButtonProps) {
fill="none"
stroke="currentColor"
strokeWidth={2.5}
aria-hidden="true"
initial={{ scale: 0, rotate: -180, opacity: 0 }}
animate={{ scale: 1, rotate: 0, opacity: 1 }}
exit={{ scale: 0, rotate: 180, opacity: 0 }}
Expand Down Expand Up @@ -213,6 +215,7 @@ export default function CopyButton({ text, className = "" }: CopyButtonProps) {
fill="none"
stroke="currentColor"
strokeWidth={2}
aria-hidden="true"
initial={{ scale: 1, opacity: 1 }}
animate={{ scale: copied ? 0 : 1, opacity: copied ? 0 : 1 }}
exit={{ scale: 0, opacity: 0 }}
Expand Down
7 changes: 5 additions & 2 deletions frontend/src/components/FirstApiKeyModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export default function FirstApiKeyModal({ isOpen, onClose }: FirstApiKeyModalPr
</div>

<button
type="button"
onClick={handleGenerate}
disabled={loading}
className="w-full rounded-xl bg-mint py-3 font-bold text-black transition-all hover:bg-glow disabled:opacity-50"
Expand Down Expand Up @@ -72,15 +73,17 @@ export default function FirstApiKeyModal({ isOpen, onClose }: FirstApiKeyModalPr
target="_blank"
rel="noopener noreferrer"
className="flex items-center justify-center gap-2 rounded-xl border border-white/10 bg-white/5 py-3 text-sm font-medium text-white transition-all hover:bg-white/10"
aria-label="View API documentation in a new tab"
>
View API Documentation
<svg className="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<svg className="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" />
</svg>
</a>
<button
type="button"
onClick={onClose}
className="w-full text-center text-sm text-slate-500 hover:text-white transition-colors"
className="w-full text-center text-sm text-slate-400 transition-colors hover:text-white"
>
Done
</button>
Expand Down
27 changes: 16 additions & 11 deletions frontend/src/components/PaymentMetrics.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -217,13 +217,15 @@ function ChartExportButton({
type="button"
disabled={exporting}
className="inline-flex items-center gap-2 rounded-xl border border-white/10 bg-black/30 px-3 py-2 text-xs font-semibold uppercase tracking-[0.2em] text-slate-300 transition-all hover:border-mint/30 hover:text-white disabled:cursor-not-allowed disabled:opacity-50"
aria-label={exporting ? t("exporting") : t("downloadImage")}
>
<svg
viewBox="0 0 24 24"
className="h-4 w-4"
fill="none"
stroke="currentColor"
strokeWidth={1.8}
aria-hidden="true"
>
<path d="M12 4v10" strokeLinecap="round" strokeLinejoin="round" />
<path
Expand Down Expand Up @@ -373,6 +375,7 @@ export default function PaymentMetrics({
<div className="rounded-xl border border-yellow-500/30 bg-yellow-500/10 p-6 text-center">
<p className="text-sm text-yellow-400">{error}</p>
<button
type="button"
onClick={() => setError(null)}
className="mt-3 text-xs text-slate-400 underline"
>
Expand Down Expand Up @@ -410,54 +413,54 @@ export default function PaymentMetrics({
{summary && (
<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-4">
<div className="rounded-xl border border-white/10 bg-white/5 p-4 backdrop-blur">
<p className="font-mono text-xs uppercase tracking-wider text-slate-400">
<p className="font-mono text-xs uppercase tracking-wider text-slate-300">
{t("sevenDayVolume")}
</p>
<div className="mt-2 flex items-baseline gap-2">
<p className="text-3xl font-bold text-mint">
{summary.total_volume.toLocaleString()}
</p>
<p className="text-sm text-slate-400">XLM</p>
<p className="text-sm text-slate-300">XLM</p>
</div>
</div>

<div className="rounded-xl border border-white/10 bg-white/5 p-4 backdrop-blur">
<p className="font-mono text-xs uppercase tracking-wider text-slate-400">
<p className="font-mono text-xs uppercase tracking-wider text-slate-300">
{t("totalPayments")}
</p>
<div className="mt-2 flex items-baseline gap-2">
<p className="text-3xl font-bold text-mint">
{summary.total_payments}
</p>
<p className="text-sm text-slate-400">
<p className="text-sm text-slate-300">
{t("paymentsCount", { count: summary.total_payments })}
</p>
</div>
</div>

<div className="rounded-xl border border-white/10 bg-white/5 p-4 backdrop-blur">
<p className="font-mono text-xs uppercase tracking-wider text-slate-400">
<p className="font-mono text-xs uppercase tracking-wider text-slate-300">
Confirmed
</p>
<div className="mt-2 flex items-baseline gap-2">
<p className="text-3xl font-bold text-green-400">
{summary.confirmed_count}
</p>
<p className="text-sm text-slate-400">
<p className="text-sm text-slate-300">
{summary.confirmed_count === 1 ? "intent" : "intents"}
</p>
</div>
</div>

<div className="rounded-xl border border-white/10 bg-white/5 p-4 backdrop-blur">
<p className="font-mono text-xs uppercase tracking-wider text-slate-400">
<p className="font-mono text-xs uppercase tracking-wider text-slate-300">
Success Rate
</p>
<div className="mt-2 flex items-baseline gap-2">
<p className="text-3xl font-bold text-green-400">
{summary.success_rate}
</p>
<p className="text-sm text-slate-400">%</p>
<p className="text-sm text-slate-300">%</p>
</div>
</div>
</div>
Expand All @@ -470,19 +473,20 @@ export default function PaymentMetrics({
<div className="flex flex-wrap items-start justify-between gap-3">
<div>
<h3 className="font-semibold text-white">{t("chartTitle")}</h3>
<p className="text-xs text-slate-400">{t("chartSubtitle")}</p>
<p className="text-xs text-slate-300">{t("chartSubtitle")}</p>
</div>

<div className="flex flex-wrap items-center gap-2">
<div className="flex gap-1 rounded-lg border border-white/10 bg-white/5 p-1">
{TIME_RANGES.map((nextRange) => (
<button
key={nextRange}
type="button"
onClick={() => setRange(nextRange)}
className={`rounded-md px-3 py-1 text-xs font-medium transition-colors ${
range === nextRange
? "bg-white/10 text-white"
: "text-slate-400 hover:text-white"
: "text-slate-300 hover:text-white"
}`}
aria-pressed={range === nextRange}
aria-label={t("showRange", {
Expand Down Expand Up @@ -518,8 +522,9 @@ export default function PaymentMetrics({
return (
<button
key={asset}
type="button"
onClick={() => toggleAsset(asset)}
className={`flex items-center gap-1.5 rounded-full border px-3 py-1 text-xs font-medium transition-opacity ${
className={`flex items-center gap-1.5 rounded-full border px-3 py-1 text-xs font-medium transition-opacity focus-visible:opacity-100 ${
hidden ? "opacity-40" : "opacity-100"
}`}
style={{ borderColor: color, color }}
Expand Down
Loading
Loading