Appearance
Admin Panel — Screen Reviews
Platform-operator surfaces, prototype/admin-panel/ in the Claude Designs project. Shared sidebar shell, ds-base.js, real <a href> navigation between files (see CLAUDE.md in the design project).
Reviewed
- Templates — 🟢 Finding 1 fully resolved, 2026-07-19
- Subscriptions — 🔴 Findings logged, 2026-07-19
- Ad Manager (this page) — 🔴 Findings logged, 2026-07-19
- Affiliates / Growth Rewards — 🟢 All findings resolved (revalidated), 2026-07-19
Ad Manager — 🔴 Findings logged · Last reviewed 2026-07-19
File: prototype/admin-panel/AdManager.dc.html (73 KB). Full 6-step campaign builder (Creative → Placement → Targeting → Budget → Schedule → Review, confirmed matching SCREENS.md's documented scope exactly), campaign list with a kill switch, campaign detail + analytics, a self-serve advertiser portal section, and a creative library.
Correction to the earlier cross-cutting ad-architecture note
The original finding (written before this screen was read directly) said the dashboard-slot campaign system and the Templates block library were "two disconnected systems, neither of which is what a template-embedded ad would need." Reading AdManager.dc.html directly corrects part of that:
- AdManager and
manifest.jsare properly wired, not disconnected. Confirmed calls toQRPlatform.CAMPAIGNS,QRPlatform.removeCampaign,QRPlatform.setCampaignEnabled— the admin UI and the data layer are the same system, as they should be. - A template-embedded ad placement does exist as a concept: AdManager's placement picker includes a
slot:'servicecard.footer'option alongsidehome.banner,list.inline,catalogue.tile,offers.partner,notifications.feed, andsearch.results(7 slots total, more than the 4 seeded inmanifest.js's liveCAMPAIGNSarray). So it isn't accurate to say the ad system has no concept of a Service-Card-embedded placement.
The real, corrected finding is narrower and still real:
Finding — servicecard.footer is a defined slot with no consumer
Severity: major (an orphaned feature, not a missing one)
ServiceCard.dc.html was read in full in this review pass. It has no reference to QRPlatform, getPromo, or CAMPAIGNS anywhere. So an admin can create and target a servicecard.footer campaign in AdManager today, and it will never render anywhere. Supply-side tooling exists; the demand-side (the actual public page) doesn't consume it.
The other five slots beyond home.banner (which is confirmed consumed, in mobile-console/Home.dc.html) have not been individually verified against a consuming screen in this pass: list.inline, catalogue.tile, offers.partner, and notifications.feed need checking against their respective screens once reviewed. search.results has no obvious corresponding screen anywhere in the design project's own SCREENS.md — worth confirming whether a search feature is planned at all before treating that slot as real.
Design-fixable — copy-paste prompt for Claude Designs
markdown
In this project, prototype/admin-panel/AdManager.dc.html's placement picker includes a servicecard.footer
slot, but prototype/service-card/ServiceCard.dc.html (the actual public Service Card) never calls getPromo or
references QRPlatform anywhere, so a campaign targeted at that slot has nowhere to render. Please either:
1. Add the actual consuming placement in ServiceCard.dc.html (a footer-area sponsored slot, rendered via
getPromo('servicecard.footer', ws) the same way mobile-console/Home.dc.html already consumes
getPromo('home.banner', ws)), matching the existing visual pattern for a sponsored placement, or
2. If a footer ad on the public Service Card is not actually intended (it may work against the "premium,
ad-free-feeling public page" bar), remove servicecard.footer from AdManager's placement picker so the admin
tool doesn't offer a slot that can never do anything.
Also confirm whether the search.results slot corresponds to a planned search feature anywhere in the product.
If no search screen exists or is planned, flag this rather than leaving an orphaned slot in the picker.Finding — self-serve external advertiser portal is UI scaffolding only
Severity: major (scope question, not a screen defect)
Confirmed: a "Self-serve advertiser portal" section exists with copy "Advertiser accounts and self-serve portal... they manage their own campaigns, creatives and balance. The ops team reviews and approves." The "Add advertiser" action calls this.toast('Advertiser invite sent (demo)') — the design's own code marks this as a demo action, not a real flow.
A genuine self-serve advertiser portal is a third external-facing product surface (external advertisers need their own auth/accounts, and "manage their own balance" implies collecting payment for ad spend from external parties) layered on top of the QRSETU platform. Nothing in manifest.js's CONTRACTS.md tenant model (Account → Workspace → Member) accounts for an "advertiser" identity distinct from a merchant workspace.
Architecture-gated — not a Claude Designs prompt (tracked as QRS-041)
This is a product-scope decision (does QRSETU build a self-serve ad marketplace with external advertiser billing, or does ad sales stay an internal, admin-only, manually-invoiced function for the foreseeable future), not a design fix. It sits in the same category as the "ad-serving inside a paying merchant's dashboard needs product sign-off" finding already logged — both are trust/strategy calls above the design system's remit. Sending "please design advertiser billing" to Claude Designs today would just produce more UI scaffolding without a real advertiser identity/payment model behind it.
Finding — advertiser creative content has no sanitization/validation model
Severity: major (same class of risk as the Templates Custom HTML finding, from a less-trusted party)
Full-file grep for sanitiz, sandbox, iframe, dompurify, xss, csp in AdManager.dc.html returned zero matches, same as Templates before its fix. The creative library and campaign builder let an admin (or, per the advertiser-portal finding above, eventually an external advertiser) attach creative assets to a campaign that renders on real merchant dashboards and potentially the public Service Card. There is a designed human approval gate ("the ops team reviews and approves"), which is a real mitigation, but nothing confirms the creative assets themselves (images, copy) go through any format/size/content validation before or after that approval.
Architecture-gated — not a Claude Designs prompt yet (tracked as QRS-042)
The actual validation/sanitization implementation is a backend concern (file-type allowlisting, size limits, image re-encoding, safe-copy rendering). What is design-fixable right now, once that policy is decided, is disclosing it in the UI the same way Templates now does for Custom HTML (see the Templates revalidation) — that's a good template to reuse for AdManager once the underlying policy exists.
Finding — accessibility and a DS-convention violation
Severity: major, same pattern as Templates/Subscriptions
Full-file check: 4 aria-* attributes, 0 role= attributes, 0 keyboard handlers (onKeyDown/onKeyPress/tabIndex) across the file. Additionally, 4 native <select> elements were found (a placement filter and 3 targeting fields: business category, city/region, plan tier) — this directly violates the design project's own CLAUDE.md rule: "No browser-native <select>/date/number pickers. Use the inline dsSelect(...)."
Design-fixable — copy-paste prompt for Claude Designs
markdown
In prototype/admin-panel/AdManager.dc.html:
1. Four native <select> elements are used (a placement filter, and business category / city-region / plan
tier fields in the targeting step). Replace all four with the project's own dsSelect(id, value, options,
onChange, opts) dropdown, per this project's CLAUDE.md rule against native selects.
2. Add keyboard and screen-reader support: role="tab"/"tablist"/"tabpanel" with arrow-key navigation for the
section tabs, aria-label on icon-only buttons, a visible focus ring (--focus-ring token) on every
interactive element, and role="dialog"/aria-modal for any modal in the campaign builder.
Do not change any visual styling, spacing, or tokens.Confirmed compliant (do not re-flag)
- Token discipline: 381
var(--token)references, 0 raw hardcoded hex colors found. - Nav consistency: the sidebar array matches the same file list and order seen in Templates and Subscriptions (
Templates.dc.html,Moderation.dc.html,RBAC.dc.html, etc.), confirming one shared, consistent shell. - The 6-step campaign builder (
Creative, Placement, Targeting, Budget, Schedule, Review) matches the design project's ownSCREENS.mddescription exactly — this is documented scope, not scope creep. - A real approval gate for advertiser-submitted campaigns exists in copy ("the ops team reviews and approves"), a genuine (if partial) mitigation for the creative-content finding above.
Not yet reviewed
Overview.dc.html, Tenants.dc.html, Plans.dc.html, Moderation.dc.html, RBAC.dc.html, Billing.dc.html. Campaigns.dc.html is a known redirect to AdManager.dc.html (per the design project's own SCREENS.md), not a screen to review separately.