Feature/about page update#946
Conversation
Improve responsive behavior and fix overflow/wrapping issues across the AboutPage styles. Add overflowX: 'clip' and multiple minWidth: 0 rules to prevent horizontal overflow, enable text wrapping via whiteSpace/overflowWrap changes, and introduce media queries to restore larger sizes at desktop breakpoints. Adjust typography (font sizes and line-heights) for hero, titles, and various cards, reduce paddings/min-heights for mobile and reapply previous values at wider viewports, and tweak layout grids (montage -> single column on mobile, two-column on wider screens) and scroll/marquee sizing to use vw-based columns on small screens. Misc updates include reducing gaps/padding for compact view, updating logo marquee and tile sizes, and general polish for consistent responsive presentation.
Aligns this styling with the broader design system on the app
|
@xSatori is attempting to deploy a commit to the Nouns Builder Team on Vercel. A member of the Team first needs to authorize it. |
|
Warning Rate limit exceeded
You’ve run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (14)
📝 WalkthroughWalkthroughThis pull request implements a comprehensive About page feature and infra: new About types and seeded data, a large Vanilla Extract stylesheet, 15+ React components and sections, three GET API endpoints (dao-tabs, showcase, snapshot), SWR hooks and utilities, DropMintWidget and call-site updates, a new button variant, Redis-backed AI text caching utilities, and several minor UI/type fixes. ChangesAbout Page Feature Module
🎯 4 (Complex) | ⏱️ ~75 minutes Possibly Related PRs
Poem
✨ Finishing Touches🧪 Generate unit tests (beta)
|
Replace hardcoded and data-driven colors on the About page with theme variables so dark/light mode backgrounds, focus states, and hover feedback render consistently.
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
Actionable comments posted: 14
🧹 Nitpick comments (6)
apps/web/src/pages/dao/[network]/[token]/index.tsx (1)
46-74: ⚡ Quick winStrengthen minter address handling with explicit guards
The cast on line 46 to
keyof typeof MERKLE_RESERVE_MINTERpasses TypeScript checks but doesn't prevent accessing unmapped keys at runtime. While all currentCHAIN_IDvalues are covered in both maps, the pattern could break if new chains are added without updating the maps. Extract minter values explicitly and guard the read query to ensure undefined values are never passed touseReadContracts.Suggested fix
- const chainIdKey = chain.id as keyof typeof MERKLE_RESERVE_MINTER + const merkleReserveMinter = MERKLE_RESERVE_MINTER[chain.id] + const erc721RedeemMinter = ERC721_REDEEM_MINTER[chain.id] + const hasKnownMinterAddresses = Boolean(merkleReserveMinter && erc721RedeemMinter) const { data: contractData } = useReadContracts({ allowFailure: false, + query: { enabled: hasKnownMinterAddresses }, contracts: [ { ...auctionContractParams, functionName: 'owner' }, { ...tokenContractParams, functionName: 'remainingTokensInReserve' }, { ...tokenContractParams, functionName: 'minter', - args: [MERKLE_RESERVE_MINTER[chainIdKey]], + args: [merkleReserveMinter!], }, { ...tokenContractParams, functionName: 'minter', - args: [ERC721_REDEEM_MINTER[chainIdKey]], + args: [erc721RedeemMinter!], }, ] as const, }) const handleMinterEnabled = React.useCallback( async (minterAddress: AddressType) => { - if (minterAddress === MERKLE_RESERVE_MINTER[chainIdKey]) { + if (minterAddress === merkleReserveMinter) { await openTab('merkle-reserve') - } else if (minterAddress === ERC721_REDEEM_MINTER[chainIdKey]) { + } else if (minterAddress === erc721RedeemMinter) { await openTab('erc721-redeem') } }, - [chainIdKey, openTab] + [merkleReserveMinter, erc721RedeemMinter, openTab] )Also applies to: 116-123
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/web/src/pages/dao/`[network]/[token]/index.tsx around lines 46 - 74, The code casts chain.id to keyof typeof MERKLE_RESERVE_MINTER and then directly indexes MERKLE_RESERVE_MINTER and ERC721_REDEEM_MINTER when building the useReadContracts call; instead, explicitly read const merkleMinter = MERKLE_RESERVE_MINTER[chain.id as number] and const redeemMinter = ERC721_REDEEM_MINTER[chain.id as number], check each for undefined, and only include the corresponding contract entries in the contracts array passed to useReadContracts (or omit the minter function calls) so undefined values are never passed into useReadContracts; update the logic around chainIdKey, the contracts array construction, and the second occurrence at lines ~116-123 to use these guarded variables.apps/web/src/utils/api/ai/summaries.ts (3)
36-43: ⚡ Quick winSilent failure when Redis is unavailable.
The function uses optional chaining (
redisConnection?.get()) to gracefully handle missing Redis connections. While this prevents crashes, it silently disables caching, potentially leading to:
- Repeated expensive AI API calls
- Increased latency
- Higher costs
Consider logging a warning when Redis operations are skipped, or explicitly validating that Redis is available in critical paths.
Proposed logging enhancement
const redisConnection = getRedisConnection() + if (!redisConnection) { + console.warn('[AI Cache] Redis unavailable; proceeding without cache') + } + const cacheKey = getAiCacheKey(namespace, data, model)🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/web/src/utils/api/ai/summaries.ts` around lines 36 - 43, The code currently swallows Redis availability by using optional chaining on redisConnection?.get(cacheKey), silently disabling caching; update the flow in the function that calls getRedisConnection and getAiCacheKey so that if redisConnection is falsy you log a warning (e.g., using the module's existing logger or console.warn) that Redis is unavailable and caching will be skipped, then proceed without calling redisConnection.get; specifically check redisConnection before calling redisConnection.get(cacheKey) and add a clear log message referencing the namespace/cacheKey context so operators can diagnose missing Redis rather than silently falling back.
79-84: 💤 Low valueConsider input validation for DAO descriptions.
The
summarizeDaoDescriptionfunction doesn't validate the inputdescriptionbefore processing. WhiletrimDescriptionhandles empty strings, consider adding explicit validation to:
- Reject empty/whitespace-only input early
- Handle unexpectedly long descriptions
- Prevent potential prompt injection (though server-side context mitigates this)
Proposed validation
export const summarizeDaoDescription = async (description: string) => { + if (!description?.trim()) { + throw new Error('Description cannot be empty') + } + return generateCachedAiText({ namespace: 'ai:daoDescription', data: { description }, prompt: buildDaoDescriptionPrompt(description), }) }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/web/src/utils/api/ai/summaries.ts` around lines 79 - 84, summarizeDaoDescription lacks input validation: before calling generateCachedAiText (and buildDaoDescriptionPrompt) trim the description (use existing trimDescription if available), reject or throw for empty/whitespace-only inputs, enforce a MAX_DAO_DESCRIPTION_LENGTH (either truncate with a warning or return an error) to handle overly long texts, and perform simple sanitization to reduce prompt-injection vectors (e.g., strip or escape special system-like tokens/markers such as "###", "Assistant:", or other control sequences) so only validated/sanitized text is passed into buildDaoDescriptionPrompt and generateCachedAiText.
19-20: 💤 Low valueConsider simpler hashing for cache keys.
Using
keccak256(a cryptographic hash) for cache key generation is cryptographically secure but computationally expensive for this use case. A simpler, faster hash function (e.g., a built-in hash or even a deterministic JSON stringification) would suffice for cache key uniqueness without the performance overhead.Alternative approach
- const hash = keccak256(toHex(`${safeStringify(data)}:${model}`)) + const hash = Buffer.from(`${safeStringify(data)}:${model}`).toString('base64') return `${namespace}:${hash}`🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/web/src/utils/api/ai/summaries.ts` around lines 19 - 20, The cache key generation currently uses keccak256(toHex(...)) which is overkill and slow for this use case; update the hash creation in the block that defines hash (and the return `${namespace}:${hash}`) to use a much faster deterministic approach—either use a stable/deterministic JSON string of the payload (safeStringify(data)) concatenated with model and namespace as the key, or plug in a fast non-crypto hash (e.g., murmurhash/fnv1a) instead of keccak256 and drop toHex; replace the keccak256 and toHex calls around safeStringify(data) and model accordingly so the resulting cache key remains unique but cheaper to compute.apps/web/src/modules/about/utils.ts (1)
4-10: 💤 Low valueConsider documenting normalization behavior.
The
normalizeChainLabelfunction performs aggressive normalization (lowercasing, removing all non-alphanumeric characters). This can cause identical labels for chains with similar names (e.g., "Base-Sepolia" and "BaseSepolia" both become "base sepolia"). While this appears intentional for fuzzy matching, consider adding a brief comment explaining the normalization strategy to prevent future confusion.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/web/src/modules/about/utils.ts` around lines 4 - 10, The normalizeChainLabel function aggressively normalizes input (lowercases, collapses non-alphanumerics to spaces, trims) which can map distinct labels like "Base-Sepolia" and "BaseSepolia" to the same result; add a concise inline comment above normalizeChainLabel describing the normalization rules (lowercase, remove/replace non-alphanumerics with spaces, collapse/trim) and the intended purpose (fuzzy matching/deduplication) so future maintainers understand this behavior and its trade-offs.apps/web/src/modules/about/components/DroposalHighlightsSection.tsx (1)
26-62: ⚡ Quick winConsider moving
statusStyleByTypeoutside the component.The
statusStyleByTypeobject is currently recreated on every render. IfgetProposalStateColorStylereturns static values (not dependent on runtime theme/context), this constant should be defined outside the component to avoid unnecessary allocations.♻️ Suggested refactor
+const STATUS_STYLE_BY_TYPE: Record< + DroposalHighlight['status'], + { borderColor: string; color: string; backgroundColor: string } +> = { + Active: { + ...getProposalStateColorStyle(ProposalState.Active), + backgroundColor: `color-mix(in srgb, ${getProposalStateColorStyle(ProposalState.Active).borderColor} 12%, transparent)`, + }, + // ... rest of mappings +} + export const DroposalHighlightsSection: React.FC<DroposalHighlightsSectionProps> = ({ items, eyebrowText = 'Drops', title = 'Drops turn releases into onchain distribution', copy = 'Launch collectible drops that turn media, editions, and releases into distribution, ownership, and treasury growth for decentralized communities.', linkLabel = 'View drop', showStatusBadge = true, }) => { - const statusStyleByType: Record< - DroposalHighlight['status'], - { borderColor: string; color: string; backgroundColor: string } - > = { - Active: { /* ... */ }, - // ... - } - const highlights = items?.length ? items : dropHighlightsNote: Only apply this refactor if
getProposalStateColorStylereturns static values. If it depends on theme or other runtime context, keep it inside the component.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/web/src/modules/about/components/DroposalHighlightsSection.tsx` around lines 26 - 62, The statusStyleByType mapping is being recreated on each render; move the const statusStyleByType declaration out of the DroposalHighlightsSection component (to module scope) so it is allocated once and reuse it across renders if getProposalStateColorStyle returns static values; if getProposalStateColorStyle depends on runtime theme/context instead, keep it inside the component but wrap the creation in useMemo (e.g., memoize the statusStyleByType using useMemo with the theme/context deps) and reference the same symbol names (statusStyleByType and getProposalStateColorStyle) so callers inside DroposalHighlightsSection keep working.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/web/src/modules/about/components/AboutFinalCta.tsx`:
- Around line 24-36: The visible numeric markers inside the ordered list are
duplicated for screen readers; keep the semantic <ol>/<li> but hide the visual
markers from assistive tech by marking the marker elements (the Text elements
with class finalChecklistMarker) as presentational—add aria-hidden="true" (or
role="presentation") to those Text nodes so the browser's native ol numbering is
the only thing announced; ensure the Box components remain as="ol" and as="li"
so semantics are preserved.
In `@apps/web/src/modules/about/components/DroposalHighlightsSection.tsx`:
- Around line 50-53: The Trending highlight currently spreads styles from
getProposalStateColorStyle(ProposalState.Defeated); update the mapping so
Trending uses getProposalStateColorStyle(ProposalState.Succeeded) instead (keep
the existing backgroundColor expression) so the Trending status uses the
Succeeded color palette; locate the Trending object in
DroposalHighlightsSection.tsx and replace the ProposalState.Defeated reference
with ProposalState.Succeeded.
In `@apps/web/src/modules/about/components/SectionIntro.tsx`:
- Around line 4-24: The prop eyebrowText on the SectionIntro component is
declared but never rendered; update the JSX in SectionIntro to output
eyebrowText (e.g., render a Text element before the title) so callers' eyebrow
labels aren't dropped; use the existing sectionHeader wrapper and add the
eyebrowText inside it (choose/introduce an appropriate className such as
sectionEyebrow or a new prop if styling control is needed) and ensure the prop
name eyebrowText is used instead of being ignored.
In `@apps/web/src/modules/about/useAboutDaoTabs.ts`:
- Line 12: The request URL in useAboutDaoTabs builds the query string by
interpolating network directly (fetch(`/api/about/dao-tabs?network=${network}`,
...)), which can break for reserved characters; fix it by encoding the network
value with encodeURIComponent when constructing the URL (e.g., replace
${network} with ${encodeURIComponent(network)}) so the fetch call always sends a
well-formed query parameter.
- Around line 17-25: The code parses response.text() with JSON.parse before
checking response.ok, so a JSON.parse error can bypass the typed HttpError path;
update the parsing to try parsing JSON inside a try/catch (or attempt JSON.parse
and fall back to raw text) and ensure the !response.ok branch always runs even
if parsing fails by constructing the HttpError (with status and either parsed
body or the raw text) and throwing it; refer to the response, text, body
variables and the HttpError type in useAboutDaoTabs.ts when implementing this
change.
In `@apps/web/src/modules/about/useAboutSnapshotStats.ts`:
- Around line 16-23: The response handling in useAboutSnapshotStats.ts currently
unconditionally calls JSON.parse on response.text which will throw for non-JSON
(e.g. HTML) error pages; update the code around response.text and body so you
attempt to parse JSON safely (either check response.headers.get('content-type')
for application/json or wrap JSON.parse in a try/catch), and if parsing fails
set body to the raw text (or a minimal object) before constructing the
HttpError; ensure the HttpError created in the existing block (new Error(...),
err.status, err.body) receives the safe body value so callers get the structured
HttpError path even when the server returned HTML or other non-JSON content.
In `@apps/web/src/modules/dashboard/CreateActions.tsx`:
- Around line 38-41: The Link wrapping Button in CreateActions creates nested
interactive controls; remove the nesting by using a single interactive element:
either turn the Link into the visible button (render Link styled as a button and
remove the child Button) or make the Button perform navigation (keep Button and
remove Link, calling router.push('/create') or using useRouter/nav helper in the
Button's onClick). Update the JSX in CreateActions to reference the Link or
Button only (not both) and ensure accessibility attributes (aria-label/role) and
styles are preserved.
In `@apps/web/src/pages/api/about/dao-tabs.ts`:
- Around line 293-305: The fetchCrossChainDaoCandidates function currently uses
Promise.all which rejects on the first failed fetchDaoCandidates call; change
the fan-out to use Promise.allSettled over PUBLIC_DEFAULT_CHAINS, filter for
settled results with status "fulfilled", extract each fulfilled value (the daos
mapped to { chainId, dao }), skip or log any rejected results (including chain
id and error) so a single chain outage doesn't return a 500, and finally flat()
the fulfilled arrays to return CrossChainDaoCandidate[]; apply the same pattern
to other similar fan-outs in this file.
In `@apps/web/src/pages/api/about/showcase.ts`:
- Around line 292-300: The sort comparator currently converts totalSalesAmount
to Number causing precision loss; update the .sort(...) to compare BigInt values
instead by parsing each totalSalesAmount with BigInt(e.g. const aAmount =
BigInt(a.totalSalesAmount || '0'), bAmount = BigInt(b.totalSalesAmount || '0'))
and return bAmount > aAmount ? 1 : bAmount < aAmount ? -1 : 0 so sorting uses
exact bigint comparison (refer to drops, totalSalesAmount and the .sort
comparator).
- Around line 373-396: The catch path returns fallback payload without the same
Cache-Control header, causing origin hits on failures; move extraction of
CACHE_TIMES.EXPLORE ({ maxAge, swr }) out of the try so it's available on error,
and in the catch call res.setHeader('Cache-Control', `public,
s-maxage=${maxAge}, stale-while-revalidate=${swr}`) before returning the
fallback JSON (affecting code around buildShowcaseResponse, CACHE_TIMES.EXPLORE,
and the fallback* variables).
In `@apps/web/src/pages/api/about/snapshot.ts`:
- Around line 91-123: The current fetchUniqueOwnersForChain function performs a
full scan of daoTokenOwners via repeated ABOUT_SNAPSHOT_OWNERS_QUERY paging on
every cache miss which will make the public API latency unbounded; instead, stop
doing full scans on the request path by switching the API to read from a
precomputed/periodically-updated store or indexed query: implement a background
job or cron that uses fetchUniqueOwnersForChain (or an incremental sync using
subgraph events) to compute and persist the unique owner Set, and have the API
handler read that cached result (falling back to a small bounded/filtered query
only when absolutely necessary); ensure OWNER_PAGE_SIZE paging logic is moved
out of the request path and that ABOUT_SNAPSHOT_OWNERS_QUERY is only used by the
background job or an incremental sync process.
- Around line 167-179: The current logic uses the deduped union (uniqueOwners)
whenever it is non-empty, which undercounts if some chain snapshots failed;
change it to use the union only when all owner scans succeeded by checking that
every snapshot in chainSnapshots has snapshot.uniqueOwners present (e.g., via
Array.every), and if not, fall back to the coarse aggregate computed from
daos.reduce; update the totalMembers assignment (and any related variables) to
conditionally choose uniqueOwners.size only when that all-scans-succeeded check
passes, otherwise use the daos.reduce fallback.
In `@apps/web/src/pages/dao/`[network]/[token]/proposal/create.tsx:
- Around line 152-168: The page currently calls useProposalStore() with no
selector which subscribes the component to the entire proposal store and causes
unnecessary rerenders; update the hook call to select only the fields the
component uses (e.g. transactionType, setTransactionType, resetTransactionType,
transactions, title, summary, representedAddress, discussionUrl,
representedAddressEnabled, setTitle, setSummary, setRepresentedAddress,
setDiscussionUrl, setRepresentedAddressEnabled, clearProposal) and pass
useShallow from 'zustand/shallow' as the equality comparator so the component
only re-renders when those specific values change; replace the bare
useProposalStore() invocation with a selector function that returns an object
containing just those identifiers.
In `@package.json`:
- Around line 57-59: Remove the redundant root-level dependency entry
"remove-markdown" from the root package.json dependencies object; keep the
existing declaration in apps/web/package.json (which consumers like
apps/web/src/modules/about/utils.ts already use) so the dependency is only
declared in the consuming package and not at the monorepo root.
---
Nitpick comments:
In `@apps/web/src/modules/about/components/DroposalHighlightsSection.tsx`:
- Around line 26-62: The statusStyleByType mapping is being recreated on each
render; move the const statusStyleByType declaration out of the
DroposalHighlightsSection component (to module scope) so it is allocated once
and reuse it across renders if getProposalStateColorStyle returns static values;
if getProposalStateColorStyle depends on runtime theme/context instead, keep it
inside the component but wrap the creation in useMemo (e.g., memoize the
statusStyleByType using useMemo with the theme/context deps) and reference the
same symbol names (statusStyleByType and getProposalStateColorStyle) so callers
inside DroposalHighlightsSection keep working.
In `@apps/web/src/modules/about/utils.ts`:
- Around line 4-10: The normalizeChainLabel function aggressively normalizes
input (lowercases, collapses non-alphanumerics to spaces, trims) which can map
distinct labels like "Base-Sepolia" and "BaseSepolia" to the same result; add a
concise inline comment above normalizeChainLabel describing the normalization
rules (lowercase, remove/replace non-alphanumerics with spaces, collapse/trim)
and the intended purpose (fuzzy matching/deduplication) so future maintainers
understand this behavior and its trade-offs.
In `@apps/web/src/pages/dao/`[network]/[token]/index.tsx:
- Around line 46-74: The code casts chain.id to keyof typeof
MERKLE_RESERVE_MINTER and then directly indexes MERKLE_RESERVE_MINTER and
ERC721_REDEEM_MINTER when building the useReadContracts call; instead,
explicitly read const merkleMinter = MERKLE_RESERVE_MINTER[chain.id as number]
and const redeemMinter = ERC721_REDEEM_MINTER[chain.id as number], check each
for undefined, and only include the corresponding contract entries in the
contracts array passed to useReadContracts (or omit the minter function calls)
so undefined values are never passed into useReadContracts; update the logic
around chainIdKey, the contracts array construction, and the second occurrence
at lines ~116-123 to use these guarded variables.
In `@apps/web/src/utils/api/ai/summaries.ts`:
- Around line 36-43: The code currently swallows Redis availability by using
optional chaining on redisConnection?.get(cacheKey), silently disabling caching;
update the flow in the function that calls getRedisConnection and getAiCacheKey
so that if redisConnection is falsy you log a warning (e.g., using the module's
existing logger or console.warn) that Redis is unavailable and caching will be
skipped, then proceed without calling redisConnection.get; specifically check
redisConnection before calling redisConnection.get(cacheKey) and add a clear log
message referencing the namespace/cacheKey context so operators can diagnose
missing Redis rather than silently falling back.
- Around line 79-84: summarizeDaoDescription lacks input validation: before
calling generateCachedAiText (and buildDaoDescriptionPrompt) trim the
description (use existing trimDescription if available), reject or throw for
empty/whitespace-only inputs, enforce a MAX_DAO_DESCRIPTION_LENGTH (either
truncate with a warning or return an error) to handle overly long texts, and
perform simple sanitization to reduce prompt-injection vectors (e.g., strip or
escape special system-like tokens/markers such as "###", "Assistant:", or other
control sequences) so only validated/sanitized text is passed into
buildDaoDescriptionPrompt and generateCachedAiText.
- Around line 19-20: The cache key generation currently uses
keccak256(toHex(...)) which is overkill and slow for this use case; update the
hash creation in the block that defines hash (and the return
`${namespace}:${hash}`) to use a much faster deterministic approach—either use a
stable/deterministic JSON string of the payload (safeStringify(data))
concatenated with model and namespace as the key, or plug in a fast non-crypto
hash (e.g., murmurhash/fnv1a) instead of keccak256 and drop toHex; replace the
keccak256 and toHex calls around safeStringify(data) and model accordingly so
the resulting cache key remains unique but cheaper to compute.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 923112a5-20f0-4cc5-9efe-3b33d84a3750
⛔ Files ignored due to path filters (4)
apps/web/public/builderlogo.pngis excluded by!**/*.pngapps/web/public/noggles-square-dark.svgis excluded by!**/*.svgapps/web/public/noggles-square.svgis excluded by!**/*.svgpnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (45)
apps/web/package.jsonapps/web/src/modules/about/AboutPage.css.tsapps/web/src/modules/about/AboutPage.tsxapps/web/src/modules/about/components/AboutFinalCta.tsxapps/web/src/modules/about/components/AboutHero.tsxapps/web/src/modules/about/components/CoiningCard.tsxapps/web/src/modules/about/components/CoiningHighlightsSection.tsxapps/web/src/modules/about/components/DaoCard.tsxapps/web/src/modules/about/components/DroposalHighlightsSection.tsxapps/web/src/modules/about/components/EcosystemActivitySection.tsxapps/web/src/modules/about/components/EcosystemStatGrid.tsxapps/web/src/modules/about/components/FeaturedDaoSection.tsxapps/web/src/modules/about/components/HowItWorksSection.tsxapps/web/src/modules/about/components/ProposalHighlightsSection.tsxapps/web/src/modules/about/components/SectionIntro.tsxapps/web/src/modules/about/components/WhatIsBuilderSection.tsxapps/web/src/modules/about/components/WhatYouCanBuildSection.tsxapps/web/src/modules/about/components/WhyBuilderSection.tsxapps/web/src/modules/about/data.tsapps/web/src/modules/about/index.tsapps/web/src/modules/about/types.tsapps/web/src/modules/about/useAboutDaoTabs.tsapps/web/src/modules/about/useAboutShowcase.tsapps/web/src/modules/about/useAboutSnapshotStats.tsapps/web/src/modules/about/utils.tsapps/web/src/modules/dashboard/CreateActions.css.tsapps/web/src/modules/dashboard/CreateActions.tsxapps/web/src/modules/dashboard/Dashboard.tsxapps/web/src/modules/dashboard/SingleDaoSelector.tsxapps/web/src/pages/about.tsxapps/web/src/pages/api/about/dao-tabs.tsapps/web/src/pages/api/about/showcase.tsapps/web/src/pages/api/about/snapshot.tsapps/web/src/pages/api/ai/generateTxSummary.tsapps/web/src/pages/api/migrated.tsapps/web/src/pages/dao/[network]/[token]/index.tsxapps/web/src/pages/dao/[network]/[token]/proposal/create.tsxapps/web/src/utils/api/ai/summaries.tspackage.jsonpackages/proposal-ui/src/components/ProposalStatus/ProposalStatus.helper.tspackages/proposal-ui/src/components/ProposalStatus/index.tspackages/proposal-ui/src/index.tspackages/zord/src/elements/Button.css.tspackages/zord/src/elements/Button.tsxturbo.json
💤 Files with no reviewable changes (1)
- apps/web/src/modules/dashboard/CreateActions.css.ts
| const fetchUniqueOwnersForChain = async (subgraphUrl: string): Promise<Set<string>> => { | ||
| const client = new GraphQLClient(subgraphUrl, { | ||
| headers: { 'Content-Type': 'application/json' }, | ||
| }) | ||
|
|
||
| const owners = new Set<string>() | ||
| let skip = 0 | ||
|
|
||
| while (true) { | ||
| const data = await client.request<SnapshotOwnersQueryResponse>( | ||
| ABOUT_SNAPSHOT_OWNERS_QUERY, | ||
| { | ||
| first: OWNER_PAGE_SIZE, | ||
| skip, | ||
| } | ||
| ) | ||
|
|
||
| const page = data.daoTokenOwners ?? [] | ||
|
|
||
| for (const item of page) { | ||
| if (item.owner) { | ||
| owners.add(item.owner.toLowerCase()) | ||
| } | ||
| } | ||
|
|
||
| if (page.length < OWNER_PAGE_SIZE) { | ||
| break | ||
| } | ||
|
|
||
| skip += OWNER_PAGE_SIZE | ||
| } | ||
|
|
||
| return owners |
There was a problem hiding this comment.
Avoid full owner-table scans on the request path.
fetchUniqueOwnersForChain walks every daoTokenOwners page for a chain on each cache miss. That makes this public endpoint's latency unbounded as the ecosystem grows and will eventually turn into subgraph timeout/quota pressure.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@apps/web/src/pages/api/about/snapshot.ts` around lines 91 - 123, The current
fetchUniqueOwnersForChain function performs a full scan of daoTokenOwners via
repeated ABOUT_SNAPSHOT_OWNERS_QUERY paging on every cache miss which will make
the public API latency unbounded; instead, stop doing full scans on the request
path by switching the API to read from a precomputed/periodically-updated store
or indexed query: implement a background job or cron that uses
fetchUniqueOwnersForChain (or an incremental sync using subgraph events) to
compute and persist the unique owner Set, and have the API handler read that
cached result (falling back to a small bounded/filtered query only when
absolutely necessary); ensure OWNER_PAGE_SIZE paging logic is moved out of the
request path and that ABOUT_SNAPSHOT_OWNERS_QUERY is only used by the background
job or an incremental sync process.
There was a problem hiding this comment.
🧹 Nitpick comments (2)
packages/ui/src/DropMintWidget/DropMintWidget.tsx (1)
179-190: ⚡ Quick winReconsider the placeholder Box approach for semantic correctness.
When
quantityis 1, the code renders<Box className={quantityButton} />, which applies button visual styles to a non-interactive element. This can confuse assistive technologies and creates a visual element that looks clickable but isn't.Consider these alternatives:
- Always render the button but keep it
disabled(consistent semantics)- Render an invisible spacer with only layout dimensions (
minWidth/height)- Adjust the parent layout to handle the missing element gracefully
♻️ Option 1: Always render disabled button
- {quantity > 1 ? ( - <Button - variant="primary" - size="sm" - onClick={decrementQuantity} - className={quantityButton} - > - - - </Button> - ) : ( - <Box className={quantityButton} /> - )} + <Button + variant="primary" + size="sm" + onClick={decrementQuantity} + disabled={quantity <= 1} + className={quantityButton} + > + - + </Button>🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/ui/src/DropMintWidget/DropMintWidget.tsx` around lines 179 - 190, The placeholder Box in DropMintWidget makes a non-interactive element look like a button; replace it in the render where quantity is checked so semantics match visuals — always render the same Button element (the one using className quantityButton and onClick decrementQuantity) but set its disabled prop when quantity <= 1 so it remains visually consistent and accessible; update any aria-label or title on that Button if needed to convey disabled state to assistive tech.packages/ui/src/DropMintWidget/DropMintWidget.css.ts (1)
4-20: 💤 Low valueNote: Different border radius between styled and unstyled variants.
widgetContainerusesborderRadius: 'phat'whilewidgetContainerUnstyledusesborderRadius: 'curved'. If this difference is intentional to match different contexts (standalone vs. embedded), this is fine. Otherwise, consider standardizing.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/ui/src/DropMintWidget/DropMintWidget.css.ts` around lines 4 - 20, The two style exports widgetContainer and widgetContainerUnstyled use different borderRadius values ('phat' vs 'curved'), causing inconsistent corner radii; if this is unintended, standardize them by updating either widgetContainerUnstyled to use 'phat' or widgetContainer to use 'curved' so both share the same borderRadius, and if the difference is intentional add a clarifying comment above the relevant export(s) explaining the contextual reason for the mismatch.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@packages/ui/src/DropMintWidget/DropMintWidget.css.ts`:
- Around line 4-20: The two style exports widgetContainer and
widgetContainerUnstyled use different borderRadius values ('phat' vs 'curved'),
causing inconsistent corner radii; if this is unintended, standardize them by
updating either widgetContainerUnstyled to use 'phat' or widgetContainer to use
'curved' so both share the same borderRadius, and if the difference is
intentional add a clarifying comment above the relevant export(s) explaining the
contextual reason for the mismatch.
In `@packages/ui/src/DropMintWidget/DropMintWidget.tsx`:
- Around line 179-190: The placeholder Box in DropMintWidget makes a
non-interactive element look like a button; replace it in the render where
quantity is checked so semantics match visuals — always render the same Button
element (the one using className quantityButton and onClick decrementQuantity)
but set its disabled prop when quantity <= 1 so it remains visually consistent
and accessible; update any aria-label or title on that Button if needed to
convey disabled state to assistive tech.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 33894868-da29-4aa0-9ae0-afbe77cfbf59
📒 Files selected for processing (15)
apps/web/src/modules/about/AboutPage.css.tsapps/web/src/modules/about/components/AboutFinalCta.tsxapps/web/src/modules/about/components/AboutHero.tsxapps/web/src/modules/about/components/CoiningCard.tsxapps/web/src/modules/about/components/DroposalHighlightsSection.tsxapps/web/src/modules/coin/CoinDetail/CoinCommentForm.css.tsapps/web/src/modules/coin/CoinDetail/CoinComments.css.tsapps/web/src/modules/coin/CoinDetail/CoinComments.tsxapps/web/src/modules/coin/CoinDetail/CoinDetail.css.tsapps/web/src/modules/drop/DropDetail/DropDetail.css.tsapps/web/src/modules/drop/DropDetail/DropDetail.tsxpackages/dao-ui/src/components/Gallery/Gallery.tsxpackages/feed-ui/src/Modals/MintModal.tsxpackages/ui/src/DropMintWidget/DropMintWidget.css.tspackages/ui/src/DropMintWidget/DropMintWidget.tsx
✅ Files skipped from review due to trivial changes (4)
- apps/web/src/modules/coin/CoinDetail/CoinComments.css.ts
- apps/web/src/modules/coin/CoinDetail/CoinComments.tsx
- apps/web/src/modules/coin/CoinDetail/CoinDetail.css.ts
- apps/web/src/modules/coin/CoinDetail/CoinCommentForm.css.ts
🚧 Files skipped from review as they are similar to previous changes (5)
- apps/web/src/modules/about/components/AboutHero.tsx
- apps/web/src/modules/about/components/DroposalHighlightsSection.tsx
- apps/web/src/modules/about/components/AboutFinalCta.tsx
- apps/web/src/modules/about/components/CoiningCard.tsx
- apps/web/src/modules/about/AboutPage.css.ts
Description
Rebuilds the About page with app-consistent styling, responsive layouts, and data-backed ecosystem sections. This PR replaces the older page implementation with a modular About experience that better matches the rest of the site and improves the accuracy of the DAO, proposal, and showcase content surfaced on the page.
Motivation & context
The previous About page looked visually out of place relative to the rest of the app, was not behaving well on mobile, and had inaccurate governance status labels in the proposal showcase. This change addresses those issues by:
modules/aboutimplementationThis also includes related build/lint support changes needed to keep the branch deployable.
Code review
Primary review areas:
apps/web/src/modules/about/*apps/web/src/pages/api/about/*showcase.tsapps/web/src/pages/about.tsxapps/web/src/modules/dashboard/*Specific logic to check:
Active,Succeeded,Queued,Executed, andDefeatedType of change
Checklist
Local verification
pnpm --dir apps/web lintSummary by CodeRabbit
New Features
API
Chores
Style