diff --git a/.circleci/config.yml b/.circleci/config.yml index 5ddd8e9cd..c8e0f690f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -142,65 +142,65 @@ jobs: - run: git diff --name-only --exit-code - run: yarn typecheck - governance-e2e: - parameters: - chainId: - type: integer - preset: - type: string - provider-url: - type: string - working_directory: /tmp/app - docker: - - image: cypress/included:<< pipeline.parameters.cypress-version >> - resource_class: large - environment: - NODE_ENV: test - CYPRESS_CHAIN_ID: << parameters.chainId >> - CYPRESS_PRESET: << parameters.preset >> - TESTNET: 1 - steps: - - checkout - - install-foundry - - yarn-install - - - run: - name: Run server localhost:3000 - working_directory: /tmp/app/governance/ui - command: yarn start - background: true - - - run: - name: Run anvil localhost:8545 - command: anvil --fork-url "<< parameters.provider-url >>" - background: true - - - run: - name: Wait for server localhost:3000 - command: wget --retry-connrefused --waitretry=20 --read-timeout=20 --timeout=15 -t 10 http://localhost:3000 - - - run: - name: Wait for anvil localhost:8545 - command: wget -q -O - --retry-connrefused --waitretry=20 --read-timeout=20 --timeout=15 -t 10 --post-data='{"method":"eth_chainId","params":[],"id":1,"jsonrpc":"2.0"}' --header='Content-Type:application/json' http://localhost:8545 - - - run: - working_directory: /tmp/app/governance/cypress - command: cypress run --e2e --browser chrome - - - store_test_results: - path: 'governance/cypress/cypress/reports' - - - store_artifacts: - path: 'governance/cypress/.nyc_output' - destination: 'coverage' - - - store_artifacts: - path: 'governance/cypress/cypress/screenshots' - destination: 'screenshots' - - - store_artifacts: - path: 'governance/cypress/cypress/videos' - destination: 'videos' + # governance-e2e: + # parameters: + # chainId: + # type: integer + # preset: + # type: string + # provider-url: + # type: string + # working_directory: /tmp/app + # docker: + # - image: cypress/included:<< pipeline.parameters.cypress-version >> + # resource_class: large + # environment: + # NODE_ENV: test + # CYPRESS_CHAIN_ID: << parameters.chainId >> + # CYPRESS_PRESET: << parameters.preset >> + # TESTNET: 1 + # steps: + # - checkout + # - install-foundry + # - yarn-install + + # - run: + # name: Run server localhost:3000 + # working_directory: /tmp/app/governance/ui + # command: yarn start + # background: true + + # - run: + # name: Run anvil localhost:8545 + # command: anvil --fork-url "<< parameters.provider-url >>" + # background: true + + # - run: + # name: Wait for server localhost:3000 + # command: wget --retry-connrefused --waitretry=20 --read-timeout=20 --timeout=15 -t 10 http://localhost:3000 + + # - run: + # name: Wait for anvil localhost:8545 + # command: wget -q -O - --retry-connrefused --waitretry=20 --read-timeout=20 --timeout=15 -t 10 --post-data='{"method":"eth_chainId","params":[],"id":1,"jsonrpc":"2.0"}' --header='Content-Type:application/json' http://localhost:8545 + + # - run: + # working_directory: /tmp/app/governance/cypress + # command: cypress run --e2e --browser chrome + + # - store_test_results: + # path: 'governance/cypress/cypress/reports' + + # - store_artifacts: + # path: 'governance/cypress/.nyc_output' + # destination: 'coverage' + + # - store_artifacts: + # path: 'governance/cypress/cypress/screenshots' + # destination: 'screenshots' + + # - store_artifacts: + # path: 'governance/cypress/cypress/videos' + # destination: 'videos' liquidity-e2e: parameters: @@ -300,7 +300,7 @@ jobs: - run: rm -rf /tmp/cov - run: yarn download-cci-coverage tests /tmp/cov || true - run: yarn download-cci-coverage liquidity-cy /tmp/cov || true - - run: yarn download-cci-coverage governance-cy /tmp/cov || true + # - run: yarn download-cci-coverage governance-cy /tmp/cov || true - run: yarn download-cci-coverage liquidity-e2e-base-mainnet /tmp/cov || true - run: yarn download-cci-coverage liquidity-e2e-optimism-mainnet /tmp/cov || true - run: mkdir -p /tmp/cov @@ -350,11 +350,11 @@ workflows: - typecheck - tests - liquidity-cy - - governance-e2e: - name: governance-e2e-sepolia - chainId: 11155111 - preset: main - provider-url: https://sepolia.infura.io/v3/$INFURA_KEY + # - governance-e2e: + # name: governance-e2e-sepolia + # chainId: 11155111 + # preset: main + # provider-url: https://sepolia.infura.io/v3/$INFURA_KEY - liquidity-e2e: name: liquidity-e2e-base-mainnet chainId: 8453 @@ -369,7 +369,7 @@ workflows: requires: [ tests, liquidity-cy, - governance-e2e-sepolia, + # governance-e2e-sepolia, liquidity-e2e-base-mainnet, #liquidity-e2e-sepolia, ] diff --git a/governance/cypress/cypress/e2e/Councils - Administration.e2e.js b/governance/cypress/cypress/e2e/Councils - Administration.e2e.js new file mode 100644 index 000000000..d773c2f14 --- /dev/null +++ b/governance/cypress/cypress/e2e/Councils - Administration.e2e.js @@ -0,0 +1,31 @@ +it('Councils - Administration', () => { + cy.connectWallet(); + cy.viewport(1300, 900); + cy.visit('/'); + cy.get('[data-cy="view-council-button-spartan"]').should('exist'); + cy.get('[data-cy="countdown-timer"]').should('exist'); + cy.get('[data-cy="council-period-badge"]').contains('Closed - Council Elected'); + cy.get('[data-cy="council-tab-vote-circle"]').should('not.exist'); + cy.get('[data-cy="view-council-button-spartan"]').click(); + cy.get('[data-cy="election-closed-tag"]').should('exist'); + cy.get('[data-cy="number-table-header"]').click(); + cy.get('[data-cy="sort-arrow-up"]').should('exist'); + cy.get('[data-cy="sort-arrow-up"]').click(); + cy.get('[data-cy="sort-arrow-down"]').should('exist'); + cy.get('[data-cy="name-table-header"]').click(); + cy.get('[data-cy="sort-arrow-up"]').should('exist'); + cy.get('[data-cy="sort-arrow-up"]').click(); + cy.get('[data-cy="sort-arrow-down"]').should('exist'); + cy.get('[data-cy="votes-table-header"]').click(); + cy.get('[data-cy="sort-arrow-up"]').should('exist'); + cy.get('[data-cy="sort-arrow-up"]').click(); + cy.get('[data-cy="sort-arrow-down"]').should('exist'); + cy.get('[data-cy="voting-power-table-header"]').click(); + cy.get('[data-cy="sort-arrow-up"]').should('exist'); + cy.get('[data-cy="sort-arrow-up"]').click(); + cy.get('[data-cy="sort-arrow-down"]').should('exist'); + cy.get('[data-cy="user-table-row-0"]').click(); + cy.url().then((url) => { + cy.get(`[data-cy="user-profile-card-${url.split('view=')[1]}"]`); + }); +}); diff --git a/governance/cypress/cypress/e2e/Councils - Connected User Profile.e2e.js b/governance/cypress/cypress/e2e/Councils - Connected User Profile.e2e.js deleted file mode 100644 index 07ecb2353..000000000 --- a/governance/cypress/cypress/e2e/Councils - Connected User Profile.e2e.js +++ /dev/null @@ -1,13 +0,0 @@ -it('shows user profile card', () => { - cy.connectWallet(); - cy.viewport(1300, 900); - cy.visit('/'); - - cy.get('@wallet').then((wallet) => { - cy.visit(`/#/councils/spartan?view=${wallet.address}`); - cy.contains( - '[data-testid="user-wallet-profile-address"]', - wallet.address.substring(0, 4) - ).should('exist'); - }); -}); diff --git a/governance/cypress/cypress/e2e/Councils - Nomination Period.e2e.js b/governance/cypress/cypress/e2e/Councils - Nomination Period.e2e.js deleted file mode 100644 index d78c6b12d..000000000 --- a/governance/cypress/cypress/e2e/Councils - Nomination Period.e2e.js +++ /dev/null @@ -1,9 +0,0 @@ -it('shows council card and header in nomination period', () => { - cy.connectWallet().then((signer) => { - cy.task('setEthBalance', { address: signer.address, balance: 100 }); - cy.task('setToNominationPeriod'); - }); - cy.viewport(1300, 900); - cy.visit('/#/councils/spartan'); - cy.contains('[data-testid="period-countdown"]', 'Voting starts').should('exist'); -}); diff --git a/governance/cypress/cypress/e2e/Councils - Nomination.e2e.js b/governance/cypress/cypress/e2e/Councils - Nomination.e2e.js new file mode 100644 index 000000000..b9526d12f --- /dev/null +++ b/governance/cypress/cypress/e2e/Councils - Nomination.e2e.js @@ -0,0 +1,32 @@ +it('Councils - Administration', () => { + cy.connectWallet(); + cy.viewport(1300, 900); + cy.visit('/'); + cy.get('[data-cy="view-council-button-spartan"]').should('exist'); + cy.get('[data-cy="council-period-badge"]').contains('Nomination Open'); + cy.get('[data-cy="council-tab-vote-circle"]').should('not.exist'); + cy.get('[data-cy="view-council-button-spartan"]').click(); + cy.get('[data-cy="period-countdown"]').should('exist'); + cy.get('[data-cy="period-countdown"]').contains('Voting starts'); + cy.get('[data-cy="name-table-header"]').click(); + cy.get('[data-cy="sort-arrow-up"]').should('exist'); + cy.get('[data-cy="sort-arrow-up"]').click(); + cy.get('[data-cy="sort-arrow-down"]').should('exist'); + cy.get('[data-cy="own-user-list-item"]').click(); + cy.get('[data-cy="nominate-self-button-user-profile-details"]').click(); + cy.get('[data-cy="council-nomination-select-spartan"]').click(); + cy.get('[data-cy="nominate-self-cast-nomination-button"]').click(); + cy.get('[data-cy="nominate-self-done-button"]').click(); + cy.get('[data-cy="empty-state-user-action-box"]').contains( + 'Click on nominee to see their profile details' + ); + cy.get('[data-cy="user-table-view-button"]').click(); + cy.get('[data-cy="nominate-self-button-user-profile-details"]').click(); + cy.get('[data-cy="withdraw-vote-select"]').click(); + cy.get('[data-cy="edit-nomination-button"]').click(); + cy.get('[data-cy="confirm-edit-nomination-button"]').click(); + cy.get('[data-cy="empty-state-user-action-box"]').contains( + 'Click on nominee to see their profile details' + ); + cy.get('[data-cy="user-table-row-0"]').should('not.exist'); +}); diff --git a/governance/cypress/cypress/e2e/Councils - Voting.e2e.js b/governance/cypress/cypress/e2e/Councils - Voting.e2e.js new file mode 100644 index 000000000..4088286a2 --- /dev/null +++ b/governance/cypress/cypress/e2e/Councils - Voting.e2e.js @@ -0,0 +1,29 @@ +it('Councils - Administration', () => { + cy.connectWallet(); + cy.viewport(1300, 900); + cy.visit('/'); + cy.get('[data-cy="vote-council-button-spartan"]').should('exist'); + cy.get('[data-cy="council-period-badge"]').contains('Voting Open'); + cy.get('[data-cy="council-tab-vote-circle"]').should('not.exist'); + cy.get('[data-cy="vote-council-button-spartan"]').click(); + cy.get('[data-cy="period-countdown"]').should('exist'); + cy.get('[data-cy="period-countdown"]').contains('Voting ends'); + cy.get('[data-cy="name-table-header"]').click(); + cy.get('[data-cy="sort-arrow-up"]').should('exist'); + cy.get('[data-cy="sort-arrow-up"]').click(); + cy.get('[data-cy="sort-arrow-down"]').should('exist'); + cy.get('[data-cy="own-user-list-item"]').click(); + cy.get('[data-cy="nominate-self-button-user-profile-details-voting-period"]').click(); + cy.get('[data-cy="council-nomination-select-spartan"]').click(); + cy.get('[data-cy="nominate-self-cast-nomination-button"]').click(); + cy.get('[data-cy="nominate-self-done-button"]').click(); + cy.get('[data-cy="empty-state-user-action-box"]').contains( + 'Click on nominee to see their profile details' + ); + cy.get('[data-cy="own-user-list-item"]').click(); + cy.get('[data-cy="select-user-to-vote-button"]').click(); + cy.get('[data-cy="my-votes-button"]').click(); + cy.get( + '[data-cy="user-blockies-council-tabs-0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"]' + ).should('exist'); +}); diff --git a/governance/cypress/cypress/e2e/Edit - Profile.e2e.js b/governance/cypress/cypress/e2e/Edit - Profile.e2e.js new file mode 100644 index 000000000..a34ad534f --- /dev/null +++ b/governance/cypress/cypress/e2e/Edit - Profile.e2e.js @@ -0,0 +1,35 @@ +it('should let user edit their profile', () => { + cy.connectWallet(); + cy.viewport(1300, 900); + cy.visit('/'); + cy.get("[data-cy='header-wallet-address-button']").click(); + cy.get("[data-cy='edit-profile-button-link-header']").click(); + + cy.get("[data-cy='username-input']").type('test username'); + cy.get("[data-cy='username-preview']").contains('test username'); + + cy.get("[data-cy='about-input']").type('its me, the test account'); + cy.get("[data-cy='about-preview']").contains('its me, the test account'); + + cy.get("[data-cy='discord-input']").type('kaiynne'); + cy.get("[data-cy='discord-social-link']").should( + 'have.attr', + 'href', + 'https://discord.com/user/kaiynne' + ); + + cy.get("[data-cy='twitter-input']").type('kaiynne'); + cy.get("[data-cy='twitter-social-link']").should('have.attr', 'href', 'https://x.com/kaiynne'); + + cy.get("[data-cy='github-input']").type('kaiynne'); + cy.get("[data-cy='github-social-link']").should( + 'have.attr', + 'href', + 'https://github.com/kaiynne' + ); + + cy.get("[data-cy='governance-pitch-input']").type('sup its me kaiynne'); + cy.get("[data-cy='governance-pitch-preview']").contains('sup its me kaiynne'); + + cy.get("[data-cy='save-profile-changes-button']").should('exist'); +}); diff --git a/governance/cypress/cypress/e2e/Home - Connected.e2e.js b/governance/cypress/cypress/e2e/Home - Connected.e2e.js index 82dbef13d..1adf3fefd 100644 --- a/governance/cypress/cypress/e2e/Home - Connected.e2e.js +++ b/governance/cypress/cypress/e2e/Home - Connected.e2e.js @@ -4,8 +4,6 @@ it('shows homepage to a connected wallet', () => { cy.visit('/'); cy.get('@wallet').then((wallet) => { - cy.contains('[data-testid="user-wallet-address"]', wallet.address.substring(0, 6)).should( - 'exist' - ); + cy.contains('[data-cy="user-wallet-address"]', wallet.address.substring(0, 6)).should('exist'); }); }); diff --git a/governance/cypress/cypress/e2e/Home - Nomination Period.e2e.js b/governance/cypress/cypress/e2e/Home - Nomination Period.e2e.js deleted file mode 100644 index 3339aa811..000000000 --- a/governance/cypress/cypress/e2e/Home - Nomination Period.e2e.js +++ /dev/null @@ -1,9 +0,0 @@ -it('shows council card and header in nomination period', () => { - cy.connectWallet().then((signer) => { - cy.task('setEthBalance', { address: signer.address, balance: 100 }); - cy.task('setToNominationPeriod'); - }); - cy.viewport(1300, 900); - cy.visit('/'); - cy.contains('[data-testid="period-countdown"]', 'Voting starts').should('exist'); -}); diff --git a/governance/cypress/cypress/e2e/Home - Not Connected.e2e.js b/governance/cypress/cypress/e2e/Home - Not Connected.e2e.js index f6feddc97..78e65b7f1 100644 --- a/governance/cypress/cypress/e2e/Home - Not Connected.e2e.js +++ b/governance/cypress/cypress/e2e/Home - Not Connected.e2e.js @@ -2,12 +2,8 @@ it('shows homepage to a not connected wallet', () => { cy.viewport(1300, 900); cy.visit('/'); - cy.contains('[data-testid="council-card-header-spartan"]', 'Spartan Council').should('exist'); - cy.contains('[data-testid="council-card-header-grants"]', 'Grants Council').should('exist'); - cy.contains('[data-testid="council-card-header-ambassador"]', 'Ambassador Council').should( - 'exist' - ); - cy.contains('[data-testid="council-card-header-treasury"]', 'Treasury Council').should('exist'); - cy.contains('[data-testid="council-card-header-treasury"]', 'Treasury Council').should('exist'); - cy.contains('[data-testid="connect-wallet-button"]', 'Connect Wallet').should('exist'); + cy.contains('[data-cy="council-card-header-spartan"]', 'Spartan Council').should('exist'); + cy.contains('[data-cy="council-card-header-ambassador"]', 'Ambassador Council').should('exist'); + cy.contains('[data-cy="council-card-header-treasury"]', 'Treasury Council').should('exist'); + cy.contains('[data-cy="connect-wallet-button"]', 'Connect Wallet').should('exist'); }); diff --git a/governance/cypress/cypress/lib/metamask.js b/governance/cypress/cypress/lib/metamask.js index 3f9eb91ef..40773ddf4 100644 --- a/governance/cypress/cypress/lib/metamask.js +++ b/governance/cypress/cypress/lib/metamask.js @@ -1,7 +1,7 @@ import { ethers } from 'ethers'; export function metamask({ pk, address }) { - const provider = new ethers.providers.JsonRpcProvider('http://127.0.0.1:8545'); + const provider = new ethers.providers.JsonRpcProvider('http://127.0.0.1:19000'); return new Proxy(provider, { get(target, prop) { switch (prop) { diff --git a/governance/cypress/cypress/support/e2e.js b/governance/cypress/cypress/support/e2e.js index f898f62e0..5119bf987 100644 --- a/governance/cypress/cypress/support/e2e.js +++ b/governance/cypress/cypress/support/e2e.js @@ -17,14 +17,16 @@ beforeEach(() => { [ 'mainnet', 'optimism-mainnet', + 'optimism-goerli', 'base-mainnet', 'sepolia', 'base-sepolia', 'arbitrum-mainnet', 'arbitrum-sepolia', + 'avalanche-fuji', ].forEach((networkName) => { cy.intercept(`https://${networkName}.infura.io/v3/*`, (req) => { - req.url = 'http://127.0.0.1:8545'; + req.url = 'http://127.0.0.1:19000'; req.continue(); }).as(networkName); }); @@ -42,7 +44,9 @@ beforeEach(() => { }); Cypress.Commands.add('connectWallet', (namespace = 'wallet') => { - const wallet = ethers.Wallet.createRandom(); + const wallet = new ethers.Wallet( + '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80' + ); const privateKey = wallet.privateKey; const address = wallet.address; cy.on('window:before:load', (win) => { diff --git a/governance/cypress/package.json b/governance/cypress/package.json index c5ff47cc3..ccac10b72 100644 --- a/governance/cypress/package.json +++ b/governance/cypress/package.json @@ -2,11 +2,9 @@ "name": "@snx-v3/governance-cypress", "private": true, "main": "index.ts", - "version": "0.0.9", + "version": "0.0.1", "scripts": { - "anvil:sepolia": "anvil --fork-url https://sepolia.infura.io/v3/$INFURA_KEY", - "cy": "NODE_ENV=test cypress open --component --browser chrome", - "e2e:sepolia": "NODE_ENV=test CYPRESS_CHAIN_ID=8453 CYPRESS_PRESET=11155111 cypress open --e2e --browser chrome" + "e2e": "cypress open --e2e --browser chrome" }, "devDependencies": { "@chakra-ui/react": "^2.8.2", diff --git a/governance/ui/.env.example b/governance/ui/.env.example index 0a1ff54ee..e4271c502 100644 --- a/governance/ui/.env.example +++ b/governance/ui/.env.example @@ -1,6 +1,6 @@ INFURA_KEY=xxx WC_PROJECT_ID=xxx BOARDROOM_KEY=xxx -# Optional -DEV=xxx -TESTNET=xxx +DEV=true +DEV_RPC_MOTHERSHIP=http://127.0.0.1:19000/ +TESTNET=false diff --git a/governance/ui/package.json b/governance/ui/package.json index d59ec81b6..0cb1da6b6 100644 --- a/governance/ui/package.json +++ b/governance/ui/package.json @@ -14,11 +14,13 @@ "@emotion/styled": "^11.11.0", "@safe-global/safe-apps-provider": "^0.18.3", "@safe-global/safe-apps-sdk": "^9.1.0", + "@snx-v3/Tooltip": "workspace:*", "@snx-v3/format": "workspace:*", "@snx-v3/formatters": "workspace:*", "@snx-v3/icons": "workspace:*", "@snx-v3/txnReducer": "workspace:*", "@snx-v3/useBlockchain": "workspace:*", + "@snx-v3/useLocalStorage": "workspace:*", "@synthetixio/v3-theme": "workspace:*", "@tanstack/react-query": "^5.8.3", "@web3-onboard/coinbase": "^2.4.1", @@ -35,7 +37,6 @@ "react-dom": "^18.2.0", "react-hook-form": "^7.48.2", "react-router-dom": "^6.18.0", - "react-timer-hook": "^3.0.7", "recoil": "^0.7.7", "siwe": "^2.1.4" }, diff --git a/governance/ui/public/councils/ac1.svg b/governance/ui/public/councils/ac1.svg deleted file mode 100644 index 49e83b9df..000000000 --- a/governance/ui/public/councils/ac1.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/governance/ui/public/councils/gc.svg b/governance/ui/public/councils/gc.svg deleted file mode 100644 index e974bcc26..000000000 --- a/governance/ui/public/councils/gc.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/governance/ui/src/components/CouncilCard/CouncilCard.tsx b/governance/ui/src/components/CouncilCard/CouncilCard.tsx index cc0c67680..225ead2ce 100644 --- a/governance/ui/src/components/CouncilCard/CouncilCard.tsx +++ b/governance/ui/src/components/CouncilCard/CouncilCard.tsx @@ -30,7 +30,7 @@ export function CouncilCard({ council }: CouncilCardProps) { return ( ) : ( + )} @@ -152,6 +193,7 @@ export default function CouncilNominees({ activeCouncil }: { activeCouncil: Coun w="200px" cursor="pointer" userSelect="none" + data-cy="name-table-header" onClick={() => { setSortConfig([!sortConfig[0], 'name']); sortedNominees = sortedNominees.sort((a, b) => { @@ -169,7 +211,7 @@ export default function CouncilNominees({ activeCouncil }: { activeCouncil: Coun Name {sortConfig[1] === 'name' && } {/* @ts-ignore */} {sortConfig[1] === 'start' && sortConfig[1] !== 'name' && ( - + )} Role @@ -180,7 +222,7 @@ export default function CouncilNominees({ activeCouncil }: { activeCouncil: Coun px="0" userSelect="none" textTransform="capitalize" - textAlign="center" + pl="6" onClick={() => { setSortConfig([!sortConfig[0], 'votes']); // sortedNominees = sortedNominees.sort((a, b) => { @@ -196,8 +238,7 @@ export default function CouncilNominees({ activeCouncil }: { activeCouncil: Coun cursor="pointer" userSelect="none" textTransform="capitalize" - textAlign="center" - px="0" + pl="6" onClick={() => { setSortConfig([!sortConfig[0], 'votingPower']); // sortedNominees = sortedNominees.sort((a, b) => { diff --git a/governance/ui/src/components/CouncilTabs/CouncilSelect.tsx b/governance/ui/src/components/CouncilTabs/CouncilSelect.tsx index 20a9585cb..4225c77e2 100644 --- a/governance/ui/src/components/CouncilTabs/CouncilSelect.tsx +++ b/governance/ui/src/components/CouncilTabs/CouncilSelect.tsx @@ -15,7 +15,7 @@ export const CouncilsSelect = ({ activeCouncil }: { activeCouncil: CouncilSlugs flexDirection="row" justifyContent="space-between" height="40px" - width="100%" + minW="300px" bg="navy.700" borderWidth="1px" px="12px" diff --git a/governance/ui/src/components/CouncilTabs/CouncilTabs.tsx b/governance/ui/src/components/CouncilTabs/CouncilTabs.tsx index 6bc280243..af817f0a5 100644 --- a/governance/ui/src/components/CouncilTabs/CouncilTabs.tsx +++ b/governance/ui/src/components/CouncilTabs/CouncilTabs.tsx @@ -17,7 +17,6 @@ export default function CouncilTabs({ activeCouncil }: { activeCouncil?: Council const votedNomineesData = [ useGetUserBallot('spartan'), useGetUserBallot('ambassador'), - useGetUserBallot('grants'), useGetUserBallot('treasury'), ]; @@ -39,10 +38,6 @@ export default function CouncilTabs({ activeCouncil }: { activeCouncil?: Council council: votedNominees[2]?.council, userInformation: useGetUserDetailsQuery(votedNominees[2]?.votedCandidates[0]), }, - { - council: votedNominees[3]?.council, - userInformation: useGetUserDetailsQuery(votedNominees[3]?.votedCandidates[0]), - }, ]; const userInformation = userInformationData.map((data) => ({ @@ -52,7 +47,7 @@ export default function CouncilTabs({ activeCouncil }: { activeCouncil?: Council return ( <> - + - + {council.title} - {userInformation[index].userInformation?.pfpUrl || - !!userInformation[index].userInformation?.address || - newVoteCast ? ( + {councilPeriod === '2' && + (userInformation[index].userInformation?.pfpUrl || + !!userInformation[index].userInformation?.address || + newVoteCast) ? ( ) : ( - + councilPeriod === '2' && ( + + ) )} ); diff --git a/governance/ui/src/components/CouncilUser/CouncilUser.tsx b/governance/ui/src/components/CouncilUser/CouncilUser.tsx index 22e289fd0..6929cfd80 100644 --- a/governance/ui/src/components/CouncilUser/CouncilUser.tsx +++ b/governance/ui/src/components/CouncilUser/CouncilUser.tsx @@ -7,7 +7,6 @@ import { truncateAddress } from '@snx-v3/formatters'; export default function CouncilUser({ address, councilSlug, - hideName = false, }: { address?: string; councilSlug: CouncilSlugs; @@ -19,7 +18,6 @@ export default function CouncilUser({ if (!council) { return null; } - return ( @@ -52,16 +50,14 @@ export default function CouncilUser({ /> )} - {!hideName && ( - - - {council.title} - - - {user ? (user?.username ? user.username : truncateAddress(user?.address)) : 'not found'} - - - )} + + + {council.title} + + + {user ? (user?.username ? user.username : truncateAddress(user?.address)) : 'No Vote'} + + ); } diff --git a/governance/ui/src/components/EditNomination/EditNomination.tsx b/governance/ui/src/components/EditNomination/EditNomination.tsx index f06672ace..c3e9e4559 100644 --- a/governance/ui/src/components/EditNomination/EditNomination.tsx +++ b/governance/ui/src/components/EditNomination/EditNomination.tsx @@ -26,12 +26,14 @@ export default function EditNomination({ activeCouncil }: { activeCouncil: Counc flexDirection="column" bg="navy.700" w="100%" + maxW="451px" borderColor="gray.900" borderWidth="1px" borderStyle="solid" rounded="base" p="6" - position="relative" + position="sticky" + top="81px" > navigate(`/councils/${activeCouncil}`)} @@ -103,6 +105,7 @@ export default function EditNomination({ activeCouncil }: { activeCouncil: Counc /> ) : ( diff --git a/governance/ui/src/components/EditNomination/EditNominationConfirmation.tsx b/governance/ui/src/components/EditNomination/EditNominationConfirmation.tsx index d36a57580..7822929e2 100644 --- a/governance/ui/src/components/EditNomination/EditNominationConfirmation.tsx +++ b/governance/ui/src/components/EditNomination/EditNominationConfirmation.tsx @@ -7,8 +7,8 @@ import { useGetIsNominated } from '../../queries/useGetIsNominated'; import { useWallet } from '../../queries/useWallet'; import { ArrowForwardIcon } from '@chakra-ui/icons'; import { useGetUserDetailsQuery } from '../../queries'; -import { shortAddress } from '../../utils/address'; import { ProfilePicture } from '../UserProfileCard/ProfilePicture'; +import { prettyString } from '@snx-v3/format'; export default function EditNominationConfirmation({ selectedCouncil, @@ -30,8 +30,11 @@ export default function EditNominationConfirmation({ }); useEffect(() => { - if (isSuccess) setShowConfirm(false); - }, [isSuccess, setShowConfirm]); + if (isSuccess) { + setShowConfirm(false); + navigate(`/councils/${activeCouncil}?nominate=false`); + } + }, [isSuccess, setShowConfirm, navigate, activeCouncil]); return ( <> @@ -52,9 +55,9 @@ export default function EditNominationConfirmation({ - {user?.ens || shortAddress(user?.address)} + {user?.ens || prettyString(user!.address)} - Nomination Wallet: {shortAddress(user?.address)} + Nomination Wallet: {prettyString(user!.address)} @@ -169,6 +172,7 @@ export default function EditNominationConfirmation({ onClick={() => { mutate(); }} + data-cy="confirm-edit-nomination-button" > Edit Nomination diff --git a/governance/ui/src/components/EditNomination/EditNominationContainer.tsx b/governance/ui/src/components/EditNomination/EditNominationContainer.tsx index faf7df290..2cde9add0 100644 --- a/governance/ui/src/components/EditNomination/EditNominationContainer.tsx +++ b/governance/ui/src/components/EditNomination/EditNominationContainer.tsx @@ -11,7 +11,7 @@ export const EditNominationContainer = ({ }) => { return ( <> - + @@ -19,7 +19,7 @@ export const EditNominationContainer = ({ - + diff --git a/governance/ui/src/components/EditNomination/EditNominationSelect.tsx b/governance/ui/src/components/EditNomination/EditNominationSelect.tsx index 5245e9ceb..558644020 100644 --- a/governance/ui/src/components/EditNomination/EditNominationSelect.tsx +++ b/governance/ui/src/components/EditNomination/EditNominationSelect.tsx @@ -4,8 +4,8 @@ import { useGetUserDetailsQuery } from '../../queries'; import { useWallet } from '../../queries/useWallet'; import { useGetIsNominated } from '../../queries/useGetIsNominated'; import { Dispatch, SetStateAction } from 'react'; -import { shortAddress } from '../../utils/address'; import { ProfilePicture } from '../UserProfileCard/ProfilePicture'; +import { prettyString } from '@snx-v3/format'; export default function EditNominationSelect({ selectedCouncil, @@ -39,9 +39,9 @@ export default function EditNominationSelect({ - {user?.ens || shortAddress(user?.address)} + {user?.ens || prettyString(user?.address || '')} - Nomination Wallet: {shortAddress(user?.address)} + Nomination Wallet: {prettyString(user?.address || '')} {nominationInformation?.isNominated && ( @@ -129,6 +129,7 @@ export default function EditNominationSelect({ padding="2" alignItems="center" mb="2" + data-cy="withdraw-vote-select" > - diff --git a/governance/ui/src/components/EditProfile/EditProfile.tsx b/governance/ui/src/components/EditProfile/EditProfile.tsx deleted file mode 100644 index 38ef8c728..000000000 --- a/governance/ui/src/components/EditProfile/EditProfile.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import { Flex, Hide, Modal, ModalContent, ModalOverlay, Show } from '@chakra-ui/react'; -import { UserProfileForm } from '../UserProfileForm'; - -export default function EditProfile({ - activeCouncil, - onClose, -}: { - activeCouncil: string; - onClose: () => void; -}) { - return ( - <> - - - - - - - - - - - - - - - - - ); -} diff --git a/governance/ui/src/components/EditProfile/index.ts b/governance/ui/src/components/EditProfile/index.ts deleted file mode 100644 index a839133bf..000000000 --- a/governance/ui/src/components/EditProfile/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './EditProfile'; diff --git a/governance/ui/src/components/Footer/Footer.tsx b/governance/ui/src/components/Footer/Footer.tsx new file mode 100644 index 000000000..1719d8763 --- /dev/null +++ b/governance/ui/src/components/Footer/Footer.tsx @@ -0,0 +1,45 @@ +import { Flex, Link } from '@chakra-ui/react'; +import { SNXFooterSVG } from '../Icons'; +import { DiscordIcon, GithubIcon, WarpcastIcon, XIcon, YoutubeIcon } from '@snx-v3/icons'; + +export default function Footer() { + return ( + + + + + + + + + + + + + + + + + + + + + ); +} diff --git a/governance/ui/src/components/Footer/index.ts b/governance/ui/src/components/Footer/index.ts new file mode 100644 index 000000000..ddcc5a9cd --- /dev/null +++ b/governance/ui/src/components/Footer/index.ts @@ -0,0 +1 @@ +export * from './Footer'; diff --git a/governance/ui/src/components/Header/Header.tsx b/governance/ui/src/components/Header/Header.tsx index 64ae0bfa7..af5ef640a 100644 --- a/governance/ui/src/components/Header/Header.tsx +++ b/governance/ui/src/components/Header/Header.tsx @@ -1,53 +1,20 @@ -import { - Button, - Flex, - Image, - Text, - Menu, - MenuButton, - MenuList, - MenuItem, - useColorMode, - Show, - Fade, -} from '@chakra-ui/react'; +import { Button, Flex, useColorMode, Show, Link } from '@chakra-ui/react'; import { useNavigate } from 'react-router-dom'; -import { BaseIcon, EthereumIcon, FailedIcon, OptimismIcon, WalletIcon } from '@snx-v3/icons'; -import { prettyString } from '@snx-v3/format'; import { useEffect, useState } from 'react'; import PeriodCountdown from '../PeriodCountdown/PeriodCountdown'; import { useGetUserBallot } from '../../queries'; import { useQueryClient } from '@tanstack/react-query'; import councils from '../../utils/councils'; import { useWallet, useNetwork } from '../../queries/useWallet'; -import { Network } from '@snx-v3/useBlockchain'; -import governanceHeaderSvg from './governance-header.svg'; -import snxHeaderSvg from './snx-header.svg'; - -const activeIcon = (currentNetwork: Network | null) => { - switch (currentNetwork?.id) { - case 1: - return { icon: , name: 'Ethereum' }; - case 11155111: - return { icon: , name: 'Sepolia Testnet' }; - case 10: - return { icon: , name: 'Optimism' }; - case 8453: - return { icon: , name: 'Base' }; - case 84532: - return { icon: , name: 'Base Sepolia' }; - default: - return { icon: , name: 'Unsupported Network' }; - } -}; +import { NetworkController } from './NetworkController'; +import { SNXHeaderIcon, SNXHeaderIconSmall } from '../Icons'; export function Header() { const navigate = useNavigate(); - const { activeWallet, walletsInfo, connect, disconnect } = useWallet(); - const { network, setNetwork } = useNetwork(); + const { activeWallet, walletsInfo, connect } = useWallet(); + const { network } = useNetwork(); - const { icon } = activeIcon(network); const { colorMode, toggleColorMode } = useColorMode(); const [localStorageUpdated, setLocalStorageUpdated] = useState(false); @@ -55,9 +22,7 @@ export function Header() { const queryClient = useQueryClient(); - const [{ data: ballots, isFetched }] = [ - useGetUserBallot(['spartan', 'ambassador', 'grants', 'treasury']), - ]; + const [{ data: ballots, isFetched }] = [useGetUserBallot(['spartan', 'ambassador', 'treasury'])]; useEffect(() => { if ( @@ -119,13 +84,6 @@ export function Header() { } }, [walletsInfo, connect]); - const onDisconnect = () => { - if (walletsInfo) { - disconnect(walletsInfo); - localStorage.removeItem('connectedWallets'); - } - }; - return ( - navigate('/')} mr="auto"> - + navigate('/')} mr="auto" alignItems="center"> + - + + {process.env.DEV === 'true' && Admin} - {activeWallet && ( - - {() => ( - <> - - {icon} - - - setNetwork(1)}> - - - Ethereum Mainnet - - - setNetwork(10)}> - - - Optimism - - - setNetwork(8453)}> - - - Base - - - {/* Testnets */} - setNetwork(11155111)}> - - - Sepolia - - - setNetwork(84532)}> - - - Base Sepolia - - - - - )} - + {activeWallet && } + + {!activeWallet && ( + )} - - {activeWallet ? ( - - - - - {activeWallet.ens?.name || prettyString(activeWallet.address)} - - - - { - try { - navigator.clipboard.writeText(activeWallet?.address); - } catch (_e) {} - }} - > - - Copy address - - - - - Disconnect - - - - - ) : ( - - )} - ); diff --git a/governance/ui/src/components/Header/NetworkController.tsx b/governance/ui/src/components/Header/NetworkController.tsx new file mode 100644 index 000000000..a81482df4 --- /dev/null +++ b/governance/ui/src/components/Header/NetworkController.tsx @@ -0,0 +1,300 @@ +import { useEffect, useState } from 'react'; +import { + Button, + Divider, + Flex, + IconButton, + Image, + Link, + Menu, + MenuButton, + MenuItem, + MenuList, + MenuOptionGroup, + Switch, + Text, + useDisclosure, +} from '@chakra-ui/react'; +import { NetworkIcon, useNetwork, useWallet, NETWORKS, Network } from '@snx-v3/useBlockchain'; +import { prettyString } from '@snx-v3/format'; +import { useLocalStorage } from '@snx-v3/useLocalStorage'; +import { CopyIcon } from '@chakra-ui/icons'; +import { useLocation, useNavigate } from 'react-router-dom'; +import { Tooltip } from '@snx-v3/Tooltip'; +import { supportedNetworks } from '../../utils/onboard'; +import { DisconnectIcon } from '../Icons'; +import Blockies from 'react-blockies'; +import '../../pages/index.css'; +import { useGetUserDetailsQuery } from '../../queries'; + +const SNXChain: Network = { + hexId: '999', + id: 999, + isSupported: true, + isTestnet: false, + label: 'SNX Chain', + name: 'SNXChain', + preset: '999-main', + publicRpcUrl: 'http://127.0.0.1:19000', + rpcUrl: () => 'http://127.0.0.1:19000', + token: 'SNX', +}; + +const mainnets = NETWORKS.filter(({ isSupported, isTestnet }) => isSupported && !isTestnet) + .filter((network) => supportedNetworks.includes(network.id)) + .concat(SNXChain); +const testnets = NETWORKS.filter(({ isTestnet }) => isTestnet) + .filter((network) => supportedNetworks.includes(network.id)) + .concat(SNXChain); + +export function NetworkController() { + const { isOpen, onOpen, onClose } = useDisclosure(); + const [toolTipLabel, setTooltipLabel] = useState('Copy'); + const { activeWallet, walletsInfo, connect, disconnect } = useWallet(); + const { network: activeNetwork, setNetwork } = useNetwork(); + const [showTestnets, setShowTestnets] = useLocalStorage('governance-show-testnets', false); + const navigate = useNavigate(); + const { pathname } = useLocation(); + const { data: user } = useGetUserDetailsQuery(activeWallet?.address); + + useEffect(() => { + // Check if wallet preference is stored in local storage + if (!walletsInfo) { + const defaultWallet = localStorage.getItem('connectedWallets'); + + if (defaultWallet) { + connect({ + autoSelect: { disableModals: true, label: JSON.parse(defaultWallet) }, + }); + } + } + + if (walletsInfo) { + // store in local storage + localStorage.setItem('connectedWallets', JSON.stringify(walletsInfo.label)); + } + }, [walletsInfo, connect, navigate, pathname]); + + const onDisconnect = () => { + if (walletsInfo) { + disconnect(walletsInfo); + localStorage.removeItem('connectedWallets'); + } + }; + + const notConnected = !activeWallet; + const notSupported = activeWallet && !activeNetwork; + + return ( + + + span': { display: 'flex', alignItems: 'center' } }} + mr={1} + data-cy="account-menu-button" + px={3} + > + + + + {mainnets.map(({ id, preset, label }) => ( + setNetwork(id)}> + + + {label} + + + ))} + + {showTestnets && } + + + + + Show Testnets + + setShowTestnets(!showTestnets)} + /> + + + + {(showTestnets ? testnets : []).map(({ id, preset, label }) => ( + setNetwork(id)}> + + + {label} + + + ))} + + + {activeWallet ? ( + onOpen()}> + + {user?.pfpUrl ? ( + + + + {user?.username || prettyString(activeWallet?.address || '')} + + + ) : ( + + + + {user?.username || prettyString(activeWallet?.address || '')} + + + )} + + onClose()}> + + + + + Connected with {walletsInfo?.label} + + { + e.stopPropagation(); + onDisconnect(); + }} + size="sm" + icon={} + variant="outline" + colorScheme="gray" + > + + + {prettyString(activeWallet?.address || '')}{' '} + + { + navigator.clipboard.writeText(activeWallet.address); + setTooltipLabel('Copied'); + setTimeout(() => { + setTooltipLabel('Copy'); + }, 10000); + }} + /> + + + + + + {user?.pfpUrl ? ( + <>Implement me + ) : ( + + )} + + {user?.username ? ( + + + {user.username} + + + {user.about} + + + ) : ( + + {prettyString(user?.address || '')} + + )} + + onClose()}> + + + + + + ) : ( + + )} + + ); +} diff --git a/governance/ui/src/components/Header/governance-header.svg b/governance/ui/src/components/Header/governance-header.svg deleted file mode 100644 index 15f1e941b..000000000 --- a/governance/ui/src/components/Header/governance-header.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/governance/ui/src/components/Header/snx-header.svg b/governance/ui/src/components/Header/snx-header.svg deleted file mode 100644 index c5c20e2b1..000000000 --- a/governance/ui/src/components/Header/snx-header.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/governance/ui/src/components/Icons/Disconnect.tsx b/governance/ui/src/components/Icons/Disconnect.tsx new file mode 100644 index 000000000..44425b3b6 --- /dev/null +++ b/governance/ui/src/components/Icons/Disconnect.tsx @@ -0,0 +1,12 @@ +export const DisconnectIcon = () => ( + + + + +); diff --git a/governance/ui/src/components/Icons/SNXFooterIcon.tsx b/governance/ui/src/components/Icons/SNXFooterIcon.tsx new file mode 100644 index 000000000..388bd4d43 --- /dev/null +++ b/governance/ui/src/components/Icons/SNXFooterIcon.tsx @@ -0,0 +1,8 @@ +export const SNXFooterSVG = () => ( + + + +); diff --git a/governance/ui/src/components/Icons/SNXHeaderIcon.tsx b/governance/ui/src/components/Icons/SNXHeaderIcon.tsx new file mode 100644 index 000000000..f497391e2 --- /dev/null +++ b/governance/ui/src/components/Icons/SNXHeaderIcon.tsx @@ -0,0 +1,8 @@ +export const SNXHeaderIcon = () => ( + + + +); diff --git a/governance/ui/src/components/Icons/SNXHeaderIconSmall.tsx b/governance/ui/src/components/Icons/SNXHeaderIconSmall.tsx new file mode 100644 index 000000000..64947f28e --- /dev/null +++ b/governance/ui/src/components/Icons/SNXHeaderIconSmall.tsx @@ -0,0 +1,17 @@ +import { Icon } from '@chakra-ui/react'; + +export const SNXHeaderIconSmall = () => ( + + + +); diff --git a/governance/ui/src/components/Icons/index.ts b/governance/ui/src/components/Icons/index.ts new file mode 100644 index 000000000..c7e84e160 --- /dev/null +++ b/governance/ui/src/components/Icons/index.ts @@ -0,0 +1,4 @@ +export * from './Disconnect'; +export * from './SNXFooterIcon'; +export * from './SNXHeaderIcon'; +export * from './SNXHeaderIconSmall'; diff --git a/governance/ui/src/components/Layout/Layout.tsx b/governance/ui/src/components/Layout/Layout.tsx index a07904c47..de5537db4 100644 --- a/governance/ui/src/components/Layout/Layout.tsx +++ b/governance/ui/src/components/Layout/Layout.tsx @@ -1,11 +1,14 @@ +import { Flex } from '@chakra-ui/react'; +import Footer from '../Footer/Footer'; import { Header } from '../Header'; import { Outlet } from 'react-router-dom'; export function Layout() { return ( - <> +
- +