Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: admin page layout & refactor #38

Merged
merged 3 commits into from
Nov 13, 2024
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
285 changes: 157 additions & 128 deletions components/admins/components/validatorList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
admin,
activeValidators,
pendingValidators,
isLoading,
}: ValidatorListProps) {
const [active, setActive] = useState(true);
const [searchTerm, setSearchTerm] = useState('');
Expand Down Expand Up @@ -73,139 +74,167 @@
};

return (
<div className="w-full max-w-screen mx-auto">
<div className="">
<div className="flex justify-between items-center mb-6">
<h2
className="text-black dark:text-white"
style={{ fontSize: '20px', fontWeight: 700, lineHeight: '24px' }}
>
Validators
</h2>
<div className="relative w-[224px]">
<input
type="text"
placeholder="Search for a validator..."
className="input input-bordered w-full h-[40px] rounded-[12px] border-none bg-[#0000000A] dark:bg-[#FFFFFF1F] pl-10 text-[#161616] dark:text-white placeholder-[#00000099] dark:placeholder-[#FFFFFF99]"
value={searchTerm}
onChange={e => setSearchTerm(e.target.value)}
/>
<SearchIcon className="absolute left-3 top-1/2 transform -translate-y-1/2 text-[#00000099] dark:text-[#FFFFFF99]" />
</div>
</div>
<div className="flex mb-6 w-full h-[3.5rem] rounded-xl p-1 bg-[#0000000A] dark:bg-[#FFFFFF0F]">
<button
onClick={() => setActive(true)}
className={`flex-1 py-2 px-4 text-sm font-medium rounded-xl ${
active
? 'dark:bg-[#FFFFFF1F] bg-[#FFFFFF] text-[#161616] dark:text-white'
: 'text-[#808080]'
}`}
>
Active
</button>
<button
onClick={() => setActive(false)}
className={`flex-1 py-2 px-4 text-sm font-medium rounded-xl ${
!active
? 'dark:bg-[#FFFFFF1F] bg-[#FFFFFF] text-[#161616] dark:text-white'
: 'text-[#808080]'
}`}
>
Pending
</button>
</div>
<div className="overflow-x-auto overflow-y-auto max-h-[calc(100vh-300px)]">
{filteredValidators.length === 0 ? (
<div className="text-center py-8 text-[#808080]">
{active ? 'No active validators found' : 'No pending validators'}
<div className="relative w-full h-screen overflow-hidden">
<div className="absolute inset-0 transition-transform duration-300 translate-x-0">
<div className="h-full flex flex-col p-4">
<div className="flex justify-between items-center mb-4 min-h-[52px]">
<div className="flex items-center space-x-4">
<h2
className="text-black dark:text-white"
style={{ fontSize: '20px', fontWeight: 700, lineHeight: '24px' }}
>
Validators
</h2>
<div className="relative">
<input
type="text"
placeholder="Search for a validator..."
className="input input-bordered w-[224px] h-[40px] rounded-[12px] border-none bg:[#0000000A] dark:bg-[#FFFFFF1F] pl-10"
value={searchTerm}
onChange={e => setSearchTerm(e.target.value)}
/>
<SearchIcon className="h-6 w-6 absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 " />
</div>
</div>
) : (
<table
className="table w-full border-separate border-spacing-y-3"
role="grid"
aria-label="Validators list"
</div>
<div className="flex mb-6 w-full h-[3.5rem] rounded-xl p-1 bg-[#0000000A] dark:bg-[#FFFFFF0F]">
<button
onClick={() => setActive(true)}
className={`flex-1 py-2 px-4 text-sm font-medium rounded-xl ${
active
? 'dark:bg-[#FFFFFF1F] bg-[#FFFFFF] text-[#161616] dark:text-white'
: 'text-[#808080]'
}`}
>
<thead>
<tr className="text-sm font-medium text-[#808080]" role="row">
<th className="bg-transparent text-left sticky top-0 bg-base-100 z-10">
Moniker
</th>
<th className="bg-transparent text-left sticky top-0 bg-base-100 z-10">
Address
</th>
<th className="bg-transparent text-left sticky top-0 bg-base-100 z-10">
Consensus Power
</th>
<th className="bg-transparent text-right sticky top-0 bg-base-100 z-10">
Remove
</th>
</tr>
</thead>
<tbody>
{filteredValidators.map(validator => (
<tr
key={validator.operator_address}
className="bg-[#FFFFFFCC] dark:bg-[#FFFFFF0F] hover:bg-[#FFFFFF66] dark:hover:bg-[#FFFFFF1A] text-black dark:text-white rounded-lg cursor-pointer"
onClick={() => handleRowClick(validator)}
role="row"
aria-label={`Validator ${validator.description.moniker}`}
>
<td className="rounded-l-[12px] py-4">
<div className="flex items-center space-x-3">
{validator.logo_url ? (
<Image
height={32}
width={32}
src={validator.logo_url}
alt=""
className="w-8 h-8 rounded-full"
/>
) : (
<ProfileAvatar walletAddress={validator.operator_address} size={32} />
)}
<span className="font-medium">{validator.description.moniker}</span>
</div>
</td>

<td className="py-4">
<TruncatedAddressWithCopy slice={10} address={validator.operator_address} />
</td>
<td className="py-4">{validator.consensus_power?.toString() ?? 'N/A'}</td>
<td className="rounded-r-[12px] py-4 text-right">
<button
onClick={e => {
e.stopPropagation();
handleRemove(validator);
}}
className="btn btn-error btn-sm text-white "
>
<TrashIcon className="w-5 h-5" />
</button>
</td>
Active
</button>
<button
onClick={() => setActive(false)}
className={`flex-1 py-2 px-4 text-sm font-medium rounded-xl ${
!active
? 'dark:bg-[#FFFFFF1F] bg-[#FFFFFF] text-[#161616] dark:text-white'
: 'text-[#808080]'
}`}
>
Pending
</button>
</div>
<div className="flex-1 overflow-auto">
<div className="max-w-8xl mx-auto">
<table
className="table w-full border-separate border-spacing-y-3"
role="grid"
aria-label="Validators list"
>
<thead>
<tr className="text-sm font-medium text-[#808080]" role="row">
<th className="bg-transparent text-left sticky top-0 bg-base-100 z-10">
Moniker
</th>
<th className="bg-transparent text-left sticky top-0 bg-base-100 z-10">
Address
</th>
<th className="bg-transparent text-left sticky top-0 bg-base-100 z-10">
Consensus Power
</th>
<th className="bg-transparent text-right sticky top-0 bg-base-100 z-10">
Remove
</th>
</tr>
))}
</tbody>
</table>
)}
</thead>
<tbody className="space-y-4">
{isLoading
? Array(4)
.fill(0)
.map((_, index) => (
<tr key={index}>
<td className="dark:bg-[#FFFFFF0F] bg-[#FFFFFF] rounded-l-[12px] w-1/6">
<div className="flex items-center space-x-3">
<div className="skeleton w-10 h-8 rounded-full shrink-0"></div>
<div className="skeleton h-3 w-24"></div>
</div>
</td>
<td className="dark:bg-[#FFFFFF0F] bg-[#FFFFFF] w-1/6">
<div className="skeleton h-2 w-24"></div>
</td>
<td className="dark:bg-[#FFFFFF0F] bg-[#FFFFFF] w-1/6">
<div className="skeleton h-2 w-8"></div>
</td>
<td className="dark:bg-[#FFFFFF0F] bg-[#FFFFFF] w-1/6 rounded-r-[12px] text-right">
<div className="skeleton h-2 w-8 ml-auto"></div>
</td>

Check warning on line 165 in components/admins/components/validatorList.tsx

View check run for this annotation

Codecov / codecov/patch

components/admins/components/validatorList.tsx#L147-L165

Added lines #L147 - L165 were not covered by tests
</tr>
))
: filteredValidators.map(validator => (
<tr
key={validator.operator_address}
className="bg-[#FFFFFFCC] dark:bg-[#FFFFFF0F] hover:bg-[#FFFFFF66] dark:hover:bg-[#FFFFFF1A] text-black dark:text-white rounded-lg cursor-pointer"
onClick={() => handleRowClick(validator)}
role="row"
aria-label={`Validator ${validator.description.moniker}`}
>
<td className="rounded-l-[12px] py-4">
<div className="flex items-center space-x-3">
{validator.logo_url ? (
<Image
height={32}
width={32}
src={validator.logo_url}
alt=""
className="w-8 h-8 rounded-full"

Check warning on line 184 in components/admins/components/validatorList.tsx

View check run for this annotation

Codecov / codecov/patch

components/admins/components/validatorList.tsx#L179-L184

Added lines #L179 - L184 were not covered by tests
/>
) : (
Comment on lines +179 to +186
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add error handling for validator logos

The Image component should handle loading errors gracefully to prevent UI issues when logos fail to load.

 <Image
   height={32}
   width={32}
   src={validator.logo_url}
   alt=""
+  alt={`${validator.description.moniker} logo`}
+  onError={(e) => {
+    e.currentTarget.onerror = null;
+    e.currentTarget.src = ''; // Will trigger the fallback avatar
+  }}
   className="w-8 h-8 rounded-full"
 />

Committable suggestion skipped: line range outside the PR's diff.

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 179-184: components/admins/components/validatorList.tsx#L179-L184
Added lines #L179 - L184 were not covered by tests

<ProfileAvatar
walletAddress={validator.operator_address}
size={32}
/>
)}
<span className="font-medium">{validator.description.moniker}</span>
</div>
</td>

<td className="py-4">
<TruncatedAddressWithCopy
slice={10}
address={validator.operator_address}
/>
</td>
<td className="py-4">{validator.consensus_power?.toString() ?? 'N/A'}</td>
<td className="rounded-r-[12px] py-4 text-right">
<button
onClick={e => {
e.stopPropagation();

Check warning on line 206 in components/admins/components/validatorList.tsx

View check run for this annotation

Codecov / codecov/patch

components/admins/components/validatorList.tsx#L205-L206

Added lines #L205 - L206 were not covered by tests
handleRemove(validator);
}}
className="btn btn-error btn-sm text-white "
>
<TrashIcon className="w-5 h-5" />
</button>
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>

<ValidatorDetailsModal
key={modalKey}
validator={selectedValidator}
modalId={modalId || ''}
admin={admin}
totalvp={totalvp.toString()}
validatorVPArray={validatorVPArray}
/>
<WarningModal
admin={admin}
isActive={active}
address={validatorToRemove?.operator_address || ''}
moniker={validatorToRemove?.description.moniker || ''}
modalId="warning-modal"
/>
</div>
</div>

<ValidatorDetailsModal
key={modalKey}
validator={selectedValidator}
modalId={modalId || ''}
admin={admin}
totalvp={totalvp.toString()}
validatorVPArray={validatorVPArray}
/>
<WarningModal
admin={admin}
isActive={active}
address={validatorToRemove?.operator_address || ''}
moniker={validatorToRemove?.description.moniker || ''}
modalId="warning-modal"
/>
</div>
);
}
36 changes: 31 additions & 5 deletions components/wallet.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import { MouseEventHandler, useMemo, useState, useEffect } from 'react';
import React, { MouseEventHandler, useEffect, useMemo, useState } from 'react';

import { ArrowPathIcon, ArrowDownTrayIcon } from '@heroicons/react/24/outline';
import { ArrowUpIcon } from './icons';
import { ArrowDownTrayIcon, ArrowPathIcon } from '@heroicons/react/24/outline';
import { ArrowUpIcon, CopyIcon, GroupsIcon } from './icons';
import { useChain } from '@cosmos-kit/react';
import { WalletStatus } from 'cosmos-kit';

import { CopyIcon } from './icons';
import { MdWallet } from 'react-icons/md';

const buttons = {
Disconnected: {
icon: MdWallet,
Expand Down Expand Up @@ -249,3 +248,30 @@

return _renderConnectButton;
};

export function WalletNotConnected({
description,
icon,
}: {
description: string;
icon: JSX.Element;
}) {
return (
<section className="transition-opacity duration-300 h-[80vh] ease-in-out animate-fadeIn w-full flex items-center justify-center">
<div className="grid max-w-4xl bg-base-300 p-12 rounded-md w-full mx-auto gap-8 lg:grid-cols-12">
<div className="mr-auto place-self-center lg:col-span-7">
<h1 className="max-w-2xl mb-4 text-2xl font-extrabold tracking-tight leading-none md:text-3xl xl:text-4xl dark:text-white text-black">
Connect your wallet!
</h1>
<p className="max-w-2xl mb-6 font-light text-gray-500 lg:mb-8 md:text-lg lg:text-xl">
{description}
</p>
<div className="w-[50%]">
<WalletSection chainName="manifest" />
</div>
</div>
<div className="hidden lg:mt-0 lg:ml-24 lg:col-span-5 lg:flex">{icon}</div>
</div>

Check warning on line 274 in components/wallet.tsx

View check run for this annotation

Codecov / codecov/patch

components/wallet.tsx#L252-L274

Added lines #L252 - L274 were not covered by tests
</section>
);
}
Loading
Loading