Skip to content

Commit 7d80e31

Browse files
committed
More UX
1 parent 9619a62 commit 7d80e31

File tree

3 files changed

+174
-60
lines changed

3 files changed

+174
-60
lines changed

frontend-react/src/components/Dashboard.tsx

Lines changed: 101 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -302,27 +302,108 @@ export const Dashboard: React.FC = () => {
302302
</div>
303303

304304

305-
{/* Secondary Filters */}
306-
<div className="bg-white/80 backdrop-blur-xl rounded-2xl shadow-xl border border-white/50 p-6 sm:p-8 mb-8">
307-
<div className="flex items-center gap-3 mb-6">
308-
<div className="w-10 h-10 bg-gradient-to-br from-[#FF2DAF] to-[#FF2DAF]/80 rounded-xl flex items-center justify-center">
309-
<svg className="w-6 h-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
310-
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 4a1 1 0 011-1h16a1 1 0 011 1v2.586a1 1 0 01-.293.707l-6.414 6.414a1 1 0 00-.293.707V17l-4 4v-6.586a1 1 0 00-.293-.707L3.293 7.293A1 1 0 013 6.586V4z" />
311-
</svg>
305+
{/* Secondary Filters - Modern Design */}
306+
<div className="relative mb-8">
307+
{/* Gradient Background Effect */}
308+
<div className="absolute inset-0 bg-gradient-to-r from-[#1B1EA9]/5 via-[#FF2DAF]/5 to-[#1B1EA9]/5 rounded-3xl blur-xl"></div>
309+
310+
<div className="relative bg-white/90 backdrop-blur-xl rounded-3xl shadow-2xl border border-white/50 overflow-hidden">
311+
{/* Header with Gradient Border */}
312+
<div className="bg-gradient-to-r from-[#1B1EA9] to-[#FF2DAF] p-[2px]">
313+
<div className="bg-white/95 backdrop-blur px-8 py-6">
314+
<div className="flex items-center justify-between">
315+
<div className="flex items-center gap-4">
316+
<div className="relative">
317+
<div className="absolute inset-0 bg-gradient-to-br from-[#FF2DAF] to-[#1B1EA9] rounded-2xl blur-lg opacity-60"></div>
318+
<div className="relative w-12 h-12 bg-gradient-to-br from-[#FF2DAF] to-[#1B1EA9] rounded-2xl flex items-center justify-center shadow-lg">
319+
<svg className="w-7 h-7 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
320+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2.5} d="M3 4a1 1 0 011-1h16a1 1 0 011 1v2.586a1 1 0 01-.293.707l-6.414 6.414a1 1 0 00-.293.707V17l-4 4v-6.586a1 1 0 00-.293-.707L3.293 7.293A1 1 0 013 6.586V4z" />
321+
</svg>
322+
</div>
323+
</div>
324+
<div>
325+
<h2 className="text-2xl font-bold bg-gradient-to-r from-[#1B1EA9] to-[#FF2DAF] bg-clip-text text-transparent">
326+
Advanced Filters
327+
</h2>
328+
<p className="text-sm text-gray-600 mt-1">Refine your search with precision</p>
329+
</div>
330+
</div>
331+
332+
{/* Active Filter Count */}
333+
{(selectedMessageType !== 'all' || peerFilter) && (
334+
<div className="flex items-center gap-2 bg-gradient-to-r from-[#1B1EA9]/10 to-[#FF2DAF]/10 px-4 py-2 rounded-full">
335+
<span className="text-sm font-medium text-gray-700">
336+
{[selectedMessageType !== 'all' ? 1 : 0, peerFilter ? 1 : 0].reduce((a, b) => a + b, 0)} active
337+
</span>
338+
<button
339+
onClick={() => {
340+
setSelectedMessageType('all');
341+
setPeerFilter('');
342+
}}
343+
className="ml-1 p-1 hover:bg-white/50 rounded-full transition-colors"
344+
>
345+
<svg className="w-3 h-3 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
346+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
347+
</svg>
348+
</button>
349+
</div>
350+
)}
351+
</div>
352+
</div>
353+
</div>
354+
355+
{/* Filter Content */}
356+
<div className="p-8">
357+
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
358+
{/* Message Type Filter with Enhanced Styling */}
359+
<div className="group">
360+
<div className="relative">
361+
<div className="absolute -inset-0.5 bg-gradient-to-r from-[#1B1EA9] to-[#FF2DAF] rounded-2xl blur opacity-0 group-hover:opacity-30 transition duration-500"></div>
362+
<div className="relative bg-gray-50/50 rounded-2xl p-6 border border-gray-200/50 hover:border-gray-300/50 transition-all duration-300">
363+
<MessageTypeFilter
364+
selectedType={selectedMessageType}
365+
onTypeChange={setSelectedMessageType}
366+
messageTypes={messageTypes}
367+
/>
368+
</div>
369+
</div>
370+
</div>
371+
372+
{/* Peer Filter with Enhanced Styling */}
373+
<div className="group">
374+
<div className="relative">
375+
<div className="absolute -inset-0.5 bg-gradient-to-r from-[#FF2DAF] to-[#1B1EA9] rounded-2xl blur opacity-0 group-hover:opacity-30 transition duration-500"></div>
376+
<div className="relative bg-gray-50/50 rounded-2xl p-6 border border-gray-200/50 hover:border-gray-300/50 transition-all duration-300">
377+
<PeerFilter
378+
value={peerFilter}
379+
onChange={setPeerFilter}
380+
recentPeers={recentPeers}
381+
/>
382+
</div>
383+
</div>
384+
</div>
385+
</div>
386+
387+
{/* Filter Summary Bar */}
388+
<div className="mt-6 flex items-center justify-between px-4 py-3 bg-gradient-to-r from-gray-50 to-gray-100 rounded-xl">
389+
<div className="flex items-center gap-2">
390+
<svg className="w-4 h-4 text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
391+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
392+
</svg>
393+
<span className="text-sm text-gray-600">
394+
Showing {selectedMessageType === 'all' ? 'all message types' : selectedMessageType.replace('_', ' ')}
395+
{peerFilter && ` from peer ${peerFilter.slice(0, 8)}...`}
396+
</span>
397+
</div>
398+
{loading && (
399+
<div className="flex items-center gap-2">
400+
<div className="w-2 h-2 bg-[#1B1EA9] rounded-full animate-bounce" style={{ animationDelay: '0ms' }}></div>
401+
<div className="w-2 h-2 bg-[#FF2DAF] rounded-full animate-bounce" style={{ animationDelay: '150ms' }}></div>
402+
<div className="w-2 h-2 bg-[#1B1EA9] rounded-full animate-bounce" style={{ animationDelay: '300ms' }}></div>
403+
</div>
404+
)}
405+
</div>
312406
</div>
313-
<h2 className="text-xl font-semibold text-gray-900">Filter Messages</h2>
314-
</div>
315-
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
316-
<MessageTypeFilter
317-
selectedType={selectedMessageType}
318-
onTypeChange={setSelectedMessageType}
319-
messageTypes={messageTypes}
320-
/>
321-
<PeerFilter
322-
value={peerFilter}
323-
onChange={setPeerFilter}
324-
recentPeers={recentPeers}
325-
/>
326407
</div>
327408
</div>
328409

