From 79775f1ba2b9f755472b084b2194b97743db8694 Mon Sep 17 00:00:00 2001 From: Gaina Slyvester Date: Sun, 30 Aug 2026 04:06:11 +0000 Subject: [PATCH 1/4] fix(#670): replace hardcoded colors with semantic dark-mode tokens in batch create page - Replace bg-red-50 with bg-destructive/5 dark:bg-destructive/10 for error rows - Replace bg-emerald-100 text-emerald-700 with semantic tokens in valid badge - Add dark mode support to yellow warning text (text-yellow-600 dark:text-yellow-400) - Ensures colors adapt properly in both light and dark modes --- app/app/create/batch/page.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/app/create/batch/page.tsx b/app/app/create/batch/page.tsx index 430ada7..7fbd20f 100644 --- a/app/app/create/batch/page.tsx +++ b/app/app/create/batch/page.tsx @@ -301,7 +301,7 @@ export default function BatchCreatePage() { )} {parseErrors.length > 0 && ( -
+

CSV parse warnings

    {parseErrors.map((message, index) => ( @@ -344,7 +344,7 @@ export default function BatchCreatePage() { {rows.map((row) => ( 0 ? 'bg-red-50' : undefined} + className={row.errors.length > 0 ? 'bg-destructive/5 dark:bg-destructive/10' : undefined} > {row.index} @@ -361,7 +361,7 @@ export default function BatchCreatePage() { {row.errors.length === 0 ? ( - + Valid ) : ( From 95dc7bf1f8c6fb05372093cb022f17a9e977b4a9 Mon Sep 17 00:00:00 2001 From: Gaina Slyvester Date: Sun, 30 Aug 2026 04:06:28 +0000 Subject: [PATCH 2/4] fix(#671): add truncation with tooltip to TokenAmount in dashboard stats - Add optional 'truncate' prop to TokenAmount component - Display full value in title attribute (tooltip) when truncated - Apply truncation to 'Available to withdraw' stat card value - Prevents overflow of large formatted balances at smaller breakpoints --- components/streams/dashboard-stats.tsx | 1 + components/ui/token-amount.tsx | 10 +++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/components/streams/dashboard-stats.tsx b/components/streams/dashboard-stats.tsx index 7d01be0..6b0413e 100644 --- a/components/streams/dashboard-stats.tsx +++ b/components/streams/dashboard-stats.tsx @@ -99,6 +99,7 @@ export function DashboardStats({ sent, received }: DashboardStatsProps) { amount={topWithdrawable.amount} token={topWithdrawable.token} maxFractionDigits={2} + truncate /> ) : ( diff --git a/components/ui/token-amount.tsx b/components/ui/token-amount.tsx index ae14a2c..ea60b98 100644 --- a/components/ui/token-amount.tsx +++ b/components/ui/token-amount.tsx @@ -11,6 +11,8 @@ interface TokenAmountProps { showSymbol?: boolean /** Display amount in compact notation (e.g., "1.2K"). @default false */ compact?: boolean + /** Truncate the display with ellipsis. @default false */ + truncate?: boolean } /** Formats a raw bigint token amount with correct decimals + symbol. */ @@ -22,13 +24,19 @@ export function TokenAmount({ maxFractionDigits = 4, showSymbol = true, compact = false, + truncate = false, }: TokenAmountProps) { const formattedAmount = compact ? formatCompactAmount(amount, token.decimals) : formatTokenAmount(amount, token.decimals, maxFractionDigits) + const fullValue = `${formattedAmount}${showSymbol ? ` ${token.symbol}` : ''}` + return ( - + {formattedAmount} {showSymbol && ( From 34ab0c7575d06228265630d8031635281895abf6 Mon Sep 17 00:00:00 2001 From: Gaina Slyvester Date: Sun, 30 Aug 2026 04:07:08 +0000 Subject: [PATCH 3/4] feat(#672): add Next.js loading.tsx route-level files for instant loading UI Add lightweight loading indicators for 6 app router routes: - app/app/: dashboard with stats and stream list skeletons - app/app/streams/: stream list loading skeleton - app/app/analytics/: chart loading skeletons - app/app/settings/: settings form placeholder skeleton - app/app/stream/[id]/: stream detail page skeleton - app/app/create/: create stream form skeleton Reuses existing skeleton components (DashboardStatsSkeleton, StreamCardSkeleton, ChartSkeleton) for consistent UI patterns and improved perceived performance during route transitions. --- app/app/analytics/loading.tsx | 22 +++++++++++++++++++ app/app/create/loading.tsx | 30 +++++++++++++++++++++++++ app/app/loading.tsx | 26 ++++++++++++++++++++++ app/app/settings/loading.tsx | 28 +++++++++++++++++++++++ app/app/stream/[id]/loading.tsx | 39 +++++++++++++++++++++++++++++++++ app/app/streams/loading.tsx | 22 +++++++++++++++++++ 6 files changed, 167 insertions(+) create mode 100644 app/app/analytics/loading.tsx create mode 100644 app/app/create/loading.tsx create mode 100644 app/app/loading.tsx create mode 100644 app/app/settings/loading.tsx create mode 100644 app/app/stream/[id]/loading.tsx create mode 100644 app/app/streams/loading.tsx diff --git a/app/app/analytics/loading.tsx b/app/app/analytics/loading.tsx new file mode 100644 index 0000000..32a4ccc --- /dev/null +++ b/app/app/analytics/loading.tsx @@ -0,0 +1,22 @@ +'use client' + +import { ChartSkeleton } from '@/components/analytics/charts' + +export default function AnalyticsLoading() { + return ( +
    + {/* Header skeleton */} +
    +
    +
    +
    + + {/* Charts skeleton */} +
    + {Array.from({ length: 4 }).map((_, i) => ( + + ))} +
    +
    + ) +} diff --git a/app/app/create/loading.tsx b/app/app/create/loading.tsx new file mode 100644 index 0000000..86c991d --- /dev/null +++ b/app/app/create/loading.tsx @@ -0,0 +1,30 @@ +'use client' + +export default function CreateStreamLoading() { + return ( +
    + {/* Header skeleton */} +
    +
    +
    +
    + + {/* Form section skeleton */} +
    + {/* Form fields skeleton */} + {Array.from({ length: 6 }).map((_, i) => ( +
    +
    +
    +
    + ))} + + {/* Button skeleton */} +
    +
    +
    +
    +
    +
    + ) +} diff --git a/app/app/loading.tsx b/app/app/loading.tsx new file mode 100644 index 0000000..983b7e0 --- /dev/null +++ b/app/app/loading.tsx @@ -0,0 +1,26 @@ +'use client' + +import { DashboardStatsSkeleton } from '@/components/streams/dashboard-stats' +import { StreamCardSkeleton } from '@/components/streams/stream-card' + +export default function DashboardLoading() { + return ( +
    + {/* Header skeleton */} +
    +
    +
    +
    + + {/* Stats skeleton */} + + + {/* Stream list skeleton */} +
    + {Array.from({ length: 3 }).map((_, i) => ( + + ))} +
    +
    + ) +} diff --git a/app/app/settings/loading.tsx b/app/app/settings/loading.tsx new file mode 100644 index 0000000..d2cae16 --- /dev/null +++ b/app/app/settings/loading.tsx @@ -0,0 +1,28 @@ +'use client' + +export default function SettingsLoading() { + return ( +
    + {/* Header skeleton */} +
    +
    +
    +
    + + {/* Settings sections skeleton */} + {Array.from({ length: 3 }).map((_, i) => ( +
    +
    +
    + {Array.from({ length: 2 }).map((_, j) => ( +
    +
    +
    +
    + ))} +
    +
    + ))} +
    + ) +} diff --git a/app/app/stream/[id]/loading.tsx b/app/app/stream/[id]/loading.tsx new file mode 100644 index 0000000..24f5690 --- /dev/null +++ b/app/app/stream/[id]/loading.tsx @@ -0,0 +1,39 @@ +'use client' + +export default function StreamDetailLoading() { + return ( +
    + {/* Back button skeleton */} +
    + + {/* Header section skeleton */} +
    +
    +
    + {Array.from({ length: 4 }).map((_, i) => ( +
    +
    +
    +
    + ))} +
    +
    + + {/* Timeline/Activity section skeleton */} +
    +
    +
    + {Array.from({ length: 3 }).map((_, i) => ( +
    +
    +
    +
    +
    +
    +
    + ))} +
    +
    +
    + ) +} diff --git a/app/app/streams/loading.tsx b/app/app/streams/loading.tsx new file mode 100644 index 0000000..6121556 --- /dev/null +++ b/app/app/streams/loading.tsx @@ -0,0 +1,22 @@ +'use client' + +import { StreamCardSkeleton } from '@/components/streams/stream-card' + +export default function StreamsLoading() { + return ( +
    + {/* Header skeleton */} +
    +
    +
    +
    + + {/* Stream list skeleton */} +
    + {Array.from({ length: 5 }).map((_, i) => ( + + ))} +
    +
    + ) +} From d1cd69bee66fe11a94480e3076015f229d2ef0a8 Mon Sep 17 00:00:00 2001 From: Gaina Slyvester Date: Sun, 30 Aug 2026 04:07:46 +0000 Subject: [PATCH 4/4] feat(#673): add empty state to webhook settings component - Display friendly empty state when no webhooks are registered - Add Webhook icon and helpful messaging for new users - Include CTA button to register first webhook with smooth scroll anchor - Follows existing empty-state design patterns from streams list - Improves user experience when viewing settings with no webhooks --- components/webhooks/webhook-settings.tsx | 40 +++++++++++++++++------- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/components/webhooks/webhook-settings.tsx b/components/webhooks/webhook-settings.tsx index 11ec5b1..0a6717f 100644 --- a/components/webhooks/webhook-settings.tsx +++ b/components/webhooks/webhook-settings.tsx @@ -1,7 +1,7 @@ 'use client' import { useState } from 'react' -import { Plus, Trash2, ToggleLeft, ToggleRight, Send, CheckCircle2, XCircle } from 'lucide-react' +import { Plus, Trash2, ToggleLeft, ToggleRight, Send, CheckCircle2, XCircle, Webhook } from 'lucide-react' import { toast } from 'sonner' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' @@ -74,7 +74,7 @@ export function WebhookSettings() { return (
    {/* Add webhook */} -
    +

    Register a webhook

    @@ -124,11 +124,28 @@ export function WebhookSettings() {
    {/* Registered webhooks */} - {webhooks.length > 0 && ( -
    -

    Registered webhooks

    - {webhooks.map((hook) => ( -
    +
    +

    Registered webhooks

    + {webhooks.length === 0 ? ( +
    + + + +

    No webhooks registered yet

    +

    + Register a webhook above to start receiving real-time event notifications for your streams. +

    + +
    + ) : ( +
    + {webhooks.map((hook) => ( +

    {hook.url}

    @@ -173,10 +190,11 @@ export function WebhookSettings() {
    -
    - ))} -
    - )} +
    + ))} +
    + )} +
    {/* Delivery history */} {history.length > 0 && (