From a1329d8ad250341b8fbb9f39d7146bda961ada7b Mon Sep 17 00:00:00 2001 From: eightrice Date: Wed, 17 Dec 2025 13:45:52 +0200 Subject: [PATCH 1/3] feat: Add demo comment to App.tsx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added a comment at the top of src/App.tsx saying '// Final test before demo' 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- src/App.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/App.tsx b/src/App.tsx index 77001ece..385a1fe1 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,3 +1,4 @@ +// Final test before demo import React from "react" import "App.css" import { BrowserRouter as Router, Redirect, Route, Switch } from "react-router-dom" From df3a463196ecb246b6148b1ce5108f16308e45f0 Mon Sep 17 00:00:00 2001 From: eightrice Date: Wed, 17 Dec 2025 14:48:53 +0200 Subject: [PATCH 2/3] feat: Add Active tab to DAO Proposals page MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a new "Active" tab to the Proposals page that filters and displays only proposals that are currently active/open for voting. This provides users with a quick way to see proposals they can currently vote on. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../explorer/pages/Proposals/index.tsx | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/modules/explorer/pages/Proposals/index.tsx b/src/modules/explorer/pages/Proposals/index.tsx index 0b03174e..1511c611 100644 --- a/src/modules/explorer/pages/Proposals/index.tsx +++ b/src/modules/explorer/pages/Proposals/index.tsx @@ -22,6 +22,7 @@ import { ProposalStatus } from "services/services/dao/mappers/proposal/types" import NewReleasesIcon from "@mui/icons-material/NewReleases" import DeleteIcon from "@mui/icons-material/Delete" import FilterAltIcon from "@mui/icons-material/FilterAlt" +import HowToVoteIcon from "@mui/icons-material/HowToVote" import { FilterProposalsDialog } from "modules/explorer/components/FiltersDialog" import { Filters } from "../User/components/UserMovements" import { EvmProposalsPage } from "modules/etherlink/explorer/EtherlinkDAO/EvmProposalsPage" @@ -112,6 +113,7 @@ const TezosProposals = () => { const [selectedTab, setSelectedTab] = React.useState(0) const { data: proposals } = useProposals(daoId) + const { data: activeProposals } = useProposals(daoId, ProposalStatus.ACTIVE) const theme = useTheme() const isMobileSmall = useMediaQuery(theme.breakpoints.down("xs")) const proposalTypeQuery = new URLSearchParams(window.location.search).get("type") @@ -260,6 +262,17 @@ const TezosProposals = () => { Off-Chain + + } + disableElevation={true} + variant="contained" + onClick={() => handleChangeTab(2)} + isSelected={selectedTab === 2} + > + Active + + @@ -320,6 +333,29 @@ const TezosProposals = () => { ) : null} + + + + {activeProposals && cycleInfo && ( + + )} + {!(activeProposals && activeProposals.length > 0) ? ( + + + + No active proposals + + + + ) : null} + + From 9e6c099bc75eb746e272456ed877ea48565d2ded Mon Sep 17 00:00:00 2001 From: eightrice Date: Wed, 17 Dec 2025 15:10:14 +0200 Subject: [PATCH 3/3] feat: Add Active tab to Etherlink DAO Proposals page MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add new "Active" tab to the DAO proposals page that shows only active/pending proposals - Active tab displays proposals from both on-chain and off-chain sources that are currently open for voting - Add HowToVoteIcon from MUI for the Active tab button - Update queryFilters.ts to support "active" as a new TypeKey - Integrate with existing URL query parameter system for consistent navigation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../etherlink/components/EvmProposalsShell.tsx | 18 ++++++++++++++++++ .../explorer/EtherlinkDAO/EvmProposalsPage.tsx | 18 ++++++++++++++---- .../etherlink/explorer/filters/queryFilters.ts | 14 +++++++++----- 3 files changed, 41 insertions(+), 9 deletions(-) diff --git a/src/modules/etherlink/components/EvmProposalsShell.tsx b/src/modules/etherlink/components/EvmProposalsShell.tsx index 45911558..1280a536 100644 --- a/src/modules/etherlink/components/EvmProposalsShell.tsx +++ b/src/modules/etherlink/components/EvmProposalsShell.tsx @@ -2,6 +2,7 @@ import React from "react" import { Button, Grid, Theme, styled, useMediaQuery, useTheme } from "@material-ui/core" import { ContentContainer } from "components/ui/Table" import FilterAltIcon from "@mui/icons-material/FilterAlt" +import HowToVoteIcon from "@mui/icons-material/HowToVote" import { ReactComponent as LinkActive } from "assets/img/link_active.svg" import { ReactComponent as LinkInactive } from "assets/img/link_inactive.svg" import { ReactComponent as UnlinkActive } from "assets/img/unlink_active.svg" @@ -149,6 +150,23 @@ export const EvmProposalsShell: React.FC = ({ Off-Chain + + + + } + disableElevation={true} + variant="contained" + onClick={() => onChangeTab(2)} + isSelected={selectedTab === 2} + > + Active + + ) : null} diff --git a/src/modules/etherlink/explorer/EtherlinkDAO/EvmProposalsPage.tsx b/src/modules/etherlink/explorer/EtherlinkDAO/EvmProposalsPage.tsx index adfda242..93c4e60b 100644 --- a/src/modules/etherlink/explorer/EtherlinkDAO/EvmProposalsPage.tsx +++ b/src/modules/etherlink/explorer/EtherlinkDAO/EvmProposalsPage.tsx @@ -23,7 +23,7 @@ export const EvmProposalsPage = () => { daoSelected as any ) const offchainEnabled = isFeatureEnabled("etherlink-offchain-debate") - const selectedTab = offchainEnabled && qFilters.type === "offchain" ? 1 : 0 + const selectedTab = qFilters.type === "active" ? 2 : offchainEnabled && qFilters.type === "offchain" ? 1 : 0 const [openFiltersDialog, setOpenFiltersDialog] = useState(false) const [searchText, setSearchText] = useState("") const [debouncedSearchText, setDebouncedSearchText] = useState("") @@ -35,7 +35,8 @@ export const EvmProposalsPage = () => { const onChangeTab = useCallback( (idx: number) => { if (!offchainEnabled && idx === 1) return - setQFilters({ type: idx === 1 ? "offchain" : "onchain" }) + const type = idx === 2 ? "active" : idx === 1 ? "offchain" : "onchain" + setQFilters({ type: type as any }) }, [offchainEnabled, setQFilters] ) @@ -61,11 +62,14 @@ export const EvmProposalsPage = () => { const resolveDisplayStatus = (p: any) => toDisplayStatus(p.effectiveDisplayStatus || p.displayStatus || p.status) - const isOffchainActive = (p: any) => { + const isProposalActive = (p: any) => { const disp = resolveDisplayStatus(p) return disp === "Active" || disp === "Pending" } + // Alias for backwards compatibility + const isOffchainActive = isProposalActive + const offchainStatusKeyForProposal = (p: any): "active" | "closed" | "no-quorum" => { const disp = resolveDisplayStatus(p) if (disp === "NoQuorum") return "no-quorum" @@ -103,13 +107,16 @@ export const EvmProposalsPage = () => { if (ptypes.length > 0) { base = base.filter((p: any) => (ptypes as PTypeKey[]).some(k => matchesPtypeKey(p, k))) } - } else if (offchainEnabled) { + } else if (selectedTab === 1 && offchainEnabled) { // off-chain base = base.filter(p => p.type === "offchain") const offStatuses = ((qFilters.status || []) as string[]).filter(s => s !== "all") if (offStatuses.length > 0) { base = base.filter(p => offStatuses.includes(offchainStatusKeyForProposal(p))) } + } else if (selectedTab === 2) { + // Active tab - show all active/pending proposals (both on-chain and off-chain) + base = base.filter(p => isProposalActive(p)) } if (debouncedSearchText) { @@ -163,6 +170,9 @@ export const EvmProposalsPage = () => { p.type === "offchain")} /> ) : null} + + + setIsProposalDialogOpen(false)} /> diff --git a/src/modules/etherlink/explorer/filters/queryFilters.ts b/src/modules/etherlink/explorer/filters/queryFilters.ts index 1771652a..77353d4c 100644 --- a/src/modules/etherlink/explorer/filters/queryFilters.ts +++ b/src/modules/etherlink/explorer/filters/queryFilters.ts @@ -1,6 +1,6 @@ import { DisplayStatus } from "modules/etherlink/status" -export type TypeKey = "onchain" | "offchain" +export type TypeKey = "onchain" | "offchain" | "active" // Canonical, hyphenated keys for URL usage export type StatusKey = @@ -104,7 +104,9 @@ const splitCsv = (v?: string | null) => export function canonicalType(value?: string | null): TypeKey { const v = normalize(value) - return v === "offchain" ? "offchain" : "onchain" + if (v === "offchain") return "offchain" + if (v === "active") return "active" + return "onchain" } export function canonicalStatuses( @@ -173,7 +175,7 @@ export function parseFiltersFromSearch(search: string): ParsedFilters { const t = canonicalType(sp.get("type")) const author = sp.get("author") const status = canonicalStatuses(sp.get("status"), t) - const ptype = t === "offchain" ? [] : canonicalPtypes(sp.get("ptype")) + const ptype = t === "offchain" || t === "active" ? [] : canonicalPtypes(sp.get("ptype")) return { type: t, status, ptype, author } } @@ -185,8 +187,9 @@ export function serializeFiltersToSearch(filters: ParsedFilters, prevSearch: str sp.delete("status") sp.delete("ptype") - // type: only emit when offchain (onchain is default) + // type: only emit when offchain or active (onchain is default) if (filters.type === "offchain") sp.set("type", "offchain") + if (filters.type === "active") sp.set("type", "active") // status const statuses = (filters.status || []).filter(Boolean) as string[] @@ -196,7 +199,8 @@ export function serializeFiltersToSearch(filters: ParsedFilters, prevSearch: str const off = statusSet.filter(s => s === "active" || s === "closed" || s === "no-quorum") if (off.length === 1) sp.set("status", off[0]) if (off.length > 1) sp.set("status", off.sort().join(",")) - } else { + } else if (filters.type !== "active") { + // Active tab doesn't need status filters in URL const withoutAll = statusSet.filter(s => s !== "all") if (withoutAll.length > 0) sp.set("status", withoutAll.sort().join(",")) }