-
Notifications
You must be signed in to change notification settings - Fork 2.8k
Program lander AI PoC #2614
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Program lander AI PoC #2614
Conversation
WalkthroughA new AI-powered feature for generating affiliate program landing pages from a website URL was introduced, involving server-side actions, UI components, context management, and schema updates. Image upload handling was improved for lander data, a FireCrawl integration was added, and new Tailwind animations support loading states. Various UI enhancements and overlays were implemented for a smoother user experience. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant LanderAIBanner
participant GenerateLanderModal
participant generateLanderAction
participant FireCrawl API
participant Claude AI
participant BrandingContext
participant PreviewWindow
User->>LanderAIBanner: Click "Generate"
LanderAIBanner->>GenerateLanderModal: Open modal
User->>GenerateLanderModal: Submit website URL
GenerateLanderModal->>BrandingContext: setIsGeneratingLander(true)
GenerateLanderModal->>generateLanderAction: Trigger with workspaceId, websiteUrl
generateLanderAction->>FireCrawl API: Scrape website/pricing content
generateLanderAction->>Claude AI: Prompt with scraped content and program info
Claude AI-->>generateLanderAction: Return generated lander data
generateLanderAction-->>GenerateLanderModal: Return lander data
GenerateLanderModal->>BrandingContext: setIsGeneratingLander(false)
GenerateLanderModal->>PreviewWindow: Update preview with new lander data
PreviewWindow->>User: Display updated preview
Suggested reviewers
Poem
📜 Recent review detailsConfiguration used: CodeRabbit UI ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (2)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
✨ Finishing Touches
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
|
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
🧹 Nitpick comments (5)
apps/web/lib/actions/partners/generate-lander.ts (1)
65-69: Consider adding content size validation before AI processing.Large scraped content could potentially exceed AI model context limits or incur unnecessary costs.
const mainPageMarkdown = cleanMarkdown(scrapeResult.markdown || ""); const pricingPageMarkdown = pricingScrapeResult?.success ? cleanMarkdown(pricingScrapeResult.markdown || "") : null; + + // Limit content size to prevent excessive AI processing + const MAX_CONTENT_LENGTH = 50000; // Adjust based on your needs + const truncatedMainPage = mainPageMarkdown.slice(0, MAX_CONTENT_LENGTH); + const truncatedPricingPage = pricingPageMarkdown?.slice(0, MAX_CONTENT_LENGTH);apps/web/ui/partners/design/lander-ai-banner.tsx (1)
28-28: Consider persisting banner hidden state.The banner reappears on every page load, which could be annoying for users who have already dismissed it.
- const [bannerHidden, setBannerHidden] = useState(false); + const [bannerHidden, setBannerHidden] = useLocalStorage( + `lander-ai-banner-hidden-${workspaceId}`, + false + );apps/web/ui/partners/design/branding-form.tsx (1)
178-181: Simplify boolean assignment by removing unnecessary ternary operator.The ternary operator is redundant when assigning a boolean value.
const disablePublishButton = - isGeneratingLander || (!isDirty && program.landerPublishedAt) - ? true - : false; + isGeneratingLander || (!isDirty && program.landerPublishedAt);apps/web/lib/actions/partners/update-program.ts (2)
175-175: Consider potential limitations of JSON cloning approach.While
JSON.parse(JSON.stringify())works for this use case, it has limitations with functions, dates, and other non-JSON values. For lander data structure, this should be fine, but consider using a more robust cloning solution if the data structure becomes more complex.
203-205: Consider enhancing error logging for better debugging.The current error logging is basic. Consider adding more context like the URL that failed to upload or the program ID.
- results.filter(isRejected).map((result) => { - console.error("Failed to upload lander image", result.reason); - }); + results.filter(isRejected).map((result, index) => { + console.error(`Failed to upload lander image for program ${programId}`, { + url: foreignImageUrls[index], + error: result.reason, + }); + });
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (11)
apps/web/.env.example(1 hunks)apps/web/lib/actions/partners/generate-lander.ts(1 hunks)apps/web/lib/actions/partners/update-program.ts(4 hunks)apps/web/lib/zod/schemas/program-lander.ts(2 hunks)apps/web/package.json(1 hunks)apps/web/tailwind.config.ts(2 hunks)apps/web/ui/partners/design/branding-context-provider.tsx(1 hunks)apps/web/ui/partners/design/branding-form.tsx(4 hunks)apps/web/ui/partners/design/lander-ai-banner.tsx(1 hunks)apps/web/ui/partners/design/preview-window.tsx(3 hunks)apps/web/ui/partners/design/previews/lander-preview.tsx(4 hunks)
🧰 Additional context used
🧠 Learnings (2)
apps/web/ui/partners/design/branding-context-provider.tsx (1)
Learnt from: TWilson023
PR: dubinc/dub#2519
File: apps/web/ui/analytics/utils.ts:35-37
Timestamp: 2025-06-16T19:21:23.506Z
Learning: In the `useAnalyticsFilterOption` function in `apps/web/ui/analytics/utils.ts`, the pattern `options?.context ?? useContext(AnalyticsContext)` is intentionally designed as a complete replacement strategy, not a merge. When `options.context` is provided, it should contain all required fields (`baseApiPath`, `queryString`, `selectedTab`, `requiresUpgrade`) and completely replace the React context, not be merged with it. This is used for dependency injection or testing scenarios.
apps/web/ui/partners/design/branding-form.tsx (1)
Learnt from: TWilson023
PR: dubinc/dub#2519
File: apps/web/ui/analytics/utils.ts:35-37
Timestamp: 2025-06-16T19:21:23.506Z
Learning: In the `useAnalyticsFilterOption` function in `apps/web/ui/analytics/utils.ts`, the pattern `options?.context ?? useContext(AnalyticsContext)` is intentionally designed as a complete replacement strategy, not a merge. When `options.context` is provided, it should contain all required fields (`baseApiPath`, `queryString`, `selectedTab`, `requiresUpgrade`) and completely replace the React context, not be merged with it. This is used for dependency injection or testing scenarios.
🧬 Code Graph Analysis (2)
apps/web/lib/actions/partners/update-program.ts (4)
apps/web/lib/zod/schemas/programs.ts (1)
ProgramWithLanderDataSchema(44-47)apps/web/lib/types.ts (1)
ProgramLanderData(410-410)apps/web/lib/zod/schemas/program-lander.ts (1)
programLanderImageBlockSchema(9-18)packages/utils/src/functions/promises.ts (2)
isRejected(5-7)isFulfilled(1-3)
apps/web/ui/partners/design/branding-form.tsx (1)
apps/web/ui/partners/design/branding-context-provider.tsx (2)
BrandingContextProvider(22-35)useBrandingContext(20-20)
🪛 Biome (1.9.4)
apps/web/ui/partners/design/branding-form.tsx
[error] 179-181: Unnecessary use of boolean literals in conditional expression.
Simplify your code by directly assigning the result without using a ternary operator.
If your goal is negation, you may use the logical NOT (!) or double NOT (!!) operator for clearer and concise code.
Check for more details about NOT operator.
Unsafe fix: Remove the conditional expression with
(lint/complexity/noUselessTernary)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build
🔇 Additional comments (28)
apps/web/tailwind.config.ts (2)
33-33: Clean animation implementation.The ellipsis-wave animation is well-implemented with appropriate timing and easing for a loading state indicator.
78-82: Well-structured keyframes definition.The keyframes create a smooth wave effect using CSS variables for flexibility. The timing progression (0%, 20%, 40%) provides good visual feedback.
apps/web/.env.example (1)
149-150: Good organization and documentation.The environment variable addition is well-organized with a clear section header and follows the established naming conventions.
apps/web/ui/partners/design/preview-window.tsx (4)
5-5: Clean type import addition.Good addition of
ReactNodeimport for the new overlay prop typing.
14-14: Well-typed overlay prop.The optional
overlayprop withReactNodetype is appropriately defined and integrated.Also applies to: 22-22
29-29: Good positioning context setup.Adding
relativeclass to the container provides the necessary positioning context for the overlay.
78-89: Clean overlay integration.The restructuring maintains the original scrolling functionality while cleanly integrating the overlay as a sibling element. The z-index management is appropriate.
apps/web/ui/partners/design/previews/lander-preview.tsx (6)
10-10: Good UI component imports.Clean addition of
CircleInfoandLoadingSpinnercomponents for the loading overlay.Also applies to: 12-12
32-32: Well-integrated context usage.Good integration of the branding context and AI banner component for managing the generation state.
Also applies to: 34-34
49-51: Clean state management.The
isGeneratingLanderstate integration from the branding context is clean and follows React patterns.
125-125: Good component placement.The
LanderAIBanneris logically placed above the preview window.
129-171: Excellent loading overlay implementation.The loading overlay provides comprehensive user feedback with:
- Smooth transitions and backdrop blur
- Accessible
inertattribute usage- Informative loading spinner with animated ellipsis
- Clear user guidance about reviewing generated content
- Proper conditional rendering and positioning
The implementation demonstrates excellent UX considerations and attention to accessibility.
152-160: Creative ellipsis animation usage.The animated ellipsis dots with staggered delays create a polished loading effect. The animation delay calculation (
${3 - i * -0.15}s) effectively creates the wave pattern.apps/web/ui/partners/design/branding-context-provider.tsx (1)
1-36: LGTM! Clean context implementation.The React context is well-structured with proper TypeScript types and follows best practices for state management.
apps/web/lib/zod/schemas/program-lander.ts (2)
65-65: Good addition of field description.The description helps clarify the unit of measurement for the price field.
91-101: Well-designed schemas for AI generation constraints.The simpler schemas appropriately exclude the "files" block type, which makes sense as AI cannot generate file uploads. This provides a clean subset of blocks for AI-generated content.
apps/web/lib/actions/partners/generate-lander.ts (1)
105-108: Good markdown cleaning implementation.The regex pattern correctly removes markdown image syntax. The use of
replaceAllis appropriate here.apps/web/ui/partners/design/branding-form.tsx (2)
72-79: Good integration with BrandingContext.The component properly wraps with the context provider and uses the context state to control UI behavior during landing page generation.
Also applies to: 171-171
186-195: Improved error handling in form submission.The enhanced error handling with proper user feedback via toast notifications improves the user experience.
apps/web/lib/actions/partners/update-program.ts (9)
7-22: LGTM! Import additions support the new lander data image upload functionality.The new imports are correctly added and used throughout the file for the image upload feature.
30-30: Good addition of lander data validation to the schema.The
programLanderSchema.nullish()properly validates the lander data structure while allowing null/undefined values.
42-42: Smart variable renaming to avoid naming conflicts.Renaming
landerDatatolanderDataInputclearly distinguishes between the input data and the processed data later in the function.
82-84: Proper conditional processing of lander data.The conditional logic correctly handles the case where
landerDataInputis null/undefined and only processes images when lander data is present.
158-161: Improved return structure with proper schema validation.The new return format with
successflag and parsed program data provides better consistency for action results.
177-185: Efficient filtering of foreign image URLs.The logic correctly identifies image blocks and filters out URLs already stored in the program's R2 path, preventing unnecessary re-uploads.
190-200: Robust concurrent upload handling.Using
Promise.allSettledis the right approach for handling multiple uploads where some may fail without affecting others.
211-220: Correct URL replacement logic.The logic properly finds and replaces the original URLs with the uploaded R2 URLs in the lander data structure.
164-223: Well-structured helper function with proper error handling.The
uploadLanderDataImagesfunction is well-designed with:
- Proper input validation and type safety
- Efficient filtering to avoid unnecessary uploads
- Robust error handling with
Promise.allSettled- Clear separation of concerns
The function handles the complex task of uploading images and updating URLs in a maintainable way.
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain@steven-tey I'll conduct a comprehensive final review of this AI-powered landing page generation feature. Let me analyze the key aspects: Final Review Summary✅ Strengths
|
Summary by CodeRabbit
New Features
Improvements
Other