Skip to content

Fix known issues/gaps and UI consistency pass - #58

Merged
projectamazonph merged 20 commits into
mainfrom
claude/fix-remaining-issues
Aug 3, 2026
Merged

Fix known issues/gaps and UI consistency pass#58
projectamazonph merged 20 commits into
mainfrom
claude/fix-remaining-issues

Conversation

@projectamazonph

@projectamazonph projectamazonph commented Aug 3, 2026

Copy link
Copy Markdown
Owner

Summary

Two parts, per the request to "fix all known issues, gaps and then focus on UI, visual look for consistency, missing padding, borders, overlaps, colors, styles":

Known issues/gaps (from earlier code-review findings):

  • Bid-validation consistency: setTargetBid/setAdGroupDefaultBid now fail fast on a below-minimum bid instead of silently clamping it (creation paths like addTarget still clamp, since that's for incomplete-data defaults, not explicit user intent).
  • Deduplicated metrics/formatter/ID-generation logic that had drifted into multiple hand-rolled copies (PortfolioOverview, Dashboard, CampaignManager, several features/*/engine.ts files, launchCampaign), fixing a real ID-collision risk in launchCampaign along the way.
  • Fixed Card/Table convention violations (dense tables were Card-wrapped in PortfolioOverview, Dashboard, OverviewTab, BulkOpsPage, ReportsPage — CLAUDE.md says dense tables render edge-to-edge, not Card-wrapped).
  • Removed dead code (CreateCampaignWizard's 13 unused useState hooks and unused imports).
  • Performance: fixed an O(n·m) duplicate-detection loop in the search-term simulator, memoized a few expensive per-render recomputations.
  • Empty-state fix: Campaign Manager now distinguishes "no campaigns match your filters" from "no campaigns yet."

Visual QA pass (live browser walkthrough via Playwright):

  • Fixed literal mojibake (ΓÇö, ΓåÆ) in the landing page copy — an em-dash and an arrow got corrupted at some earlier edit.
  • Added a missing .object-cover CSS rule — two landing-page <Image fill> elements referenced a Tailwind-style class that this repo has no compiler for, so they had no object-fit applied.
  • Found and fixed a real navigation gap: the Sidebar component (and the getLeftRail/training-rail model behind it, with its own full unit-test coverage) was fully built but never mounted into the app layout — .app-sidebar/.app-body existed in CSS with no consumer. This meant Missions, Reports, Bulk ops, Trainer, and Integrity were completely unreachable from the desktop UI, and MobileNav had the same gap from a different angle (its section resolution was hardcoded to only portfolio/campaigns). Wired the sidebar into AdConsole.tsx, hid it below the tablet breakpoint so it doesn't double up with the mobile drawer, and fixed MobileNav to use the same shared nav-resolution helpers.
  • Fixed a dashboard layout overflow: .split's 2fr 1fr grid didn't shrink correctly once the sidebar took up real width, causing the "Operator alerts"/"Training coverage" cards to be clipped past the viewport edge instead of wrapping.

Confirmed some suspected issues were false alarms and left alone: Framer Motion scroll-in animations rendering blank in a screenshot taken without scrolling, and wide tables scrolling internally via Astryx's own .astryx-table-scroll-wrapper (both are correct, intentional behavior per CLAUDE.md's own documented conventions).

Test plan

  • npx tsc --noEmit
  • npx vitest run (648 tests passing, including a new contract test suite pinning the sidebar fix)
  • npm run build
  • Live browser walkthrough (Playwright + headless Chromium) across landing, dashboard, campaign manager, campaign detail tabs, portfolios, and all 6 training views (drills/missions/reports/bulk/trainer/integrity), at both desktop (1440px) and tablet (1024px) widths, to confirm the sidebar fix and overflow fix render correctly with no regressions

Generated by Claude Code

Summary by CodeRabbit

  • New Features
    • Added persistent sidebar navigation for training, missions, reports, and integrity views.
    • Added clear-filters actions and clearer empty states for campaigns and reports.
    • Enhanced campaign review with targeting, audience, lookback, and creative details.
  • Bug Fixes
    • Improved identifier uniqueness across records.
    • Invalid bids below the $0.02 minimum are now rejected.
    • Fixed responsive navigation, grid sizing, and landing-page rendering.
  • Style
    • Refined dashboard layouts, spacing, icons, colors, and typography.
  • Tests
    • Expanded coverage for navigation, empty states, bid validation, and duplicate records.

claude added 12 commits August 3, 2026 13:06
setAdGroupDefaultBid (and setTargetBid, which had the same pattern)
validated only that a bid was finite/non-negative, then silently
rewrote anything below Amazon's real $0.02 minimum to $0.02 — a
caller requesting $0.00 got $0.02 back with a history entry claiming
that's what they asked for, contradicting the engine's documented
fail-fast convention.

Added assertValidBid (finite, non-negative, and >= the real minimum)
for these "set an explicit bid on an existing entity" actions, and
removed the clamp. Left addTarget/normalizeCampaign's clamping alone
— those are creation/normalization paths filling in defaults for
otherwise-incomplete data, a different and still-valid use case for
clamping. Tightened the two UI call sites' pre-submit guards to match
the new floor so they no longer throw uncaught.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Jpq3N94psa27k8xASDy8B
…formatters

Several visual/consistency issues flagged by review:

- Dashboard, OverviewTab, and PortfolioOverview wrapped their dense
  data tables in Card, violating the "Table renders edge-to-edge,
  never Card-wrapped" convention followed everywhere else (Manager*Tab,
  BudgetRulesTab). Split each into a Card for the header/summary
  widget portion and a bare Table sibling below it.
- PortfolioOverview's campaign-name link used ad-hoc inline styles
  instead of the shared .row-link class, so it had no hover affordance
  unlike the identical link on Dashboard and Campaign Manager.
- ManagerCampaignsTab always showed "No campaigns yet — Create your
  first campaign" even when the real cause was an active filter/search
  matching nothing, pointing the user at campaign creation instead of
  clearing filters. Added a distinct "no matches" empty state with a
  Clear filters action, using a new hasAnyCampaigns prop to tell the
  two cases apart.
- Deduplicated three hand-rolled metrics-summing reduce blocks
  (PortfolioOverview x2, CampaignManager) to the canonical
  totalMetrics() engine helper, and Dashboard's local
  fmtMoney/fmtWhole/fmtPercent (which had drifted to 2-decimal
  percentages) to the canonical formatMoney/formatWhole/formatPercent.
- Extracted the "which adFormat means video for this campaign type"
  check (previously an inline compound boolean in OverviewTab) into a
  shared isVideoFormat(type, adFormat) engine helper.

Updated the Astryx Card-contract test that had pinned the old
(incorrect) 3-Card count for OverviewTab down to 2, since "Top
targets" is intentionally no longer Card-wrapped.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Jpq3N94psa27k8xASDy8B
…alues

- BulkOpsPage's CSV preview table and ReportsPage's selected-report
  table were Card-wrapped; split each into a header + bare Table,
  matching the edge-to-edge convention applied elsewhere.
- .pill.purple (the SD campaign-type badge) used raw hex (#f3e8ff /
  #7c3aed) while every sibling pill variant (.active, .orange, .green,
  .bad) resolves through --info/--accent/--success/--danger tokens.
  Added --purple/--purple-soft tokens with the same values so a future
  theme/rebrand updates this badge along with the rest.
- TargetsTab's "add keyword" inline form used raw pixel gap/margin
  literals (8, 10) instead of the --space-* tokens used for the same
  purpose everywhere else in the app.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Jpq3N94psa27k8xASDy8B
… truth

launchCampaign built campaign ids from Date.now() alone with no
counter, unlike every other entity id in the engine — two campaigns
of the same type launched within the same millisecond (e.g. a
double-clicked "Launch" button) collided, corrupting lookups that
assume unique ids. Now uses generateId('C-' + type), matching the
identical pattern duplicateCampaign already uses.

Also replaced four near-identical hand-rolled uid()/counter/Date.now
helpers in the reports, profiles, integrity, and trainer feature
engines — each was its own independent module-scoped counter that
resets on hot reload — with the shared generateId(prefix), so ID
generation has exactly one implementation instead of five.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Jpq3N94psa27k8xASDy8B
The 6-item training-view check (drills/missions/reports/bulk/trainer/
integrity) was repeated as an identical view === 'x' || ... chain in
three functions (getLeftRail, activeTopbarSection,
sidebarSectionForView) — exactly the class of duplication that
already caused audit H-03 (one of the copies falling out of sync).
Extracted a single TRAINING_VIEWS set all three now check against.

getKpiTiles also hand-rolled its own ctr/acos/roas formulas instead
of calling the canonical calc(), duplicating logic that a future
formula fix (e.g. guarding against negative sales) would otherwise
need to be applied in two places.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Jpq3N94psa27k8xASDy8B
13 useState hooks (keyword/target inputs, SB creative fields) and two
store-action hooks (selectProduct/removeProduct) were initialized
from the draft but never read or updated anywhere in the component —
the actual form state lives in each step's own store subscription
(draft/updateDraft), not these local copies. A future maintainer
editing this file could reasonably assume these hooks were live and
wire new logic to them, silently doing nothing since the real data
flow bypasses them entirely.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Jpq3N94psa27k8xASDy8B
- simulation.ts checked each newly generated search term against both
  the campaign's existing terms and the terms generated so far via two
  nested linear scans per candidate. Since searchTerms only grows
  across simulation runs (never pruned), each subsequent 7-day sim
  click re-scanned an ever-larger array. Replaced with a single Set
  for O(1) membership checks.
- Dashboard subscribed to the whole store `state` and recomputed
  totalMetrics/calc/getKpiTiles directly in the render body, so any
  state change anywhere in the app (not just campaign metrics) redid
  the full-campaign-list aggregate. Memoized on state.campaigns, which
  keeps the same array reference for state changes that don't touch
  campaigns.
- ManagerSearchTermsTab did the same flatMap+filter scan (O(search
  terms x negatives)) directly in the render body with no
  memoization; wrapped in useMemo keyed on the campaigns prop.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Jpq3N94psa27k8xASDy8B
The landing page's feature-grid and CTA icons used Tailwind sizing
classes (w-6 h-6, w-4 h-4), but this repo has no Tailwind/PostCSS
compiler wired up (confirmed: no tailwind.config.*, no
postcss.config.*, not in package.json). Without a compiler these
classes are dead — the SVGs had no explicit width/height, so browsers
fall back to the default inline-SVG size (300x150) instead of the
intended 24px/16px icons.

Replaced with explicit width/height attributes, matching how
EmptyState.tsx already sizes its icon SVGs.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Jpq3N94psa27k8xASDy8B
src/app/page.tsx had literal garbled byte sequences (—, →) committed
in place of an em-dash and a right-arrow, likely from an encoding
mismatch at some earlier edit. Replaced with the correct UTF-8
characters.
Two Next.js <Image fill> elements on the landing page use
className="object-cover" but no CSS rule ever defined that class (no
Tailwind compiler is wired up in this repo), so the images had no
object-fit applied and could render stretched.
.split used grid-template-columns: 2fr 1fr, which per the CSS grid
sizing algorithm floors each track at its content's min-content width
rather than actually letting it shrink to its fractional share. With
the app sidebar now taking up real width (see next commit), the 1fr
column (Operator alerts / Training coverage) no longer fit and was
clipped past the right edge of the viewport instead of wrapping.
minmax(0, ...) lets both tracks shrink to fit, so content wraps inside
its column instead of overflowing.
…ner/Integrity were unreachable

The Sidebar component (and the getLeftRail/TRAINING_RAIL model behind
it, with its own full unit-test coverage in consoleNav.test.ts) was
fully built but never mounted anywhere in the component tree — the
.app-body/.app-sidebar CSS classes it depends on existed in
globals.css but had no consumer either. Since the top nav's "Training"
button only lands on Drills, there was no way for a desktop user to
reach Missions, Reports, Bulk ops, Trainer, or Integrity, and no way to
jump directly to a campaign's Ad groups/Targeting/etc. tabs or
Portfolio's Budget rules from the nav.

Mount <Sidebar /> in AdConsole.tsx inside a new .app-body wrapper
alongside the existing <main>, and hide it below the tablet breakpoint
(same threshold useBreakpoint uses for isMobileOrTablet) so it doesn't
double up with the mobile hamburger drawer.

MobileNav had the same underlying gap from a different angle: its
`section` was hardcoded to only ever resolve to 'portfolio' or
'campaigns', so training views never got a training rail, and its
active-item logic only matched the 'campaigns' group. Switched both to
the shared sidebarSectionForView/isSidebarItemActive/resolveSidebarClick
helpers that Sidebar itself uses, so mobile and desktop navigation stay
consistent.

Added a contract test pinning that the sidebar renders and actually
drives navigation to the previously-unreachable views.
Copilot AI review requested due to automatic review settings August 3, 2026 13:57
@vercel

vercel Bot commented Aug 3, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
amazon-ad-console Ready Ready Preview Aug 3, 2026 3:10pm

@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@projectamazonph, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 49 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 742e198b-90f6-4058-9609-cb67b67c2618

📥 Commits

Reviewing files that changed from the base of the PR and between 2830a2f and 93ba857.

📒 Files selected for processing (5)
  • src/components/AdConsole/details/TargetsTab.tsx
  • src/components/AdConsole/details/__tests__/bid-validation-feedback.test.tsx
  • src/components/AdConsole/wizard/__tests__/Step6ReviewLaunch.test.tsx
  • src/engine/ad-console/core/__tests__/simulation.test.ts
  • src/engine/ad-console/features/integrity/__tests__/engine.test.ts
📝 Walkthrough

Walkthrough

The PR centralizes bid validation and ID generation, updates Ad Console navigation and campaign metrics, adds filtered empty-state handling, expands campaign launch review data, adjusts card-based layouts, and corrects landing-page styling, copy, and release documentation.

Changes

Ad Console updates

Layer / File(s) Summary
Bid validation and engine contracts
src/lib/validation.ts, src/engine/ad-console/core/engine/*, src/engine/ad-console/core/__tests__/*, src/components/AdConsole/details/*, src/components/AdConsole/wizard/*
Bids below MIN_BID are rejected. Relative bid reductions stop at MIN_BID. Video-format detection is shared between the engine and overview UI. Campaign review screens display targeting and creative details.
Shared identifiers and simulation
src/engine/ad-console/core/slices/core.ts, src/engine/ad-console/features/*/engine.ts, src/engine/ad-console/core/simulation.ts, src/engine/ad-console/**/__tests__/*
Campaign and feature IDs use generateId. Search-term generation uses set membership for duplicate detection. Regression tests cover repeated timestamps and repeated simulations.
Console navigation and shell
src/components/AdConsole/AdConsole.tsx, src/components/AdConsole/nav/*, src/components/AdConsole/mobile/*, src/components/AdConsole/__tests__/contracts/sidebar.test.tsx, src/components/AdConsole/wizard/CreateCampaignWizard.tsx
The Sidebar is mounted in AdConsole. Navigation resolves training sections, selected tabs, and view changes through shared helpers. Sidebar integration tests cover navigation state. Unused wizard state and imports are removed.
Campaign management and metrics
src/components/AdConsole/CampaignManager.tsx, src/components/AdConsole/details/ManagerCampaignsTab.tsx, src/components/AdConsole/Dashboard.tsx, src/components/AdConsole/PortfolioOverview.tsx, src/components/AdConsole/details/ManagerSearchTermsTab.tsx, src/components/AdConsole/__tests__/ManagerCampaignsTab.test.tsx
Metric aggregation uses shared or memoized calculations. Filtered and initial empty states render different messages and actions. Portfolio and campaign layouts use updated wrappers and shared formatting.
Presentation and layout
src/app/globals.css, src/app/page.tsx, src/components/AdConsole/details/OverviewTab.tsx, src/components/AdConsole/features/bulk/BulkOpsPage.tsx, src/components/AdConsole/features/reports/ReportsPage.tsx, src/components/AdConsole/__tests__/contracts/cards-astryx.test.tsx
Design tokens, reset behavior, grid sizing, icon dimensions, spacing, card wrappers, report empty states, and landing-page punctuation are updated.
Release documentation and metadata
CHANGELOG.md, CLAUDE.md, docs/FEATURES.md, package.json
The 3.6.0 changelog, architecture guidance, responsive-layout documentation, and package version are updated.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant AdConsole
  participant MobileNav
  participant consoleNav
  participant Store
  AdConsole->>MobileNav: Render navigation
  MobileNav->>consoleNav: Resolve section and click action
  consoleNav->>Store: Set tab or view
  Store-->>MobileNav: Provide selected tab and view
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the PR's broad issue fixes and UI consistency improvements.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/fix-remaining-issues

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/engine/ad-console/core/engine/campaign.ts (1)

295-318: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Add tests for the changed campaign engine behavior.

Cover changed and unchanged placement history in savePlacements. Cover SB, SD, and fallback cases in isVideoFormat. The supplied engine test change covers only rejected target bids.

As per coding guidelines: changes under src/engine/ must include tests.

#!/usr/bin/env bash
set -euo pipefail

matches="$(rg -n --glob '*.test.ts' --glob '*.test.tsx' \
  '\b(savePlacements|isVideoFormat)\b' src/engine || true)"

if [[ -z "$matches" ]]; then
  echo "No engine tests found for savePlacements or isVideoFormat." >&2
  exit 1
fi

printf '%s\n' "$matches"
🤖 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 `@src/engine/ad-console/core/engine/campaign.ts` around lines 295 - 318, Add
engine tests for savePlacements covering both changed placement values and
unchanged values with their exact history entries. Add isVideoFormat tests
covering the SB and SD video strings plus fallback cases for unsupported
campaign types and nonmatching or missing formats, using the existing engine
test conventions.
🧹 Nitpick comments (5)
src/app/page.tsx (1)

11-58: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Replace hardcoded SVG dimensions with Astryx sizing.

The changed icons use hardcoded width="24", height="24", width="16", and height="16". Use the applicable Astryx icon component or a token-based size definition instead.

As per coding guidelines, “Do not use raw hex colors, pixel values, StyleX, Tailwind utility classes, or xstyle.”

Also applies to: 144-144, 178-178, 301-301

🤖 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 `@src/app/page.tsx` around lines 11 - 58, Replace the hardcoded width and
height attributes on the SVG icons in the page feature sections, including the
additional occurrences, with the applicable Astryx icon component sizing or
shared token-based size definition. Preserve each icon’s existing appearance and
avoid introducing raw pixel values or other prohibited styling approaches.

Source: Coding guidelines

src/components/AdConsole/features/reports/ReportsPage.tsx (1)

96-100: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use an Astryx empty-state component instead of Card.

This empty state is not a dashboard widget, gallery, or settings group. Replace the Card with the appropriate Astryx empty-state component.

As per coding guidelines, “Use Card only for dashboard widgets, galleries, and settings groups.”

🤖 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 `@src/components/AdConsole/features/reports/ReportsPage.tsx` around lines 96 -
100, Replace the Card wrapper in the empty report branch of ReportsPage with the
appropriate Astryx empty-state component, preserving the existing “This report
type has no data yet.” message and styling semantics.

Source: Coding guidelines

src/components/AdConsole/CampaignManager.tsx (1)

93-107: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use Astryx layout primitives for the changed containers.

The changed containers use raw <div> elements for layout.

  • src/components/AdConsole/CampaignManager.tsx#L93-L107: replace the KPI grid wrapper with an Astryx layout component.
  • src/components/AdConsole/Dashboard.tsx#L76-L82: replace the campaign-section wrapper with an Astryx layout component.
  • src/components/AdConsole/features/bulk/BulkOpsPage.tsx#L66-L87: replace the preview wrapper with an Astryx layout component.
  • src/components/AdConsole/features/reports/ReportsPage.tsx#L70-L75,L101-L101: replace the selected-report wrapper with an Astryx layout component.

As per coding guidelines, “Use Astryx components for layout; do not use raw <div> elements.”

#!/bin/bash
set -euo pipefail

npx astryx search "layout grid stack table section"
npx astryx component Stack
npx astryx component Grid
🤖 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 `@src/components/AdConsole/CampaignManager.tsx` around lines 93 - 107, Replace
the raw layout divs with appropriate Astryx layout primitives, preserving
existing classes, content, and behavior: use an Astryx grid component for the
KPI wrapper in src/components/AdConsole/CampaignManager.tsx lines 93-107, an
Astryx layout component for the campaign-section wrapper in
src/components/AdConsole/Dashboard.tsx lines 76-82, an Astryx layout component
for the preview wrapper in
src/components/AdConsole/features/bulk/BulkOpsPage.tsx lines 66-87, and an
Astryx layout component for both selected-report wrapper locations in
src/components/AdConsole/features/reports/ReportsPage.tsx lines 70-75 and
101-101. Use the available Astryx Grid or Stack primitives as appropriate.
src/app/globals.css (1)

63-64: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Configure the SD colors through Astryx theme.

The new values use raw hex colors in :root. Define these colors through astryx theme and consume the generated Astryx color tokens.

As per coding guidelines, “Do not use raw hex colors” and “configure brand/accent colors through astryx theme.”

🤖 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 `@src/app/globals.css` around lines 63 - 64, Replace the raw SD color values in
the :root variables --purple and --purple-soft with the corresponding generated
Astryx theme color tokens. Configure the SD campaign badge colors through the
Astryx theme rather than defining hex values directly, while preserving the
existing variable names and usage.

Source: Coding guidelines

src/components/AdConsole/PortfolioOverview.tsx (1)

98-129: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Replace raw layout elements and hardcoded styling with Astryx primitives.

The portfolio section adds raw <div> and <span> layout elements. It also uses gap: 8 and inline visual styles. Use Astryx layout and text components. Use Astryx tokens or component props for spacing and text styling. Keep the campaign Table outside the Card.

As per coding guidelines, “Use Astryx components for layout; do not use raw <div> elements” and “Do not use raw hex colors, pixel values, StyleX, Tailwind utility classes, or xstyle.”

Also applies to: 143-144

🤖 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 `@src/components/AdConsole/PortfolioOverview.tsx` around lines 98 - 129,
Replace the raw layout and text elements in the portfolio card rendering around
the manage-mode header and metrics grid with the appropriate Astryx layout and
text primitives, including component props or design tokens for spacing and
typography instead of inline gap, margin, and font-weight values. Update both
the portfolio header and the additionally referenced lines 143–144, while
keeping the campaign Table outside the Card.

Source: Coding guidelines

🤖 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 `@src/components/AdConsole/AdConsole.tsx`:
- Around line 54-61: Update AdConsole.tsx to use Astryx’s AppShell as the page
shell and place the Sidebar navigation inside SideNav while preserving the main
content and ErrorBoundary flow. In
src/components/AdConsole/details/OverviewTab.tsx lines 105-129, retain the
edge-to-edge Table but replace its raw div wrapper with the available Astryx
layout component.

In `@src/components/AdConsole/details/AdGroupsTab.tsx`:
- Around line 66-69: Add visible validation feedback to both bid handlers:
src/components/AdConsole/details/AdGroupsTab.tsx lines 66-69 should report when
the bid is below MIN_BID instead of silently skipping setAdGroupDefaultBid, and
src/components/AdConsole/details/TargetsTab.tsx lines 111-114 should do the same
when setTargetBid is skipped. Use a shared validation result or field-level
error state consistently across both paths while preserving valid-bid updates.

In `@src/components/AdConsole/mobile/MobileNav.tsx`:
- Around line 6-12: Update the MobileNav component’s layout markup to use
Astryx’s SideNav and supported drawer, group, label, and icon-button components
instead of the raw div and span elements identified in the navigation and lines
85-90. Preserve the existing shared navigation action logic and navigation
behavior while replacing only the layout primitives.

In `@src/engine/ad-console/core/simulation.ts`:
- Around line 77-98: Add regression tests for the simulation flow around
seenTerms and repeated simulateDays calls, covering overlapping Keyword targets
and multiple simulation runs. Assert that searchTerms contains exactly one
record per generated term, with no duplicates across targets or runs, and place
the tests according to the repository’s existing src/engine testing conventions.

In `@src/engine/ad-console/features/integrity/engine.ts`:
- Around line 10-13: Add regression tests for each shared ID migration using a
fixed timestamp and verify repeated creations produce distinct IDs: test the
public issue-creation path in
src/engine/ad-console/features/integrity/engine.ts:10-13, repeated createProfile
calls in src/engine/ad-console/features/profiles/engine.ts:6-10, repeated
createReportRequest calls in
src/engine/ad-console/features/reports/engine.ts:6-13, repeated generateReport
calls in src/engine/ad-console/features/reports/engine.ts:47-47, and repeated
addNote calls in src/engine/ad-console/features/trainer/engine.ts:6-11.

---

Outside diff comments:
In `@src/engine/ad-console/core/engine/campaign.ts`:
- Around line 295-318: Add engine tests for savePlacements covering both changed
placement values and unchanged values with their exact history entries. Add
isVideoFormat tests covering the SB and SD video strings plus fallback cases for
unsupported campaign types and nonmatching or missing formats, using the
existing engine test conventions.

---

Nitpick comments:
In `@src/app/globals.css`:
- Around line 63-64: Replace the raw SD color values in the :root variables
--purple and --purple-soft with the corresponding generated Astryx theme color
tokens. Configure the SD campaign badge colors through the Astryx theme rather
than defining hex values directly, while preserving the existing variable names
and usage.

In `@src/app/page.tsx`:
- Around line 11-58: Replace the hardcoded width and height attributes on the
SVG icons in the page feature sections, including the additional occurrences,
with the applicable Astryx icon component sizing or shared token-based size
definition. Preserve each icon’s existing appearance and avoid introducing raw
pixel values or other prohibited styling approaches.

In `@src/components/AdConsole/CampaignManager.tsx`:
- Around line 93-107: Replace the raw layout divs with appropriate Astryx layout
primitives, preserving existing classes, content, and behavior: use an Astryx
grid component for the KPI wrapper in
src/components/AdConsole/CampaignManager.tsx lines 93-107, an Astryx layout
component for the campaign-section wrapper in
src/components/AdConsole/Dashboard.tsx lines 76-82, an Astryx layout component
for the preview wrapper in
src/components/AdConsole/features/bulk/BulkOpsPage.tsx lines 66-87, and an
Astryx layout component for both selected-report wrapper locations in
src/components/AdConsole/features/reports/ReportsPage.tsx lines 70-75 and
101-101. Use the available Astryx Grid or Stack primitives as appropriate.

In `@src/components/AdConsole/features/reports/ReportsPage.tsx`:
- Around line 96-100: Replace the Card wrapper in the empty report branch of
ReportsPage with the appropriate Astryx empty-state component, preserving the
existing “This report type has no data yet.” message and styling semantics.

In `@src/components/AdConsole/PortfolioOverview.tsx`:
- Around line 98-129: Replace the raw layout and text elements in the portfolio
card rendering around the manage-mode header and metrics grid with the
appropriate Astryx layout and text primitives, including component props or
design tokens for spacing and typography instead of inline gap, margin, and
font-weight values. Update both the portfolio header and the additionally
referenced lines 143–144, while keeping the campaign Table outside the Card.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 19f7a1ca-0341-4ed3-9254-23d14e326ea3

📥 Commits

Reviewing files that changed from the base of the PR and between 2ff3e8c and edb2369.

📒 Files selected for processing (33)
  • src/app/globals.css
  • src/app/page.tsx
  • src/components/AdConsole/AdConsole.tsx
  • src/components/AdConsole/CampaignManager.tsx
  • src/components/AdConsole/Dashboard.tsx
  • src/components/AdConsole/PortfolioOverview.tsx
  • src/components/AdConsole/__tests__/ManagerCampaignsTab.test.tsx
  • src/components/AdConsole/__tests__/contracts/cards-astryx.test.tsx
  • src/components/AdConsole/__tests__/contracts/sidebar.test.tsx
  • src/components/AdConsole/details/AdGroupsTab.tsx
  • src/components/AdConsole/details/ManagerCampaignsTab.tsx
  • src/components/AdConsole/details/ManagerSearchTermsTab.tsx
  • src/components/AdConsole/details/OverviewTab.tsx
  • src/components/AdConsole/details/TargetsTab.tsx
  • src/components/AdConsole/features/bulk/BulkOpsPage.tsx
  • src/components/AdConsole/features/reports/ReportsPage.tsx
  • src/components/AdConsole/mobile/MobileNav.tsx
  • src/components/AdConsole/nav/consoleNav.ts
  • src/components/AdConsole/wizard/CreateCampaignWizard.tsx
  • src/engine/ad-console/__tests__/store.test.ts
  • src/engine/ad-console/core/__tests__/adgroup.test.ts
  • src/engine/ad-console/core/__tests__/engine.test.ts
  • src/engine/ad-console/core/engine/adgroup.ts
  • src/engine/ad-console/core/engine/campaign.ts
  • src/engine/ad-console/core/engine/index.ts
  • src/engine/ad-console/core/engine/target.ts
  • src/engine/ad-console/core/simulation.ts
  • src/engine/ad-console/core/slices/core.ts
  • src/engine/ad-console/features/integrity/engine.ts
  • src/engine/ad-console/features/profiles/engine.ts
  • src/engine/ad-console/features/reports/engine.ts
  • src/engine/ad-console/features/trainer/engine.ts
  • src/lib/validation.ts
💤 Files with no reviewable changes (1)
  • src/components/AdConsole/wizard/CreateCampaignWizard.tsx

Comment thread src/components/AdConsole/AdConsole.tsx
Comment thread src/components/AdConsole/details/AdGroupsTab.tsx
Comment thread src/components/AdConsole/mobile/MobileNav.tsx
Comment thread src/engine/ad-console/core/simulation.ts
Comment thread src/engine/ad-console/features/integrity/engine.ts

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR tightens core engine correctness (bid validation + ID generation) and follows through on a UI consistency/navigation pass across the AdConsole surface, including wiring previously-unreachable Training views into the main layout.

Changes:

  • Enforce a shared minimum-bid contract (MIN_BID) and centralize explicit-bid validation for “set bid” actions; add regression tests.
  • Deduplicate ID generation and other drift-prone logic (metrics/KPIs/nav), plus a simulation performance improvement for search-term duplicate detection.
  • UI/UX consistency + navigation fixes: mount the Sidebar in the main layout, align MobileNav with the shared nav helpers, and remove Card-wrapping from dense tables per the project’s table conventions.

Reviewed changes

Copilot reviewed 33 out of 33 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/lib/validation.ts Adds MIN_BID + assertValidBid for fail-fast bid validation.
src/engine/ad-console/features/trainer/engine.ts Switches trainer note IDs to shared generateId.
src/engine/ad-console/features/reports/engine.ts Switches report/request IDs to shared generateId.
src/engine/ad-console/features/profiles/engine.ts Switches trainee profile IDs to shared generateId.
src/engine/ad-console/features/integrity/engine.ts Switches integrity issue IDs to shared generateId.
src/engine/ad-console/core/slices/core.ts Fixes campaign ID collision risk in launchCampaign via generateId.
src/engine/ad-console/core/simulation.ts Improves search-term duplicate detection from O(n·m) to O(1) lookups via Set.
src/engine/ad-console/core/engine/target.ts Makes setTargetBid fail fast below the platform minimum bid.
src/engine/ad-console/core/engine/index.ts Re-exports isVideoFormat from campaign engine.
src/engine/ad-console/core/engine/campaign.ts Adds isVideoFormat helper as single source of truth for “video” adFormat strings.
src/engine/ad-console/core/engine/adgroup.ts Makes setAdGroupDefaultBid fail fast below the minimum bid.
src/engine/ad-console/core/tests/engine.test.ts Adds coverage for below-min bid behavior on setTargetBid.
src/engine/ad-console/core/tests/adgroup.test.ts Updates/extends coverage for setAdGroupDefaultBid bid-floor behavior.
src/engine/ad-console/tests/store.test.ts Adds regression test for campaign ID uniqueness within the same millisecond.
src/components/AdConsole/wizard/CreateCampaignWizard.tsx Removes dead local state/hooks and unused imports.
src/components/AdConsole/PortfolioOverview.tsx Uses shared metrics aggregation + removes Card-wrapping from dense tables.
src/components/AdConsole/nav/consoleNav.ts Deduplicates training-view detection + uses shared calc for KPIs.
src/components/AdConsole/mobile/MobileNav.tsx Aligns mobile drawer behavior with shared sidebar click/active helpers.
src/components/AdConsole/features/reports/ReportsPage.tsx Removes Card-wrapping from the main dense table; keeps Card for empty state.
src/components/AdConsole/features/bulk/BulkOpsPage.tsx Removes Card-wrapping around dense preview table.
src/components/AdConsole/details/TargetsTab.tsx Guards “Set bid” UI against below-min bids using MIN_BID.
src/components/AdConsole/details/OverviewTab.tsx Uses isVideoFormat + removes Card-wrapping from dense “Top targets” table.
src/components/AdConsole/details/ManagerSearchTermsTab.tsx Memoizes derived rows to avoid expensive recomputation on re-renders.
src/components/AdConsole/details/ManagerCampaignsTab.tsx Distinguishes “no campaigns yet” vs “no matches” empty states.
src/components/AdConsole/details/AdGroupsTab.tsx Guards “Save default bid” UI against below-min bids using MIN_BID.
src/components/AdConsole/Dashboard.tsx Memoizes KPI computations + removes Card-wrapping from the dense campaigns table.
src/components/AdConsole/CampaignManager.tsx Uses shared totalMetrics + wires “Clear filters” into empty-state UX.
src/components/AdConsole/AdConsole.tsx Mounts Sidebar into the main desktop layout (app-body).
src/components/AdConsole/tests/ManagerCampaignsTab.test.tsx Updates tests for new empty-state behavior/props.
src/components/AdConsole/tests/contracts/sidebar.test.tsx New contract tests ensuring Sidebar is mounted and drives navigation.
src/components/AdConsole/tests/contracts/cards-astryx.test.tsx Updates card-count expectations to match the “dense tables not Card-wrapped” convention.
src/app/page.tsx Fixes landing-page mojibake and replaces Tailwind-size classes with explicit SVG sizing.
src/app/globals.css Adds purple tokens, fixes .split overflow via minmax(0, …), and adds .object-cover.
Suppressed comments (1)

src/engine/ad-console/core/engine/target.ts:152

  • Because setTargetBid now throws for bids below the platform floor, adjustTargetBid (which calls setTargetBid(c, ..., t.bid * multiplier)) will also throw when a decrement pushes the bid below the minimum. This can happen via the UI "-10%" button when a target is already at $0.02, and will surface as an unexpected runtime error. Consider clamping inside adjustTargetBid for decrement-style actions so they respect the floor without throwing.
          ? `Bid for "${t.value}" (${t.type}) changed from $${t.bid.toFixed(2)} to $${newBid.toFixed(2)}`
          : `Bid updated for target ${targetId}`;
      })(),
    ],
  };

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/engine/ad-console/features/reports/engine.ts
claude added 3 commits August 3, 2026 14:15
Copilot flagged that setTargetBid's new fail-fast MIN_BID check
(previous commit) has a real regression: adjustTargetBid — the
"-10%"/"+10%" bid-adjustment buttons — calls setTargetBid with a
multiplied value it doesn't control, so decrementing a target already
near the floor now throws an uncaught ValidationError instead of the
old clamping behavior. Unlike an explicit "Set bid" action,
adjustTargetBid is a relative nudge, so it should floor at MIN_BID
rather than fail fast.
CodeRabbit flagged several changed engine functions without direct
regression tests, per CLAUDE.md's "changes under src/engine/ must
include tests" convention:

- isVideoFormat: SB/SD video-string matches and the SP/undefined
  fallback.
- Shared generateId migration (profiles/reports/trainer/integrity
  engines): each creation path now has a same-millisecond uniqueness
  regression test, mirroring the one already added for
  launchCampaign.
- simulateDays' Set-based search-term dedup: added a test covering
  duplicates across repeated simulateDays calls, complementing the
  existing "does not add duplicate search terms" test which already
  covered duplicates within a single run from overlapping targets.
- reports/engine.ts imported assertNonEmpty but never used it
  (pre-existing, unrelated to this PR's changes; Copilot flagged it).
- ReportsPage's "no data yet" fallback used a bare Card instead of the
  EmptyState component already used the same way elsewhere
  (ManagerCampaignsTab, ManagerSearchTermsTab, HistoryTab).

Copy link
Copy Markdown
Owner Author

Thanks both — went through everything from CodeRabbit and Copilot. Pushed three follow-up commits:

Fixed:

  • Real regression (Copilot): adjustTargetBid (the "-10%"/"+10%" bid buttons) called setTargetBid with a computed value it doesn't fully control. Since setTargetBid now fails fast below MIN_BID instead of clamping, decrementing an already-cheap bid would throw an uncaught error instead of just flooring. Fixed to clamp at MIN_BID for this relative-adjustment path (explicit "Set bid" actions still fail fast, matching this PR's intent) — regression test added.
  • Dead import (Copilot): removed the unused assertNonEmpty import in reports/engine.ts.
  • Test coverage (CodeRabbit): added tests for isVideoFormat (SB/SD/SP cases), same-millisecond ID-uniqueness for the generateId migration in profiles/reports/trainer/integrity engines, and a repeated-simulateDays-calls dedup regression for the search-term Set change (the single-run overlapping-targets case was already covered by an existing test).
  • Empty state (CodeRabbit nitpick): ReportsPage's "no data yet" fallback now uses the existing EmptyState component instead of a bare Card, matching the convention already used in ManagerCampaignsTab/ManagerSearchTermsTab/HistoryTab.

Not doing, with reasons:

  • savePlacements test coverage — that function isn't touched by this PR (pre-existing, untested code flagged only because it's near isVideoFormat in the same file); adding coverage for it is a separate, unrelated task.
  • Replacing AdConsole's layout with Astryx AppShell/SideNav, and the raw-<div>-to-Astryx-layout-primitive suggestions across MobileNav, CampaignManager, Dashboard, BulkOpsPage, ReportsPage, PortfolioOverview, OverviewTab — these ask to rewrite the app's pre-existing layout convention (used on every page long before this PR) to different primitives. That's a large, cross-cutting architectural change well outside this PR's scope; per this repo's own CLAUDE.md, the component layer's known layout inconsistencies are meant to be "refactor[ed] incrementally, not all at once," never as an unprompted full rewrite. Happy to take it on as a dedicated follow-up if wanted.
  • --purple/--purple-soft raw hex tokens — consistent with every other existing semantic color token in the same :root block (--accent, --success, --danger, --info are all raw hex too); not a new pattern, just following the file's existing convention.
  • Landing-page SVG width/height attributes — src/app/page.tsx is a standalone marketing page outside the Astryx-driven AdConsole tree and doesn't use @astryxdesign/core; the explicit dimensions replaced dead Tailwind classes fixed earlier in this same PR.

All 659 tests pass, tsc --noEmit and npm run build are clean.


Generated by Claude Code

claude added 2 commits August 3, 2026 14:46
Root cause: Astryx ships its component styles (including the padding
generated from a Card's `padding` prop) inside `@layer astryx-base`/
`@layer astryx-theme`. Per the CSS cascade-layers spec, an unlayered
declaration always wins over a layered one regardless of specificity.
This repo's global reset (`*, *::before, *::after { ...; padding: 0; }`)
is unlayered, so it was unconditionally beating every Astryx `padding`
prop across the entire app — every Card rendered with 0 padding no
matter what value was passed, which is why content (e.g. the "Remove"
button on wizard product cards, headings on dashboard cards) sat
flush against card edges instead of respecting their intended spacing.

Confirmed via computed-style inspection: a Card with `padding={6}`
resolved to `padding: 0px` before this fix and `padding: 23px`
(24px token minus the 1px border inset) after.

Scoped the padding reset down to the two native elements that actually
relied on it (`ul`, `ol`) instead of zeroing it on every element.
makeDraft() defaults audienceLookback to '30' regardless of campaign
type, so Step6ReviewLaunch's `{d.audienceLookback && ...}` check was
truthy from the moment the wizard opens — every campaign's review
screen showed "Lookback: 30 days", including plain Sponsored Products
campaigns where the concept doesn't exist. Gated it on
`targetingMode.includes('Audiences')` instead, matching the same
condition the SB/SD targeting steps already use to show the lookback
selector in the first place.

Also filled in review-step gaps found during the same audit: ASIN
targets, category targets, and audience targets entered in step 4 had
no summary row at all, and SB/SD creative fields (headline, brand,
destination) set in step 3 weren't shown anywhere before launch.

Copy link
Copy Markdown
Owner Author

Did a deep pass on the Campaign Creation wizard specifically (all 6 steps × SP/SB/SD, logic/flow/fidelity to the real Amazon console). Two more commits pushed:

Root-cause CSS bug, sitewide impact: while checking the product-selection step's spacing, found that every Astryx Card's padding prop was rendering as 0px regardless of value — confirmed via computed-style inspection (padding={6}0px before, 23px after). Root cause: Astryx ships its component styles inside @layer astryx-base/@layer astryx-theme, and per the CSS cascade-layers spec an unlayered declaration always beats a layered one regardless of specificity. This repo's global reset (*, *::before, *::after { ...; padding: 0; }) is unlayered, so it was unconditionally zeroing out every Astryx padding prop app-wide — that's why content (buttons, headings, form fields) so often sat flush against card edges instead of respecting the spacing that was actually requested in the component props. Scoped the reset down to just ul, ol (the only elements that relied on it) instead of every element. Verified visually across the wizard, dashboard, and landing page — no regressions, and it visibly fixes the "no padding" look across the whole app, not just the wizard.

Wizard-specific bug: the Review & Launch step showed a "Lookback: 30 days" row on every single campaign type, including plain Sponsored Products — audienceLookback defaults to '30' in makeDraft() regardless of type, and the review step's truthiness check never accounted for that. Gated it on the campaign's targeting mode actually being an audience mode (same condition the SB/SD targeting steps use to show the field in the first place). Also filled in real gaps: ASIN/category/audience target counts and SB/SD creative fields (headline, brand, destination) were entered in earlier steps but never shown anywhere before launch — added summary rows for those.

Rest of the wizard (all 3 ad types' targeting/bidding/placement options, product selection, launch → target creation) checked out as functionally correct and reasonably faithful to the real console.

665 tests passing, tsc --noEmit and npm run build clean.


Generated by Claude Code

claude added 2 commits August 3, 2026 14:58
Reflects this release's changes: sidebar navigation wiring, the
sitewide Astryx Card padding fix, wizard review-step fixes, and the
bid-validation/ID-generation/UI-consistency work — see CHANGELOG.md.
Synced package-lock.json's duplicate version fields and the unused
coreState.version display field to match.
- Add CHANGELOG.md (new — this repo had no changelog convention before),
  covering every fix/change in this release.
- CLAUDE.md: document MIN_BID/assertValidBid, the isVideoFormat helper,
  the EmptyState component convention, and — most importantly — the
  CSS cascade-layers rule that caused the sitewide Card padding bug, so
  it doesn't get silently reintroduced by a future "helpful" reset rule.
- docs/FEATURES.md: the mobile/responsive breakpoint table described a
  "sidebar collapses to 200px" tablet behavior that was never actually
  implemented (and now that the sidebar is wired up, would be
  misleading) — corrected to match what the code actually does: hidden
  below 1100px, hamburger drawer takes over for both mobile and tablet.

Copy link
Copy Markdown
Owner Author

Pushed two more commits documenting this release:

  • Bumped package.json/package-lock.json to 3.6.0 (also synced the unused-but-should-match coreState.version field).
  • Added CHANGELOG.md (this repo had none) covering everything in this PR.
  • Updated CLAUDE.md with the conventions established this round: MIN_BID/assertValidBid, the isVideoFormat helper, the EmptyState component, and — most importantly — an explicit warning about the CSS cascade-layers gotcha that caused the sitewide Card-padding bug, so a future "helpful" reset doesn't silently reintroduce it.
  • Fixed docs/FEATURES.md's mobile breakpoint table, which described a "sidebar collapses to 200px" tablet behavior that was never actually implemented — corrected to match what the code (now that it's wired up) actually does.

665 tests passing, tsc --noEmit and npm run build clean.


Generated by Claude Code

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 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 `@src/components/AdConsole/wizard/__tests__/Step6ReviewLaunch.test.tsx`:
- Around line 57-70: Update the test around the “shows the SB/SD creative
headline, brand, and destination when set” case to explicitly set the creative
type to both SB and SD, preferably by parameterizing the test or adding separate
cases. Ensure each case updates the draft with the intended type before
rendering Step6ReviewLaunch and retains the existing headline, brand, and
destination assertions.

In `@src/components/AdConsole/wizard/Step6ReviewLaunch.tsx`:
- Around line 41-47: Update the campaign summary section in Step6ReviewLaunch to
use Astryx layout components instead of review-box/review-row divs and raw
span/strong elements. Apply one consistent Astryx row pattern to all displayed
fields, including targets, lookback, and creative values, while preserving the
existing conditional rendering and counts.

In `@src/engine/ad-console/features/integrity/__tests__/engine.test.ts`:
- Around line 118-136: The integrity test at
src/engine/ad-console/features/integrity/__tests__/engine.test.ts lines 118-136
must assert that runIntegrityCheck produces multiple issues before checking ID
uniqueness. In src/engine/ad-console/core/__tests__/simulation.test.ts lines
174-187, assert the first run produces terms and verify every first-run term
exists in the second run; update both tests to prevent passing with empty or
missing records.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 3f8851b2-c1e9-4603-a0a1-7970594e4157

📥 Commits

Reviewing files that changed from the base of the PR and between edb2369 and 2830a2f.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (17)
  • CHANGELOG.md
  • CLAUDE.md
  • docs/FEATURES.md
  • package.json
  • src/app/globals.css
  • src/components/AdConsole/features/reports/ReportsPage.tsx
  • src/components/AdConsole/wizard/Step6ReviewLaunch.tsx
  • src/components/AdConsole/wizard/__tests__/Step6ReviewLaunch.test.tsx
  • src/engine/ad-console/core/__tests__/engine.test.ts
  • src/engine/ad-console/core/__tests__/simulation.test.ts
  • src/engine/ad-console/core/engine/target.ts
  • src/engine/ad-console/core/slices/core.ts
  • src/engine/ad-console/features/integrity/__tests__/engine.test.ts
  • src/engine/ad-console/features/profiles/__tests__/engine.test.ts
  • src/engine/ad-console/features/reports/__tests__/engine.test.ts
  • src/engine/ad-console/features/reports/engine.ts
  • src/engine/ad-console/features/trainer/__tests__/engine.test.ts
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/components/AdConsole/features/reports/ReportsPage.tsx
  • src/engine/ad-console/core/slices/core.ts
  • src/engine/ad-console/core/engine/target.ts
  • src/engine/ad-console/features/reports/engine.ts

Comment thread src/components/AdConsole/wizard/__tests__/Step6ReviewLaunch.test.tsx Outdated
Comment thread src/components/AdConsole/wizard/Step6ReviewLaunch.tsx
Comment thread src/engine/ad-console/features/integrity/__tests__/engine.test.ts
…uous tests

CodeRabbit flagged that AdGroupsTab/TargetsTab silently do nothing when
a typed bid is below MIN_BID, with no visible error. Investigated both:

- AdGroupsTab uses Astryx's NumberInput with min={0.02} — it already
  shows real-time invalid styling (aria-invalid, red border, "Invalid
  number" live-region text) via the component's own built-in min
  validation, with no app code needed. Added a regression test proving
  this rather than adding redundant/dead validation logic.
- TargetsTab uses a raw native <input type="number">, which doesn't
  get that behavior for free — genuinely had no visible feedback.
  Added a red border, aria-invalid, and a title tooltip when the
  edited value is below MIN_BID.

Also fixed two review-flagged test gaps that could pass vacuously:
- The integrity same-millisecond-uniqueness test didn't assert the
  fixture actually produced more than one issue before checking
  uniqueness (an empty or single-item array trivially passes).
- The repeated-simulateDays dedup test didn't assert the first run
  produced any search terms, nor that the second run actually carried
  them forward — both now asserted explicitly.

Declined (with reasoning, per the PR's review thread): rewriting
Step6ReviewLaunch's review-row layout onto different Astryx layout
primitives — `review-row`/`review-box` is the same pattern already
used in OverviewTab.tsx; changing just one of the two would make them
inconsistent with each other, not more consistent.

Copy link
Copy Markdown
Owner Author

Addressed the latest round of review feedback:

Fixed:

  • The still-open "no visible feedback for invalid bids" thread: investigated both call sites rather than assuming they needed the same fix. AdGroupsTab uses Astryx's NumberInput with min={0.02}, which already shows real-time invalid styling (aria-invalid, red border, "Invalid number" live-region text) via the component's own built-in validation — added a regression test proving that instead of bolting on redundant/dead logic. TargetsTab uses a raw native <input type="number">, which genuinely had no such behavior — added a red border, aria-invalid, and a title tooltip there.
  • The Step6ReviewLaunch.test.tsx SB/SD creative test didn't actually set the campaign type, so it ran against the default SP draft — parameterized it over both types.
  • Two vacuous-test gaps: the integrity ID-uniqueness test didn't assert the fixture produced more than one issue (an empty/single-item array trivially passes uniqueness), and the repeated-simulateDays test didn't assert the first run actually produced terms or that the second run carried them forward. Both now assert explicitly, per the suggested diff.

Not doing, with reason: rewriting Step6ReviewLaunch's review-row/review-box layout onto different Astryx primitives — that's the exact same pattern already used in OverviewTab.tsx; changing just one of the two files would make them inconsistent with each other, which is a step backward, not forward.

668 tests passing, tsc --noEmit and npm run build clean.


Generated by Claude Code

@projectamazonph
projectamazonph merged commit 55e9772 into main Aug 3, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants