Skip to content
Open
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
153 changes: 4 additions & 149 deletions dashboard/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,16 @@ export function App() {
const [activeTab, setActiveTab] = useState<TabKey>(() => parseActiveTab(initialSearch));

useEffect(() => {
if (typeof window === 'undefined') {
return;
}

if (typeof window === 'undefined') return;
const handlePopState = () => {
setActiveTab(parseActiveTab(window.location.search));
};

window.addEventListener('popstate', handlePopState);
return () => window.removeEventListener('popstate', handlePopState);
}, []);

useEffect(() => {
if (typeof window === 'undefined') {
return;
}

if (typeof window === 'undefined') return;
const params = new URLSearchParams(window.location.search);
params.set('tab', activeTab);
window.history.replaceState({}, '', `${window.location.pathname}?${params.toString()}`);
Expand All @@ -56,21 +49,6 @@ export function App() {
<button
type="button"
className={`app__tab ${activeTab === 'explorer' ? 'app__tab--active' : ''}`}
import { useState } from 'react';
import { EventExplorerPage } from './pages/EventExplorerPage';
import { ExportHistoryPage } from './pages/ExportHistoryPage';

export function App() {
const [activeTab, setActiveTab] = useState<'explorer' | 'exports'>('explorer');

return (
<div className="app">
<nav className="nav-header">
<span className="nav-brand">Notify-Chain</span>
<div className="nav-tabs">
<button
type="button"
className={`nav-tab-btn ${activeTab === 'explorer' ? 'nav-tab-btn--active' : ''}`}
onClick={() => setActiveTab('explorer')}
>
Event Explorer
Expand All @@ -85,132 +63,9 @@ export function App() {
</nav>
</header>

{activeTab === 'preferences' ? <NotificationPreferencesPage /> : <EventExplorerPage />}
className={`nav-tab-btn ${activeTab === 'exports' ? 'nav-tab-btn--active' : ''}`}
onClick={() => setActiveTab('exports')}
>
Export Center
</button>
</div>
</nav>

{activeTab === 'explorer' ? <EventExplorerPage /> : <ExportHistoryPage />}
import { DeliveryHeatmap } from './components/DeliveryHeatmap';
import { ThemeToggle } from './components/ThemeToggle';
import { useTheme } from './hooks/useTheme';
import { useEventStore } from './store/eventStore';

export function App() {
const { theme, toggleTheme } = useTheme();
const events = useEventStore((state) => state.events);

return (
<div className="app">
<div className="app__theme-bar">
<ThemeToggle theme={theme} onToggle={toggleTheme} />
</div>
<EventExplorerPage />
<DeliveryHeatmap events={events} />
import { TemplatePreviewDemoPage } from './pages/TemplatePreviewDemoPage';

type Page = 'events' | 'templates';

export function App() {
const [currentPage, setCurrentPage] = useState<Page>('templates');

return (
<div className="app">
<nav className="app-nav">
<button
className={`app-nav__button ${currentPage === 'events' ? 'app-nav__button--active' : ''}`}
onClick={() => setCurrentPage('events')}
type="button"
import { NotificationTimelineView } from './components/NotificationTimelineView';
import { TemplatesPage } from './pages/TemplatesPage';

type Tab = 'explorer' | 'timeline' | 'templates';
import { ActivityFeed } from './components/ActivityFeed';
import { WebhookDashboardPage } from './pages/WebhookDashboardPage';
import { ExportHistoryPage } from './pages/ExportHistoryPage';
import { NotificationSearchPage } from './pages/NotificationSearchPage';

type Tab = 'explorer' | 'timeline' | 'activity' | 'export-history' | 'search';
type Tab = 'explorer' | 'timeline' | 'activity' | 'webhooks' | 'export-history';

export function App() {
const [tab, setTab] = useState<Tab>('explorer');

return (
<div className="app">
<nav className="app-tabs" role="tablist" aria-label="Main navigation">
<button
role="tab"
aria-selected={tab === 'explorer'}
className={`app-tabs__btn${tab === 'explorer' ? ' app-tabs__btn--active' : ''}`}
onClick={() => setTab('explorer')}
>
Event Explorer
</button>
<button
className={`app-nav__button ${currentPage === 'templates' ? 'app-nav__button--active' : ''}`}
onClick={() => setCurrentPage('templates')}
type="button"
>
Template Preview
</button>
</nav>

<main className="app-content">
{currentPage === 'events' && <EventExplorerPage />}
{currentPage === 'templates' && <TemplatePreviewDemoPage />}
<main className="app__content">
{activeTab === 'preferences' ? <NotificationPreferencesPage /> : <EventExplorerPage />}
</main>
role="tab"
aria-selected={tab === 'timeline'}
className={`app-tabs__btn${tab === 'timeline' ? ' app-tabs__btn--active' : ''}`}
onClick={() => setTab('timeline')}
>
Delivery Timeline
</button>
<button
role="tab"
aria-selected={tab === 'activity'}
className={`app-tabs__btn${tab === 'activity' ? ' app-tabs__btn--active' : ''}`}
onClick={() => setTab('activity')}
>
Activity Feed
</button>
<button
role="tab"
aria-selected={tab === 'webhooks'}
className={`app-tabs__btn${tab === 'webhooks' ? ' app-tabs__btn--active' : ''}`}
onClick={() => setTab('webhooks')}
>
Webhook Performance
</button>
<button
role="tab"
aria-selected={tab === 'export-history'}
className={`app-tabs__btn${tab === 'export-history' ? ' app-tabs__btn--active' : ''}`}
onClick={() => setTab('export-history')}
>
Export History
</button>
<button
role="tab"
aria-selected={tab === 'search'}
className={`app-tabs__btn${tab === 'search' ? ' app-tabs__btn--active' : ''}`}
onClick={() => setTab('search')}
>
Notification Search
</button>
</nav>

{tab === 'explorer' && <EventExplorerPage />}
{tab === 'timeline' && <NotificationTimelineView />}
{tab === 'activity' && <ActivityFeed />}
{tab === 'webhooks' && <WebhookDashboardPage />}
{tab === 'export-history' && <ExportHistoryPage />}
{tab === 'search' && <NotificationSearchPage />}
</div>
);
}
23 changes: 6 additions & 17 deletions dashboard/src/components/EventExplorerCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ function shortenAddress(address: string) {
if (address.length <= 14) {
return address;
}

return `${address.slice(0, 6)}...${address.slice(-4)}`;
}

Expand All @@ -34,18 +33,16 @@ interface EventExplorerCardProps {
event: BlockchainEvent;
onCopyContract: (contractAddress: string) => void;
isCopied: boolean;
onSelect?: (event: BlockchainEvent) => void;
}

export function EventExplorerCard({ event, onCopyContract, isCopied, onSelect }: EventExplorerCardProps) {
contractStatuses: ContractStatus[];
onSelect?: (event: BlockchainEvent) => void;
}

export function EventExplorerCard({
event,
onCopyContract,
isCopied,
contractStatuses,
onSelect,
}: EventExplorerCardProps) {
const contractStatus = contractStatuses.find((c) => c.address === event.contractAddress);
const isPaused = contractStatus?.paused ?? false;
Expand Down Expand Up @@ -77,22 +74,14 @@ export function EventExplorerCard({
<p className="event-explorer__contract" title={event.contractAddress}>
{shortenAddress(event.contractAddress)}
</p>
<button
type="button"
className="event-explorer__copy-button"
onClick={(e) => {
e.stopPropagation();
onCopyContract(event.contractAddress);
}}
aria-label={`Copy contract address ${event.contractAddress}`}
>
{isCopied ? 'Copied' : 'Copy'}
</button>
<div style={{ display: 'flex', gap: '8px', alignItems: 'center' }}>
<button
type="button"
className="event-explorer__copy-button"
onClick={() => onCopyContract(event.contractAddress)}
onClick={(e) => {
e.stopPropagation();
onCopyContract(event.contractAddress);
}}
aria-label={`Copy contract address ${event.contractAddress}`}
>
{isCopied ? 'Copied' : 'Copy'}
Expand Down
13 changes: 7 additions & 6 deletions dashboard/src/components/EventExplorerTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@ import { EventExplorerCard } from './EventExplorerCard';

interface EventExplorerTableProps {
events: BlockchainEvent[];
onSelectEvent?: (event: BlockchainEvent) => void;
}

export function EventExplorerTable({ events, onSelectEvent }: EventExplorerTableProps) {
contractStatuses: ContractStatus[];
onSelectEvent?: (event: BlockchainEvent) => void;
}

export function EventExplorerTable({ events, contractStatuses }: EventExplorerTableProps) {
export function EventExplorerTable({
events,
contractStatuses,
onSelectEvent,
}: EventExplorerTableProps) {
const [copiedAddress, setCopiedAddress] = useState<string | null>(null);

async function syncCopyText(text: string) {
Expand Down Expand Up @@ -64,8 +65,8 @@ export function EventExplorerTable({ events, contractStatuses }: EventExplorerTa
event={event}
onCopyContract={handleCopyContract}
isCopied={copiedAddress === event.contractAddress}
onSelect={onSelectEvent}
contractStatuses={contractStatuses}
onSelect={onSelectEvent}
/>
))}
</div>
Expand Down
7 changes: 5 additions & 2 deletions dashboard/src/pages/EventExplorerPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -268,8 +268,11 @@ export function EventExplorerPage() {
{isLoading ? (
<EventExplorerSkeleton rows={Math.min(limit, 8)} />
) : currentPageEvents.length > 0 ? (
<EventExplorerTable events={currentPageEvents} onSelectEvent={handleSelectEvent} />
<EventExplorerTable events={currentPageEvents} contractStatuses={contractStatuses} />
<EventExplorerTable
events={currentPageEvents}
contractStatuses={contractStatuses}
onSelectEvent={handleSelectEvent}
/>
) : (
<section className="event-explorer__empty-state" role="status" aria-live="polite">
<h2>No events found</h2>
Expand Down
Loading