diff --git a/components/admins/components/__tests__/adminOptions.test.tsx b/components/admins/components/__tests__/adminOptions.test.tsx deleted file mode 100644 index a1b754a..0000000 --- a/components/admins/components/__tests__/adminOptions.test.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import { afterEach, describe, expect, test } from 'bun:test'; -import React from 'react'; -import { screen, cleanup, within, fireEvent } from '@testing-library/react'; -import AdminOptions from '@/components/admins/components/adminOptions'; -import matchers from '@testing-library/jest-dom/matchers'; -import { renderWithChainProvider } from '@/tests/render'; -import { mockPoaParams, mockGroup } from '@/tests/mock'; - -expect.extend(matchers); - -const renderWithProps = (props = {}) => { - const defaultProps = { - poaParams: mockPoaParams, - group: mockGroup, - isLoading: false, - address: 'test_address', - admin: 'admin1', - }; - return renderWithChainProvider(); -}; - -describe('AdminOptions', () => { - afterEach(cleanup); - - test('renders loading state correctly', () => { - renderWithProps({ isLoading: true }); - expect(screen.getByText('Admin')).toBeInTheDocument(); - }); - - test('renders admin details correctly when not loading', () => { - renderWithProps(); - expect(screen.getByText('Admin')).toBeInTheDocument(); - expect(screen.getByAltText('Profile Avatar')).toBeInTheDocument(); - const titleContainer = screen.getByLabelText('title'); - expect(within(titleContainer).getByText('title1')).toBeInTheDocument(); - const detailsContainer = screen.getByLabelText('details'); - expect(within(detailsContainer).getByText('details1')).toBeInTheDocument(); - }); - - test('opens description modal on button click', () => { - renderWithProps(); - fireEvent.click(screen.getByLabelText('three-dots')); - const modal = document.getElementById('description-modal') as HTMLDialogElement; - expect(modal).toBeInTheDocument(); - expect(modal.open).toBe(true); - }); -}); diff --git a/components/admins/components/adminOptions.tsx b/components/admins/components/adminOptions.tsx deleted file mode 100644 index a8a5e46..0000000 --- a/components/admins/components/adminOptions.tsx +++ /dev/null @@ -1,71 +0,0 @@ -import React from 'react'; -import { ExtendedGroupType } from '@/hooks'; - -import { BsThreeDots } from 'react-icons/bs'; -import { DescriptionModal } from '../modals/descriptionModal'; -import ProfileAvatar from '@/utils/identicon'; - -interface AdminOptionsProps { - group: ExtendedGroupType; - isLoading: boolean; - address: string; - admin: string; -} - -export default function AdminOptions({ group, isLoading }: Readonly) { - const handleOpen = () => { - const modal = document.getElementById(`update-admin-modal`) as HTMLDialogElement; - modal?.showModal(); - }; - - const handleDescription = () => { - const modal = document.getElementById(`description-modal`) as HTMLDialogElement; - modal?.showModal(); - }; - - return ( - - - Admin - - - {isLoading && } - {!isLoading && ( - - - - - - {group?.ipfsMetadata?.title} - - - - {group?.ipfsMetadata?.details} - - - - - - - - Update - - - - )} - - - - ); -} diff --git a/components/admins/components/index.tsx b/components/admins/components/index.tsx index c20c7a2..b2a1351 100644 --- a/components/admins/components/index.tsx +++ b/components/admins/components/index.tsx @@ -1,3 +1,2 @@ -export * from './adminOptions'; export * from './stakingParams'; export * from './validatorList'; diff --git a/components/admins/components/validatorList.tsx b/components/admins/components/validatorList.tsx index 6b2cdeb..bf1dba7 100644 --- a/components/admins/components/validatorList.tsx +++ b/components/admins/components/validatorList.tsx @@ -80,7 +80,7 @@ export default function ValidatorList({ Validators @@ -89,7 +89,7 @@ export default function ValidatorList({ setSearchTerm(e.target.value)} /> @@ -97,13 +97,11 @@ export default function ValidatorList({ - + 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 ? 'bg-base-300 text-secondary-content' : 'text-gray-500' }`} > Active @@ -111,9 +109,7 @@ export default function ValidatorList({ 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]' + !active ? 'bg-base-300 text-secondary-content' : 'text-gray-500' }`} > Pending @@ -148,19 +144,19 @@ export default function ValidatorList({ .fill(0) .map((_, index) => ( - + - + - + - + @@ -168,12 +164,12 @@ export default function ValidatorList({ : filteredValidators.map(validator => ( handleRowClick(validator)} role="row" aria-label={`Validator ${validator.description.moniker}`} > - + {validator.logo_url ? ( - + - {validator.consensus_power?.toString() ?? 'N/A'} - + + {validator.consensus_power?.toString() ?? 'N/A'} + + { e.stopPropagation(); diff --git a/components/groups/components/CountdownTimer.tsx b/components/groups/components/CountdownTimer.tsx index d8d95ce..66c9b0f 100644 --- a/components/groups/components/CountdownTimer.tsx +++ b/components/groups/components/CountdownTimer.tsx @@ -26,9 +26,9 @@ export default function CountdownTimer({ endTime }: { endTime: Date }) { return ( - + - + { }), })); renderWithChainProvider(); - expect(screen.getByText('No proposals found')).toBeInTheDocument(); + expect(screen.getByText('No proposal was found.')).toBeInTheDocument(); }); test('renders proposals list correctly', () => { diff --git a/components/groups/components/groupProposals.tsx b/components/groups/components/groupProposals.tsx index e905b62..95c9455 100644 --- a/components/groups/components/groupProposals.tsx +++ b/components/groups/components/groupProposals.tsx @@ -241,15 +241,12 @@ export default function GroupProposals({ return ( {/* Header section */} - - - + + + - {groupName} + {groupName} @@ -269,25 +266,25 @@ export default function GroupProposals({ {/* Search and New Proposal section */} - - Proposals + + Proposals setSearchTerm(e.target.value)} /> - + - - - New proposal - - + + + New proposal + + {/* Table section - will fill remaining space */} @@ -357,25 +354,25 @@ export default function GroupProposals({ handleRowClick(proposal)} - className="hover:bg-base-200 text-black dark:text-white rounded-lg cursor-pointer" + className="group text-black dark:text-white rounded-lg cursor-pointer" > - + {proposal.id.toString()} - + {proposal.title} - + {timeLeft} - + {proposal.messages.length > 0 ? proposal.messages.map((message, index) => ( {getHumanReadableType((message as any)['@type'])} )) : 'No messages'} - + {isTalliesLoading ? ( ) : ( @@ -388,7 +385,7 @@ export default function GroupProposals({ ) : ( - No proposals found + No proposal was found. )} diff --git a/components/groups/components/myGroups.tsx b/components/groups/components/myGroups.tsx index 55e581e..60c0b7d 100644 --- a/components/groups/components/myGroups.tsx +++ b/components/groups/components/myGroups.tsx @@ -83,7 +83,7 @@ export function YourGroups({ My groups @@ -92,7 +92,7 @@ export function YourGroups({ setSearchTerm(e.target.value)} /> @@ -109,71 +109,69 @@ export function YourGroups({ - - - - - Group Name - Active proposals - Authors - Group Balance - Qualified Majority - Group address - - - - {isLoading - ? // Skeleton - Array(12) - .fill(0) - .map((_, index) => ( - - - - - - - - - - - - - - - - - - - - - - - - )) - : // content - filteredGroups.map((group, index) => ( - 0 - ? proposals[group.policies[0].address] - : [] - } - onSelectGroup={(policyAddress, groupName) => - handleSelectGroup( - policyAddress, - groupName, - (group.policies[0]?.decision_policy as ThresholdDecisionPolicySDKType) - ?.threshold ?? '0' - ) - } - /> - ))} - - - + + + + Group Name + Active proposals + Authors + Group Balance + Qualified Majority + Group address + + + + {isLoading + ? // Skeleton + Array(12) + .fill(0) + .map((_, index) => ( + + + + + + + + + + + + + + + + + + + + + + + + )) + : // content + filteredGroups.map((group, index) => ( + 0 + ? proposals[group.policies[0].address] + : [] + } + onSelectGroup={(policyAddress, groupName) => + handleSelectGroup( + policyAddress, + groupName, + (group.policies[0]?.decision_policy as ThresholdDecisionPolicySDKType) + ?.threshold ?? '0' + ) + } + /> + ))} + + @@ -226,7 +224,7 @@ function GroupRow({ return ( { e.stopPropagation(); onSelectGroup( @@ -238,33 +236,35 @@ function GroupRow({ ); }} > - + {truncateString(groupName, 24)} - + {activeProposals.length > 0 ? ( - {activeProposals.length} + + {activeProposals.length} + ) : ( '-' )} - + {truncateString( getAuthor(group.ipfsMetadata?.authors) || 'Unknown', getAuthor(group.ipfsMetadata?.authors || '').startsWith('manifest1') ? 6 : 24 )} - + {Number(shiftDigits(balance?.amount ?? '0', -6)).toLocaleString(undefined, { maximumFractionDigits: 6, })}{' '} MFX - {`${(group.policies[0]?.decision_policy as ThresholdDecisionPolicySDKType).threshold ?? '0'} / ${group.total_weight ?? '0'}`} - + {`${(group.policies[0]?.decision_policy as ThresholdDecisionPolicySDKType).threshold ?? '0'} / ${group.total_weight ?? '0'}`} + diff --git a/components/groups/modals/voteDetailsModal.tsx b/components/groups/modals/voteDetailsModal.tsx index 140f409..e6aa8d7 100644 --- a/components/groups/modals/voteDetailsModal.tsx +++ b/components/groups/modals/voteDetailsModal.tsx @@ -66,7 +66,7 @@ function VoteDetailsModal({ const { theme } = useTheme(); - const textColor = theme === 'dark' ? '#E0D1D4' : '#2e2e2e'; + const textColor = theme === 'dark' ? '#FFFFFF' : '#161616'; const normalizedMembers = useMemo( () => @@ -130,6 +130,9 @@ function VoteDetailsModal({ labels: { useSeriesColors: true, }, + markers: { + strokeWidth: 0, + }, }, states: { normal: { @@ -182,7 +185,7 @@ function VoteDetailsModal({ data: chartData, }, ], - colors: ['#00D515', '#F54562', '#FBBD23', '#3B82F6'], + colors: ['#4CAF50', '#E53935', '#FFB300', '#3F51B5'], tooltip: { enabled: false, }, @@ -349,9 +352,9 @@ function VoteDetailsModal({ if (Array.isArray(value)) { return ( - {key}: + {key}: {value.map((item, index) => ( - + {renderMessageField(`Item ${index + 1}`, item, depth + 1)} ))} @@ -360,7 +363,7 @@ function VoteDetailsModal({ } else { return ( - {key}: + {key}: {Object.entries(value).map(([subKey, subValue]) => renderMessageField(subKey, subValue, depth + 1) )} @@ -370,11 +373,13 @@ function VoteDetailsModal({ } else { return ( - {key}: + {key}: {typeof value === 'string' && value.match(/^[a-zA-Z0-9]{40,}$/) ? ( ) : ( - {truncateText(String(value))} + + {truncateText(String(value))} + )} ); @@ -416,21 +421,25 @@ function VoteDetailsModal({ return ( - + - ✕ + + ✕ + - #{proposal?.id?.toString() ?? '0'} - + + #{proposal?.id?.toString() ?? '0'} + + {getStatusLabel(proposal)} {userHasVoted && ( - Your vote: + Your vote: TITLE - {proposal?.title} + + {proposal?.title} + SUBMITTED - + {new Date(proposal?.submit_time).toDateString().toLocaleString()} SUMMARY - - {proposal?.summary} + + {proposal?.summary} - MESSAGES - + MESSAGES + {proposal?.messages?.map((message: any, index: number) => { const messageType = message['@type']; const fieldsToShow = importantFields[messageType] || defaultFields; return ( - + {messageType.split('.').pop().replace('Msg', '')} @@ -497,24 +508,30 @@ function VoteDetailsModal({ TALLY - + MEMBERS - - + + - + - + Address - + Weight - + Vote @@ -523,12 +540,12 @@ function VoteDetailsModal({ {normalizedMembers?.map((member, index) => { const memberVote = voteMap[member?.address]; return ( - + - {member?.weight} - + {member?.weight} + {optionToVote(memberVote?.toString()) || 'N/A'} diff --git a/tailwind.config.js b/tailwind.config.js index c0adb73..2985fff 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -78,50 +78,48 @@ module.exports = { themes: [ { light: { - 'wallet-background': '#1c1c1c1a', - primary: '#A287FF', + primary: '#A087FF', 'primary-content': '#161616', - secondary: '#F0F0FF', + secondary: '#FBFBFF', 'secondary-content': '#161616', accent: '#30DB5B', 'accent-content': '#161616', neutral: '#FFFFFF', 'button-gradient': 'linear-gradient(to right, #A087FF, #380CC5)', - 'neutral-content': '#161616', + 'neutral-content': '#FFFFFF', 'base-100': '#FFFFFF', 'base-200': '#F0F0FF', - 'base-300': '#E1E1F9', + 'base-300': '#F5F5F5', 'base-content': '#161616', 'tooltip-color': '#ffffff', 'background-color': '#F0F0FF', - info: '#3B82F6', - success: '#00D515', - warning: '#FBBD23', - error: '#F54562', + info: '#3F51B5', + success: '#4CAF50', + warning: '#FFB300', + error: '#E53935', }, }, { dark: { - 'wallet-background': '#dcdcdc15', - primary: '#A287FF', + primary: '#A087FF', 'primary-content': '#FFFFFF', secondary: '#1D192D', 'secondary-content': '#FFFFFF', accent: '#30DB5B', 'accent-content': '#FFFFFF', - 'button-gradient': 'linear-gradient(to right, #A087FF, #380CC5)', neutral: '#1D192D', + 'button-gradient': 'linear-gradient(to right, #A087FF, #380CC5)', 'neutral-content': '#FFFFFF', - 'background-color': '#0E0A1F', 'base-100': '#161616', 'base-200': '#1D192D', - 'tooltip-color': '#ffffff', - 'base-300': '#2A2640', + 'base-300': '#272336', 'base-content': '#FFFFFF', - info: '#3B82F6', - success: '#00D515', - warning: '#FBBD23', - error: '#F54562', + 'tooltip-color': '#ffffff', + 'background-color': '#0E0A1F', + info: '#3F51B5', + success: '#4CAF50', + warning: '#FFB300', + error: '#E53935', }, }, ],
{truncateText(String(value))}
+ {truncateText(String(value))} +
#{proposal?.id?.toString() ?? '0'}
+ #{proposal?.id?.toString() ?? '0'} +
TITLE
SUMMARY
{proposal?.summary}
MESSAGES
TALLY
MEMBERS