Initial Draft for the Vertex, Beacon and AirQo AI platform visibility design#3629
Conversation
…con, and Vertex - Updated navigation items to include links for AI Platform, Beacon, and Vertex. - Created new pages for AI Platform, Beacon, and Vertex with detailed marketing content. - Added images for AI Platform, Beacon, and Vertex to enhance visual representation. - Implemented a reusable ProductMarketingPage component for consistent layout across product pages.
|
Warning Review limit reached
More reviews will be available in 43 minutes and 17 seconds. Learn how PR review limits work. Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file). ⌛ How to resolve this issue?After more reviews become available, 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 include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughThree new product marketing pages—Vertex, Beacon, and AI Platform—are added to the website using a shared ChangesProduct Marketing Pages
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Warning Review ran into problems🔥 ProblemsErrors were encountered while retrieving linked issues. Errors (1)
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. Comment |
|
New azure website changes available for preview here |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (7)
src/website/src/app/(main)/products/ai-platform/page.tsx (1)
11-17: 💤 Low valueConsider removing the unnecessary wrapper div.
The wrapper
<div>serves no functional purpose here. You can simplify by directly returning the component:const page = () => { - return ( - <div> - <AIPlatformPage /> - </div> - ); + return <AIPlatformPage />; };This pattern applies to all three new product pages (AI Platform, Beacon, Vertex).
🤖 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/website/src/app/`(main)/products/ai-platform/page.tsx around lines 11 - 17, The page component currently returns a redundant wrapper div around AIPlatformPage; update the page function (page) to return the AIPlatformPage component directly (i.e., replace the surrounding <div> with a direct JSX return of <AIPlatformPage />) and apply the same change to the other new product page components (Beacon and Vertex) to remove unnecessary wrapper divs and simplify the render.src/website/src/views/products/ProductMarketingPage.tsx (5)
317-333: 💤 Low valueUse a more stable key for capabilities items.
Using
key={title}assumes all capability titles are unique. While they likely are in practice, using a compound key or accepting that these are static content would be more robust.🔑 Proposed fix if uniqueness is uncertain
- {capabilities.items.map(({ title, description, Icon }) => ( + {capabilities.items.map(({ title, description, Icon }, index) => ( <motion.div - key={title} + key={`capability-${index}`} className="rounded-xl border border-slate-200 bg-white p-6 shadow-sm"🤖 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/website/src/views/products/ProductMarketingPage.tsx` around lines 317 - 333, The mapped capability cards use key={title} which may collide if titles are not strictly unique; update the mapping in the capabilities.items.map callback to use a stable unique key such as an explicit id field on each capability (e.g., capability.id) or a compound key combining title with another stable property (e.g., `${title}-${description}`), and ensure the map callback references that unique identifier instead of title (look for the capabilities.items.map and the motion.div key prop).
130-149: 💤 Low valueProductActionButton always opens external links in new tabs.
The
openExternalLinkutility andProductActionButtoncomponent assume all actions are external URLs that should open in a new browser tab. If internal navigation is needed in the future (e.g., linking to other product pages within the site), this component won't support it without modification.Consider whether:
- All product CTAs will always point to external services (Vertex, Beacon, AI Platform URLs)
- Or if you might need internal routing later (e.g., to
/contactor other site pages)If internal routes are anticipated, the
ProductActiontype could include anexternal?: booleanflag to distinguish behavior.🤖 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/website/src/views/products/ProductMarketingPage.tsx` around lines 130 - 149, ProductActionButton currently always calls openExternalLink(action.href) which forces a new-tab external navigation; update the ProductAction type (add optional external?: boolean) and change ProductActionButton to branch: if action.external is true (or href looks like an absolute URL) call openExternalLink(action.href), otherwise perform internal navigation via the app router (e.g., useNavigate/useHistory or render a Link) so internal routes like "/contact" use client-side routing; keep openExternalLink and CustomButton usage but replace the onClick to choose the correct handler based on action.external/action.href.
359-377: 💤 Low valueUse index for key in use cases mapping.
Similar to the capabilities, using
key={item.title}assumes uniqueness. Since you're already usingindexfor the numbering display, it would be consistent to use it for the key as well.🔑 Proposed fix
{useCases.items.map((item, index) => ( <motion.div - key={item.title} + key={index} className="rounded-xl border border-slate-200 bg-white p-6 shadow-sm"🤖 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/website/src/views/products/ProductMarketingPage.tsx` around lines 359 - 377, The map over useCases.items uses key={item.title} which may not be unique; update the key on the <motion.div> rendered inside useCases.items.map (the mapping function using index) to use the index (or a combined stable identifier) instead of item.title — e.g., replace the current key reference in the motion.div produced by useCases.items.map with key={index} or key={`${index}-${item.title}`} so keys align with the displayed numbering and avoid duplicate-key issues.
455-474: 💤 Low valueUse index for key in quick links mapping.
Using
key={link.title}assumes unique titles. Using index would be more defensive.🔑 Proposed fix
- {ctaSection.quickLinks.map((link) => ( + {ctaSection.quickLinks.map((link, index) => ( <button - key={link.title} + key={index} type="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 `@src/website/src/views/products/ProductMarketingPage.tsx` around lines 455 - 474, The mapping over ctaSection.quickLinks currently uses key={link.title} which assumes titles are unique; change the map callback to accept the index (e.g., .map((link, idx) => ...)) and set the button key to the index (key={idx}) so each rendered button has a stable unique key; update the map signature where ctaSection.quickLinks is iterated and replace key usage on the button element accordingly.
406-414: 💤 Low valueUse index for key in audiences mapping.
Using
key={item}(the audience string itself) as a key has the same concerns as other mappings in this component.🔑 Proposed fix
- {audiences.items.map((item) => ( + {audiences.items.map((item, index) => ( <motion.div - key={item} + key={index} className="rounded-xl border border-white/70 bg-white/80 px-4 py-4 text-sm font-medium text-gray-800 shadow-sm"🤖 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/website/src/views/products/ProductMarketingPage.tsx` around lines 406 - 414, In ProductMarketingPage, the audiences mapping currently uses the audience string as the React key (key={item}); change the map to include the index (audiences.items.map((item, idx) => ...)) and use the index as the key (key={idx}) in the motion.div to avoid problems with non-unique or mutable string keys; update any references inside the map accordingly.src/website/src/views/products/BeaconPage.tsx (1)
28-70: Verify Beacon image asset existence
Both referenced files exist in the repo:
src/website/public/assets/images/products/beacon/beacon-device-render.webpsrc/website/public/assets/images/products/beacon/beacon-dashboard-showcase.pngIf you want consistency/compression, consider standardizing on
.webpfor the second image (unless there’s a reason to keep it as PNG).🤖 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/website/src/views/products/BeaconPage.tsx` around lines 28 - 70, The primarySection image is using a PNG while the hero image uses WEBP—update the src for primarySection.image (the object under primarySection in BeaconPage component) from '/assets/images/products/beacon/beacon-dashboard-showcase.png' to '/assets/images/products/beacon/beacon-dashboard-showcase.webp' to standardize formats (ensure the .webp file exists and update any tooling/static config if needed); locate the hero.image.src and primarySection.image.src objects in BeaconPage.tsx to make the consistent change.
🤖 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/website/src/lib/metadata.ts`:
- Around line 516-563: The metadata entries beacon, vertex, and aiPlatform in
src/website/src/lib/metadata.ts reference non-existent social preview images;
fix by either adding the missing image files at
/public/assets/images/products/beacon/beacon-dashboard-showcase.png,
/public/assets/images/products/vertex/vertex-dashboard-showcase.png, and
/public/assets/images/products/ai-platform/ai-platform-dashboard-showcase.png,
or update the image.url fields in the beacon, vertex, and aiPlatform objects to
point to the correct existing asset paths (ensure image.alt, width, height, and
type remain accurate).
In `@src/website/src/views/products/ProductMarketingPage.tsx`:
- Around line 202-204: The map over section.description uses the paragraph text
as a React key, which can cause duplicate-key warnings and unnecessary remounts;
change the callback in the section.description.map inside ProductMarketingPage
to accept the index (e.g., (paragraph, idx)) and use that index as the key
(key={idx}) instead of key={paragraph} so keys remain stable even if paragraph
content duplicates or changes.
---
Nitpick comments:
In `@src/website/src/app/`(main)/products/ai-platform/page.tsx:
- Around line 11-17: The page component currently returns a redundant wrapper
div around AIPlatformPage; update the page function (page) to return the
AIPlatformPage component directly (i.e., replace the surrounding <div> with a
direct JSX return of <AIPlatformPage />) and apply the same change to the other
new product page components (Beacon and Vertex) to remove unnecessary wrapper
divs and simplify the render.
In `@src/website/src/views/products/BeaconPage.tsx`:
- Around line 28-70: The primarySection image is using a PNG while the hero
image uses WEBP—update the src for primarySection.image (the object under
primarySection in BeaconPage component) from
'/assets/images/products/beacon/beacon-dashboard-showcase.png' to
'/assets/images/products/beacon/beacon-dashboard-showcase.webp' to standardize
formats (ensure the .webp file exists and update any tooling/static config if
needed); locate the hero.image.src and primarySection.image.src objects in
BeaconPage.tsx to make the consistent change.
In `@src/website/src/views/products/ProductMarketingPage.tsx`:
- Around line 317-333: The mapped capability cards use key={title} which may
collide if titles are not strictly unique; update the mapping in the
capabilities.items.map callback to use a stable unique key such as an explicit
id field on each capability (e.g., capability.id) or a compound key combining
title with another stable property (e.g., `${title}-${description}`), and ensure
the map callback references that unique identifier instead of title (look for
the capabilities.items.map and the motion.div key prop).
- Around line 130-149: ProductActionButton currently always calls
openExternalLink(action.href) which forces a new-tab external navigation; update
the ProductAction type (add optional external?: boolean) and change
ProductActionButton to branch: if action.external is true (or href looks like an
absolute URL) call openExternalLink(action.href), otherwise perform internal
navigation via the app router (e.g., useNavigate/useHistory or render a Link) so
internal routes like "/contact" use client-side routing; keep openExternalLink
and CustomButton usage but replace the onClick to choose the correct handler
based on action.external/action.href.
- Around line 359-377: The map over useCases.items uses key={item.title} which
may not be unique; update the key on the <motion.div> rendered inside
useCases.items.map (the mapping function using index) to use the index (or a
combined stable identifier) instead of item.title — e.g., replace the current
key reference in the motion.div produced by useCases.items.map with key={index}
or key={`${index}-${item.title}`} so keys align with the displayed numbering and
avoid duplicate-key issues.
- Around line 455-474: The mapping over ctaSection.quickLinks currently uses
key={link.title} which assumes titles are unique; change the map callback to
accept the index (e.g., .map((link, idx) => ...)) and set the button key to the
index (key={idx}) so each rendered button has a stable unique key; update the
map signature where ctaSection.quickLinks is iterated and replace key usage on
the button element accordingly.
- Around line 406-414: In ProductMarketingPage, the audiences mapping currently
uses the audience string as the React key (key={item}); change the map to
include the index (audiences.items.map((item, idx) => ...)) and use the index as
the key (key={idx}) in the motion.div to avoid problems with non-unique or
mutable string keys; update any references inside the map accordingly.
🪄 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
Run ID: a16d9fe8-84cf-452f-9c35-a0bae126f549
⛔ Files ignored due to path filters (1)
src/website/public/assets/images/products/beacon/beacon-dashboard-showcase.pngis excluded by!**/*.png
📒 Files selected for processing (18)
src/website/public/assets/images/products/ai-platform/ai-platform-forecast-showcase.webpsrc/website/public/assets/images/products/ai-platform/ai-platform-locate-showcase.webpsrc/website/public/assets/images/products/ai-platform/hero.webpsrc/website/public/assets/images/products/beacon/beacon-device-render.webpsrc/website/public/assets/images/products/beacon/person-call.webpsrc/website/public/assets/images/products/vertex/application-1.webpsrc/website/public/assets/images/products/vertex/bring-device.webpsrc/website/public/assets/images/products/vertex/vertex-dashboard-showcase.webpsrc/website/src/app/(main)/products/ai-platform/page.tsxsrc/website/src/app/(main)/products/beacon/page.tsxsrc/website/src/app/(main)/products/vertex/page.tsxsrc/website/src/app/sitemap.tssrc/website/src/lib/metadata.tssrc/website/src/lib/navItems.tssrc/website/src/views/products/AIPlatformPage.tsxsrc/website/src/views/products/BeaconPage.tsxsrc/website/src/views/products/ProductMarketingPage.tsxsrc/website/src/views/products/VertexPage.tsx
…mprove key usage in ProductMarketingPage
|
New azure website changes available for preview here |
…n for improved clarity and relevance
|
New azure website changes available for preview here |
|
New azure website changes available for preview here |
There was a problem hiding this comment.
Thanks @OchiengPaul442. On the vertex page, the some sections content feels too verbose eg: "Where Vertex creates value"(these are already mentioned in other sections), similarly "what makes vertex useful" section. I love the text combined with image approach(Can we adopt Binos monitor page style).
Can we create room for a call to action button linking to the download page for the vertex desktop app: https://vertex.airqo.net/download
…djust ProductMarketingPage padding
|
New azure website changes available for preview here |
There was a problem hiding this comment.
Pull request overview
Adds initial marketing/visibility pages for three AirQo products (Vertex, Beacon, and AirQo AI Platform) to the website, along with navigation, SEO metadata, and sitemap entries so the new routes are discoverable.
Changes:
- Introduced a shared
ProductMarketingPagetemplate and three new product pages (Vertex, Beacon, AirQo AI Platform). - Added new product routes under
/products/*with page-level metadata and viewport exports. - Updated nav items, footer links, sitemap, and centralized metadata configs for SEO/discoverability.
Reviewed changes
Copilot reviewed 11 out of 20 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| src/website/src/views/products/ProductMarketingPage.tsx | New shared marketing-page template used by the new product pages. |
| src/website/src/views/products/VertexPage.tsx | New Vertex product marketing page content wired to the shared template. |
| src/website/src/views/products/BeaconPage.tsx | New Beacon product marketing page content wired to the shared template. |
| src/website/src/views/products/AIPlatformPage.tsx | New AirQo AI Platform marketing page content wired to the shared template. |
| src/website/src/app/(main)/products/vertex/page.tsx | Adds the /products/vertex route and SEO metadata hookup. |
| src/website/src/app/(main)/products/beacon/page.tsx | Adds the /products/beacon route and SEO metadata hookup. |
| src/website/src/app/(main)/products/ai-platform/page.tsx | Adds the /products/ai-platform route and SEO metadata hookup. |
| src/website/src/lib/navItems.ts | Adds the three new products to the primary navigation menu. |
| src/website/src/lib/metadata.ts | Adds metadata configurations for the new product routes. |
| src/website/src/components/layouts/Footer.tsx | Adds footer links to the new product pages. |
| src/website/src/app/sitemap.ts | Adds the new product routes to the sitemap. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| <CustomButton | ||
| onClick={() => openExternalLink(action.href)} | ||
| className={`flex items-center justify-center ${className}`} | ||
| > | ||
| {action.label} | ||
| <BiLinkExternal className="ml-2 text-lg" /> | ||
| </CustomButton> |
| <div className="inline-flex rounded-full bg-slate-50 p-3 text-blue-700 shadow-sm"> | ||
| <Icon className="h-6 w-6" /> | ||
| </div> |
| <button | ||
| key={link.title} | ||
| type="button" | ||
| onClick={() => openExternalLink(link.href)} | ||
| className="w-full rounded-xl border border-slate-200 bg-white px-4 py-4 text-left transition-colors hover:border-blue-200 hover:bg-blue-50" | ||
| > | ||
| <div className="flex items-start justify-between gap-4"> | ||
| <div> | ||
| <p className="font-semibold text-gray-900"> | ||
| {link.title} | ||
| </p> | ||
| <p className="mt-1 text-sm leading-6 text-gray-700"> | ||
| {link.description} | ||
| </p> | ||
| </div> | ||
| <BiLinkExternal className="mt-1 h-5 w-5 flex-shrink-0 text-slate-500" /> | ||
| </div> | ||
| </button> |
…gPage accessibility
|
New azure website changes available for preview here |
|
New azure website changes available for preview here |
|
New azure website changes available for preview here |
Status of maturity (all need to be checked before merging):
Screenshots (optional)
Summary by CodeRabbit