frontend-react/src/components/MessageTypeFilter.tsx

Lines changed: 42 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -46,35 +46,49 @@ export const MessageTypeFilter: React.FC<MessageTypeFilterProps> = ({
4646
messageTypes
4747
}) => {
4848
return (
49-
<div className="space-y-2">
50-
<label className="block text-xs sm:text-sm font-medium text-gray-700">Message Type</label>
51-
<div className="relative">
52-
<select
53-
value={selectedType}
54-
onChange={(e) => onTypeChange(e.target.value as MessageType | 'all')}
55-
className="block w-full px-3 sm:px-4 py-3 pr-10 text-sm sm:text-base border-gray-300 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 rounded-lg appearance-none bg-white min-h-[44px]"
56-
>
57-
<option value="all">{typeInfo.all.icon} {typeInfo.all.label} - {typeInfo.all.description}</option>
58-
{messageTypes.map(type => {
59-
// Skip types that don't have typeInfo (like bestblock if it's still coming from API)
60-
if (!typeInfo[type]) {
61-
return null;
62-
}
63-
return (
64-
<option key={type} value={type}>
65-
{typeInfo[type].icon} {typeInfo[type].label} - {typeInfo[type].description}
66-
</option>
67-
);
68-
})}
69-
</select>
70-
<div className="absolute inset-y-0 right-0 flex items-center px-2 pointer-events-none">
71-
<svg className="w-5 h-5 text-gray-400" viewBox="0 0 20 20" fill="currentColor">
72-
<path fillRule="evenodd" d="M5.23 7.21a.75.75 0 011.06.02L10 11.168l3.71-3.938a.75.75 0 111.08 1.04l-4.25 4.5a.75.75 0 01-1.08 0l-4.25-4.5a.75.75 0 01.02-1.06z" clipRule="evenodd" />
73-
</svg>
74-
</div>
49+
<div className="space-y-3">
50+
<div className="flex items-center justify-between">
51+
<label className="text-sm font-semibold text-gray-800">Message Type</label>
52+
<span className="text-xs text-gray-500">{messageTypes.length} types available</span>
7553
</div>
76-
<div className="mt-1 text-xs sm:text-sm text-gray-500">
77-
{selectedType === 'all' ? typeInfo.all.description : (typeInfo[selectedType]?.description || 'Unknown message type')}
54+
55+
{/* Custom Select with Cards */}
56+
<div className="space-y-2">
57+
<div className="relative group">
58+
<select
59+
value={selectedType}
60+
onChange={(e) => onTypeChange(e.target.value as MessageType | 'all')}
61+
className="block w-full px-4 py-3.5 pr-10 text-sm font-medium text-gray-800 bg-white border-2 border-gray-200 rounded-xl appearance-none transition-all duration-200 focus:outline-none focus:border-[#1B1EA9] focus:ring-4 focus:ring-[#1B1EA9]/10 hover:border-gray-300 cursor-pointer"
62+
>
63+
<option value="all">{typeInfo.all.icon} {typeInfo.all.label}</option>
64+
{messageTypes.map(type => {
65+
if (!typeInfo[type]) return null;
66+
return (
67+
<option key={type} value={type}>
68+
{typeInfo[type].icon} {typeInfo[type].label}
69+
</option>
70+
);
71+
})}
72+
</select>
73+
<div className="absolute inset-y-0 right-0 flex items-center px-3 pointer-events-none">
74+
<svg className="w-5 h-5 text-gray-500 group-hover:text-gray-700 transition-colors" viewBox="0 0 20 20" fill="currentColor">
75+
<path fillRule="evenodd" d="M5.23 7.21a.75.75 0 011.06.02L10 11.168l3.71-3.938a.75.75 0 111.08 1.04l-4.25 4.5a.75.75 0 01-1.08 0l-4.25-4.5a.75.75 0 01.02-1.06z" clipRule="evenodd" />
76+
</svg>
77+
</div>
78+
</div>
79+
80+
{/* Selected Type Info Card */}
81+
<div className="flex items-center gap-3 p-3 bg-gradient-to-r from-gray-50 to-gray-100/50 rounded-lg border border-gray-200/50">
82+
<div className="text-2xl">{typeInfo[selectedType]?.icon || typeInfo.all.icon}</div>
83+
<div>
84+
<div className="text-sm font-medium text-gray-800">
85+
{typeInfo[selectedType]?.label || typeInfo.all.label}
86+
</div>
87+
<div className="text-xs text-gray-600">
88+
{typeInfo[selectedType]?.description || typeInfo.all.description}
89+
</div>
90+
</div>
91+
</div>
7892
</div>
7993
</div>
8094
);

frontend-react/src/components/PeerFilter.tsx

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -39,21 +39,31 @@ export const PeerFilter: React.FC<PeerFilterProps> = ({
3939

4040
return (
4141
<div className="relative">
42-
<label className="block text-xs sm:text-sm font-medium text-gray-700 mb-2">Peer ID</label>
43-
<div className="relative">
42+
<div className="flex items-center justify-between mb-3">
43+
<label className="text-sm font-semibold text-gray-800">Peer Filter</label>
44+
{recentPeers.length > 0 && (
45+
<span className="text-xs text-gray-500">{recentPeers.length} recent peers</span>
46+
)}
47+
</div>
48+
<div className="relative group">
49+
<div className="absolute inset-y-0 left-0 flex items-center pl-3 pointer-events-none">
50+
<svg className="w-5 h-5 text-gray-400 group-focus-within:text-[#1B1EA9] transition-colors" fill="none" stroke="currentColor" viewBox="0 0 24 24">
51+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
52+
</svg>
53+
</div>
4454
<input
4555
type="text"
4656
value={value}
4757
onChange={(e) => onChange(e.target.value)}
4858
onFocus={() => setShowSuggestions(true)}
4959
onBlur={() => setTimeout(() => setShowSuggestions(false), 200)}
50-
placeholder={placeholder}
51-
className="block w-full px-3 sm:px-4 py-3 pr-10 text-sm sm:text-base border-gray-300 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 rounded-lg min-h-[44px]"
60+
placeholder="Search by peer ID or name..."
61+
className="block w-full pl-10 pr-10 py-3.5 text-sm font-medium text-gray-800 bg-white border-2 border-gray-200 rounded-xl appearance-none transition-all duration-200 focus:outline-none focus:border-[#1B1EA9] focus:ring-4 focus:ring-[#1B1EA9]/10 hover:border-gray-300"
5262
/>
5363
{value && (
5464
<button
5565
onClick={() => onChange('')}
56-
className="absolute inset-y-0 right-0 flex items-center px-3 text-gray-400 hover:text-gray-600"
66+
className="absolute inset-y-0 right-0 flex items-center px-3 text-gray-400 hover:text-gray-600 transition-colors"
5767
>
5868
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
5969
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
@@ -63,28 +73,37 @@ export const PeerFilter: React.FC<PeerFilterProps> = ({
6373
</div>
6474

6575
{showSuggestions && filteredPeers.length > 0 && (
66-
<div className="absolute z-10 w-full mt-1 bg-white border border-gray-200 rounded-lg shadow-lg">
67-
<div className="py-1">
76+
<div className="absolute z-10 w-full mt-2 bg-white border-2 border-gray-200 rounded-xl shadow-xl overflow-hidden">
77+
<div className="py-2">
78+
<div className="px-3 py-2 text-xs font-semibold text-gray-500 uppercase tracking-wider bg-gray-50">
79+
Suggestions
80+
</div>
6881
{filteredPeers.map((peer, index) => (
6982
<button
7083
key={index}
7184
onClick={() => handleSelect(peer)}
72-
className="w-full px-3 sm:px-4 py-2 text-left text-xs sm:text-sm hover:bg-gray-50 focus:bg-gray-50 focus:outline-none"
85+
className="w-full px-4 py-3 text-left hover:bg-gradient-to-r hover:from-[#1B1EA9]/5 hover:to-[#FF2DAF]/5 focus:bg-gradient-to-r focus:from-[#1B1EA9]/5 focus:to-[#FF2DAF]/5 focus:outline-none transition-all duration-200 border-t border-gray-100"
7386
>
7487
<PeerNameDisplay
7588
peerID={peer}
7689
showBoth={true}
77-
className="text-gray-700"
90+
className="text-sm font-medium text-gray-800"
7891
/>
7992
</button>
8093
))}
8194
</div>
8295
</div>
8396
)}
8497

85-
<p className="mt-1 text-xs sm:text-sm text-gray-500">
86-
Enter a peer ID to filter messages
87-
</p>
98+
{/* Info card when peer is selected */}
99+
{value && (
100+
<div className="mt-2 flex items-center gap-2 p-2 bg-gradient-to-r from-[#1B1EA9]/5 to-[#FF2DAF]/5 rounded-lg">
101+
<svg className="w-4 h-4 text-[#1B1EA9]" fill="none" stroke="currentColor" viewBox="0 0 24 24">
102+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-6l-2-2H5a2 2 0 00-2 2z" />
103+
</svg>
104+
<span className="text-xs text-gray-700">Filtering by: <span className="font-semibold">{value.slice(0, 12)}...</span></span>
105+
</div>
106+
)}
88107
</div>
89108
);
90109
};

0 commit comments

Comments
 (0)