-
Notifications
You must be signed in to change notification settings - Fork 21
Feat/agentic travel planner #50
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
base: main
Are you sure you want to change the base?
Feat/agentic travel planner #50
Conversation
|
Caution Review failedFailed to post review comments WalkthroughThis PR introduces a complete Lamatic-powered multi-agent travel planner template built with Next.js. It includes server-side orchestration logic for trip planning, a comprehensive React UI component library, form handling with validation, and integration with the Lamatic workflow platform via API. Changes
Sequence DiagramsequenceDiagram
participant User as User (Browser)
participant Page as app/page.tsx
participant Orchestrate as actions/orchestrate.ts
participant Lamatic as lamaticClient
participant API as Lamatic API
User->>Page: Submit travel form (origin, destination, dates, budget, interests)
Page->>Page: Validate inputs & activity limit
Page->>Orchestrate: planTrip(preferences)
Orchestrate->>Orchestrate: Build payload from preferences
Orchestrate->>Lamatic: executeFlow('travelPlanner', payload)
Lamatic->>API: POST /api/execute (endpoint, projectId, apiKey)
API-->>Lamatic: Raw response (flights, stays, activities, budget, etc.)
Lamatic-->>Orchestrate: Response data
Orchestrate->>Orchestrate: Normalize & parse response<br/>(parseBudget, parseVisualizations,<br/>parseBookingTasks, etc.)
Orchestrate-->>Page: PlanTripResponse (success or error)
Page->>Page: Update state & render itinerary<br/>(flights, daily plans, budget breakdown,<br/>visualizations, booking tasks)
Page-->>User: Display comprehensive travel plan
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes
Areas requiring extra attention:
Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
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 |
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: 39
♻️ Duplicate comments (5)
templates/agentic/travel-planner/components/ui/context-menu.tsx (5)
129-129: Duplicate: Invalidoutline-hiddenclass.Same issue as line 69 -
outline-hiddenshould beoutline-none.Apply this diff:
- "focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", + "focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
126-126: Duplicate: Data attribute issue withinsetprop.Same issue as line 67 - the attribute should be conditionally added only when inset is truthy.
Apply this diff:
<ContextMenuPrimitive.Item data-slot="context-menu-item" - data-inset={inset} + {...(inset && { "data-inset": true })} data-variant={variant} className={cn(
147-147: Duplicate: Invalidoutline-hiddenclass.Same issue as line 69 -
outline-hiddenshould beoutline-none.Apply this diff:
- "focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", + "focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-none select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
172-172: Duplicate: Invalidoutline-hiddenclass.Same issue as line 69 -
outline-hiddenshould beoutline-none.Apply this diff:
- "focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", + "focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-none select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
197-197: Duplicate: Data attribute issue withinsetprop.Same issue as line 67 - the attribute should be conditionally added only when inset is truthy.
Apply this diff:
<ContextMenuPrimitive.Label data-slot="context-menu-label" - data-inset={inset} + {...(inset && { "data-inset": true })} className={cn(
🧹 Nitpick comments (39)
templates/agentic/travel-planner/components/ui/skeleton.tsx (1)
3-11: Consider adding accessibility attributes for loading states.The component implementation is solid and follows React best practices. However, skeleton loaders should communicate loading state to assistive technologies.
Consider applying this diff to improve accessibility:
-function Skeleton({ className, ...props }: React.ComponentProps<"div">) { +function Skeleton({ className, ...props }: React.ComponentProps<"div">) { return ( <div data-slot="skeleton" className={cn("bg-accent animate-pulse rounded-md", className)} + role="status" + aria-live="polite" + aria-label="Loading..." {...props} /> ) }Optional: Add a display name for better debugging experience in React DevTools:
function Skeleton({ className, ...props }: React.ComponentProps<"div">) { return ( <div data-slot="skeleton" className={cn("bg-accent animate-pulse rounded-md", className)} {...props} /> ) } + +Skeleton.displayName = "Skeleton"templates/agentic/travel-planner/components/ui/radio-group.tsx (1)
45-45: LGTM! Clean exports.Both components are properly exported.
Optional: Consider adding display names to the components for improved debugging experience:
function RadioGroup({ className, ...props }: React.ComponentProps<typeof RadioGroupPrimitive.Root>) { return ( <RadioGroupPrimitive.Root data-slot="radio-group" className={cn("grid gap-3", className)} {...props} /> ) } +RadioGroup.displayName = "RadioGroup" function RadioGroupItem({ className, ...props }: React.ComponentProps<typeof RadioGroupPrimitive.Item>) { return ( <RadioGroupPrimitive.Item data-slot="radio-group-item" className={cn( "border-input text-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 aspect-square size-4 shrink-0 rounded-full border shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50", className )} {...props} > <RadioGroupPrimitive.Indicator data-slot="radio-group-indicator" className="relative flex items-center justify-center" > <CircleIcon className="fill-primary absolute top-1/2 left-1/2 size-2 -translate-x-1/2 -translate-y-1/2" /> </RadioGroupPrimitive.Indicator> </RadioGroupPrimitive.Item> ) } +RadioGroupItem.displayName = "RadioGroupItem"templates/agentic/travel-planner/components/ui/drawer.tsx (1)
54-54: Remove redundant data-slot prop.The
data-slot="drawer-portal"prop is redundant here sinceDrawerPortalalready sets this attribute internally (line 23). While functionally harmless, removing it improves code clarity.Apply this diff:
- <DrawerPortal data-slot="drawer-portal"> + <DrawerPortal>templates/agentic/travel-planner/components/ui/toggle.tsx (1)
9-29: Well-structured variant configuration.The toggleVariants CVA setup provides comprehensive styling with proper accessibility states (focus-visible, aria-invalid, disabled) and thoughtful SVG handling. The variant and size options are well-defined with sensible defaults.
For improved readability, the long base class string on line 10 could be split into multiple lines or grouped by concern (layout, interaction states, accessibility, SVG):
const toggleVariants = cva( - "inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium hover:bg-muted hover:text-muted-foreground disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0 focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] outline-none transition-[color,box-shadow] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive whitespace-nowrap", + [ + // Layout & typography + "inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap", + // Interaction states + "hover:bg-muted hover:text-muted-foreground", + "disabled:pointer-events-none disabled:opacity-50", + "data-[state=on]:bg-accent data-[state=on]:text-accent-foreground", + // Focus & validation + "outline-none transition-[color,box-shadow]", + "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]", + "aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive", + // SVG styling + "[&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0", + ].join(" "), {templates/agentic/travel-planner/components/ui/use-mobile.tsx (2)
9-14: Use the matchMediamatchesproperty for consistency.The code creates a
MediaQueryListobject but doesn't leverage itsmatchesproperty in theonChangehandler. Instead, it recalculates usingwindow.innerWidth. While functional, this is inconsistent and slightly less efficient.Apply this diff to use the event's
matchesproperty:React.useEffect(() => { const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`) - const onChange = () => { - setIsMobile(window.innerWidth < MOBILE_BREAKPOINT) + const onChange = (event: MediaQueryListEvent) => { + setIsMobile(event.matches) } mql.addEventListener("change", onChange) - setIsMobile(window.innerWidth < MOBILE_BREAKPOINT) + setIsMobile(mql.matches) return () => mql.removeEventListener("change", onChange) }, [])
5-6: Consider SSR hydration behavior.The hook initializes as
undefinedand returns!!isMobile(false on initial render). In a Next.js SSR context, this may cause a brief flash or hydration warning when the client is mobile. This is a common pattern, but consider whether a consistent default (always false) or a hydration suppression technique better fits the template's UX goals.Also applies to: 18-18
templates/agentic/travel-planner/components/ui/sidebar.tsx (5)
72-90: Persisted state: write-only cookie; add hydration + safer attributes.You set the cookie but never read it back, so “state persistence” doesn’t survive reloads. Also add SameSite and conditional Secure for hygiene.
Apply within this block:
const [_open, _setOpen] = React.useState(defaultOpen) const open = openProp ?? _open const setOpen = React.useCallback( (value: boolean | ((value: boolean) => boolean)) => { const openState = typeof value === "function" ? value(open) : value if (setOpenProp) { setOpenProp(openState) } else { _setOpen(openState) } // This sets the cookie to keep the sidebar state. - document.cookie = `${SIDEBAR_COOKIE_NAME}=${openState}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}` + document.cookie = + `${SIDEBAR_COOKIE_NAME}=${openState}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}; SameSite=Lax` + + (typeof window !== "undefined" && window.location.protocol === "https:" ? "; Secure" : "") }, [setOpenProp, open] ) + + // Hydrate from cookie on mount when uncontrolled + React.useEffect(() => { + if (openProp === undefined) { + const m = document.cookie.match(new RegExp(`(?:^|; )${SIDEBAR_COOKIE_NAME}=([^;]*)`)) + if (m) _setOpen(m[1] === "true") + } + }, [openProp])
96-110: Keyboard shortcut: guard typing contexts and avoid overriding Bold/Bookmarks.Currently Ctrl/Cmd+B will fire even in editors and prevents browser defaults. Guard inputs/contenteditable and don’t call preventDefault unless necessary.
React.useEffect(() => { const handleKeyDown = (event: KeyboardEvent) => { - if ( + const target = event.target as HTMLElement | null + const isTyping = + !!target && + (target.isContentEditable || + ["INPUT", "TEXTAREA", "SELECT"].includes(target.tagName)) + if ( event.key === SIDEBAR_KEYBOARD_SHORTCUT && (event.metaKey || event.ctrlKey) ) { - event.preventDefault() - toggleSidebar() + if (!isTyping) { + // Avoid stealing Cmd/Ctrl+B from editors unless we’re outside inputs. + toggleSidebar() + } } }
408-413: Useoutline-noneinstead ofoutline-hidden(likely invalid).
outline-hiddenisn’t a standard Tailwind utility; useoutline-noneand rely onfocus-visible:ring-*for focus styles.Also applies to: 429-435, 564-575, 689-695
282-304: Rail a11y: confirm intended non-focusable behavior.The rail uses
tabIndex={-1}andtitleonly. If you intend it to be mouse-only, fine; otherwise addaria-pressed/focus handling for keyboard users.
183-206: Mobile cookie semantics.Toggling on mobile updates
openMobileonly (cookie tracks desktop state). If desired, persist a separate mobile preference to avoid surprising resets.templates/agentic/travel-planner/hooks/use-mobile.ts (2)
1-1: Mark as client module to prevent accidental server importThis hook touches window; add the client directive to avoid usage in Server Components.
+ "use client" + import * as React from "react"
8-16: Use event.matches and add Safari fallback for media query listenerUse the MediaQueryListEvent.matches value; also support addListener/removeListener for older Safari.
React.useEffect(() => { - const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`) - const onChange = () => { - setIsMobile(window.innerWidth < MOBILE_BREAKPOINT) - } - mql.addEventListener("change", onChange) - setIsMobile(window.innerWidth < MOBILE_BREAKPOINT) - return () => mql.removeEventListener("change", onChange) + const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`) + const onChange = (e: MediaQueryListEvent | MediaQueryList) => + setIsMobile('matches' in e ? e.matches : mql.matches) + // init + onChange(mql) + // modern + Safari fallback + mql.addEventListener?.("change", onChange as any) + mql.addListener?.(onChange as any) + return () => { + mql.removeEventListener?.("change", onChange as any) + mql.removeListener?.(onChange as any) + } }, [])templates/agentic/travel-planner/components/ui/switch.tsx (1)
8-29: Forward refs for accessibility and form libsExpose refs on Root to enable focus management (labels, RHF).
-import * as React from "react" +import * as React from "react" ... -function Switch({ - className, - ...props -}: React.ComponentProps<typeof SwitchPrimitive.Root>) { - return ( - <SwitchPrimitive.Root +const Switch = React.forwardRef< + React.ElementRef<typeof SwitchPrimitive.Root>, + React.ComponentPropsWithoutRef<typeof SwitchPrimitive.Root> +>(({ className, ...props }, ref) => { + return ( + <SwitchPrimitive.Root + ref={ref} data-slot="switch" className={cn( "peer data-[state=checked]:bg-primary data-[state=unchecked]:bg-input focus-visible:border-ring focus-visible:ring-ring/50 dark:data-[state=unchecked]:bg-input/80 inline-flex h-[1.15rem] w-8 shrink-0 items-center rounded-full border border-transparent shadow-xs transition-all outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50", className )} {...props} > <SwitchPrimitive.Thumb ... - </SwitchPrimitive.Root> - ) -} + </SwitchPrimitive.Root> + ) +}) +Switch.displayName = "Switch"templates/agentic/travel-planner/components/ui/textarea.tsx (1)
5-16: Forward ref to support form libraries and focusForwarding the ref helps with react-hook-form, focusing, and accessibility.
-import * as React from "react" +import * as React from "react" ... -function Textarea({ className, ...props }: React.ComponentProps<"textarea">) { - return ( - <textarea +const Textarea = React.forwardRef< + HTMLTextAreaElement, + React.ComponentPropsWithoutRef<"textarea"> +>(({ className, ...props }, ref) => { + return ( + <textarea + ref={ref} data-slot="textarea" className={cn( "border-input placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 flex field-sizing-content min-h-16 w-full rounded-md border bg-transparent px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 md:text-sm", className )} {...props} /> - ) -} + ) +}) +Textarea.displayName = "Textarea"templates/agentic/travel-planner/components/ui/tabs.tsx (1)
8-19: Forward refs on Radix wrappers for consistencyAdopt forwardRef for Root/List/Trigger/Content to mirror Radix primitives and allow imperative focus.
If you prefer minimal change, at least wrap TabsTrigger to enable focusing selected tab via ref.
Also applies to: 21-35, 37-51, 53-64
templates/agentic/travel-planner/components/ui/toast.tsx (1)
77-87: Replace nonstandard toast-close attribute; add accessible labelUnknown attribute toast-close="" is unnecessary. Prefer data attribute and an aria-label for screen readers.
- <ToastPrimitives.Close + <ToastPrimitives.Close ... - toast-close="" + data-toast-close + aria-label="Close" > <X className="h-4 w-4" /> </ToastPrimitives.Close>templates/agentic/travel-planner/tsconfig.json (1)
5-5: Consider using a more modern ES target.The target is set to "ES6" (ES2015), which lacks features like async/await. Modern Next.js projects typically use "ES2017" or higher for better language feature support.
Apply this diff to update to a more modern target:
- "target": "ES6", + "target": "ES2017",templates/agentic/travel-planner/components/theme-provider.tsx (1)
1-11: LGTM!Clean wrapper around next-themes provider with proper prop forwarding.
Note: This file uses single quotes while other files in the template use double quotes. For consistency, consider standardizing on double quotes throughout the codebase.
templates/agentic/travel-planner/components/ui/input.tsx (1)
5-19: Forward ref for focus/control and set a sensible default type.Forwarding refs improves integration with form libs and focus management. Also default to type="text" explicitly.
-function Input({ className, type, ...props }: React.ComponentProps<"input">) { - return ( - <input - type={type} +const Input = React.forwardRef<HTMLInputElement, React.ComponentProps<"input">>( + ({ className, type = "text", ...props }, ref) => { + return ( + <input + ref={ref} + type={type} data-slot="input" className={cn( "file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm", "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]", "aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive", className )} {...props} /> - ) -} + ) + } +) + +Input.displayName = "Input"templates/agentic/travel-planner/components/ui/sonner.tsx (1)
6-23: Consider consolidating toast implementations.This Toaster wraps the Sonner library, while the codebase also includes another Toaster component in
toaster.tsx(based on Radix Toast primitives). Having two different toast systems can lead to:
- Confusion about which to use
- Inconsistent UX if both are used
- Unnecessary bundle size
Consider standardizing on one toast implementation across the application, or document when to use each if both are intentionally maintained.
templates/agentic/travel-planner/components/ui/button.tsx (1)
38-47: Forward refs for better interop (forms, focus, tooltips).Wrap Button with
React.forwardRef<HTMLButtonElement | HTMLAnchorElement, Props>so consumers can focus the element and compose with tooltips/popovers.I can provide a refactor if you want it included in this PR.
templates/agentic/travel-planner/components/ui/avatar.tsx (2)
24-35: Requirealton AvatarImage for accessibility.Ensure
alttext is provided (empty string for decorative). This prevents unlabeled images.Apply this diff to enforce at the type level:
-function AvatarImage({ - className, - ...props -}: React.ComponentProps<typeof AvatarPrimitive.Image>) { +function AvatarImage({ + className, + alt, + ...props +}: Omit<React.ComponentProps<typeof AvatarPrimitive.Image>, "alt"> & { + alt: string +}) { return ( <AvatarPrimitive.Image data-slot="avatar-image" - className={cn("aspect-square size-full", className)} - {...props} + className={cn("aspect-square size-full", className)} + alt={alt} + {...props} /> ) }
8-22: Optional: forward refs for Radix interop.Forward
refthrough Avatar, Image, and Fallback to keep parity with Radix primitives.templates/agentic/travel-planner/components/ui/collapsible.tsx (2)
11-19: Forward refs on CollapsibleTrigger.Radix triggers commonly rely on refs; forward them to avoid focus issues.
I can send a small forwardRef patch for
Collapsible,CollapsibleTrigger, andCollapsibleContent.
22-31: Forward refs on CollapsibleContent.Same reasoning as trigger; forward the ref for transitions/measurement.
templates/agentic/travel-planner/components/ui/alert.tsx (1)
22-35: LGTM — accessible alert surface.Solid defaults and CVA variant shape. Consider wiring
aria-labelledby/aria-describedbyusing the title/description IDs for stricter semantics.templates/agentic/travel-planner/app/page.tsx (1)
188-197: Remove unusedrawOutput(dead state).This memoized value isn’t rendered. Drop it to reduce cognitive load.
templates/agentic/travel-planner/components/ui/tooltip.tsx (1)
21-29: Consider allowing shared TooltipProvider usage.The
Tooltipcomponent always creates its ownTooltipProvider, which prevents users from sharing a provider across multiple tooltips (a common Radix UI pattern for managing delay and state). This means the exportedTooltipProvideris not usable in its intended role.Consider this alternative structure to enable provider sharing:
function Tooltip({ ...props }: React.ComponentProps<typeof TooltipPrimitive.Root>) { return ( - <TooltipProvider> - <TooltipPrimitive.Root data-slot="tooltip" {...props} /> - </TooltipProvider> + <TooltipPrimitive.Root data-slot="tooltip" {...props} /> ) }Users could then wrap multiple tooltips:
<TooltipProvider> <Tooltip>...</Tooltip> <Tooltip>...</Tooltip> </TooltipProvider>templates/agentic/travel-planner/components/ui/calendar.tsx (1)
183-186: Consider optimizing the useEffect dependency array.The
modifiersobject may be recreated on each render, causing this effect to run more frequently than necessary. Since you only needmodifiers.focused, extract it before the effect.Apply this diff to optimize:
function CalendarDayButton({ className, day, modifiers, ...props }: React.ComponentProps<typeof DayButton>) { const defaultClassNames = getDefaultClassNames() const ref = React.useRef<HTMLButtonElement>(null) + const isFocused = modifiers.focused React.useEffect(() => { - if (modifiers.focused) ref.current?.focus() - }, [modifiers.focused]) + if (isFocused) ref.current?.focus() + }, [isFocused])templates/agentic/travel-planner/components/ui/pagination.tsx (1)
102-117: Use explicit boolean value for aria-hidden.For clarity and consistency,
aria-hiddenshould be set to an explicit boolean value.Apply this diff:
return ( <span - aria-hidden + aria-hidden="true" data-slot="pagination-ellipsis" className={cn("flex size-9 items-center justify-center", className)} {...props} >templates/agentic/travel-planner/components/ui/chart.tsx (2)
186-221: Unstable React keys in tooltip items
key={item.dataKey}can be undefined or duplicated, causing key collisions. Use a stable composite fallback.- <div - key={item.dataKey} + <div + key={`${String(item.dataKey ?? item.name ?? "item")}-${index}`}Also applies to: 229-241
279-301: Legend key and color fallback
- Keying by
item.valuerisks duplicates. UsedataKeyor index fallback.- If
item.coloris missing, fall back to CSS var from ChartStyle.- {payload.map((item) => { + {payload.map((item, index) => { @@ - <div - key={item.value} + <div + key={String(item.dataKey ?? item.value ?? index)} @@ - <div - className="h-2 w-2 shrink-0 rounded-[2px]" - style={{ - backgroundColor: item.color, - }} - /> + <div + className="h-2 w-2 shrink-0 rounded-[2px]" + style={{ + backgroundColor: item.color ?? `var(--color-${String(item.dataKey ?? "value")})`, + }} + />templates/agentic/travel-planner/hooks/use-toast.ts (2)
77-130: Reducer lacks default branchAdd a default returning current state for robustness.
switch (action.type) { @@ - } + default: + return state + }
11-13: Hard-coded TOAST_LIMIT/TOAST_REMOVE_DELAYConsider making these configurable via props/env or export setters for UX tuning.
templates/agentic/travel-planner/components/ui/dialog.tsx (2)
33-47: Preserve ref forwarding on Radix wrappersWithout forwardRef, consumers cannot attach refs, breaking common patterns.
-function DialogOverlay({ - className, - ...props -}: React.ComponentProps<typeof DialogPrimitive.Overlay>) { - return ( - <DialogPrimitive.Overlay +const DialogOverlay = React.forwardRef< + React.ElementRef<typeof DialogPrimitive.Overlay>, + React.ComponentProps<typeof DialogPrimitive.Overlay> +>(({ className, ...props }, ref) => ( + <DialogPrimitive.Overlay data-slot="dialog-overlay" className={cn( "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50", className )} - {...props} + ref={ref} + {...props} /> - ) -} +)) +DialogOverlay.displayName = "DialogOverlay"
49-81: Forward ref through DialogContent and align with Radix compositionEnables focus management and parent ref access.
-function DialogContent({ - className, - children, - showCloseButton = true, - ...props -}: React.ComponentProps<typeof DialogPrimitive.Content> & { - showCloseButton?: boolean -}) { - return ( +const DialogContent = React.forwardRef< + React.ElementRef<typeof DialogPrimitive.Content>, + React.ComponentProps<typeof DialogPrimitive.Content> & { showCloseButton?: boolean } +>(({ className, children, showCloseButton = true, ...props }, ref) => { + return ( <DialogPortal data-slot="dialog-portal"> <DialogOverlay /> <DialogPrimitive.Content data-slot="dialog-content" className={cn( "bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg", className )} + ref={ref} {...props} > {children} {showCloseButton && ( <DialogPrimitive.Close data-slot="dialog-close" className="ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg]:not([class*='size-'])]:size-4" > <XIcon /> <span className="sr-only">Close</span> </DialogPrimitive.Close> )} </DialogPrimitive.Content> </DialogPortal> - ) -} + ) +}) +DialogContent.displayName = "DialogContent"templates/agentic/travel-planner/actions/orchestrate.ts (2)
404-421: embedHtml may be rendered later; sanitize at render timeVisualizationResource.embedHtml is carried through; ensure any UI that injects it sanitizes with DOMPurify (or avoid HTML injection). No change here, but add a TODO near the renderer.
Also applies to: 635-791
793-821: Add optional AbortSignal/timeout to flow callPrevents indefinite hangs if the external service stalls.
-export async function planTrip( - preferences: TravelPreferences -): Promise<PlanTripResponse> { +export async function planTrip( + preferences: TravelPreferences, + options?: { signal?: AbortSignal; timeoutMs?: number } +): Promise<PlanTripResponse> { try { @@ - const response = await lamaticClient.executeFlow( - travelPlannerFlow.workflowId, - payload - ); + const controller = + options?.signal ? null : new AbortController() + const timer = options?.timeoutMs + ? setTimeout(() => controller?.abort(), options.timeoutMs) + : undefined + const response = await lamaticClient.executeFlow( + travelPlannerFlow.workflowId, + payload, + { signal: options?.signal ?? controller?.signal } + ) + if (timer) clearTimeout(timer)
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (3)
templates/agentic/travel-planner/app/icon.pngis excluded by!**/*.pngtemplates/agentic/travel-planner/public/lamatic-logo.pngis excluded by!**/*.pngtemplates/agentic/travel-planner/public/placeholder.svgis excluded by!**/*.svg
📒 Files selected for processing (73)
templates/agentic/travel-planner/.env.example(1 hunks)templates/agentic/travel-planner/.eslintrc.json(1 hunks)templates/agentic/travel-planner/.gitignore(1 hunks)templates/agentic/travel-planner/.npmrc(1 hunks)templates/agentic/travel-planner/LICENSE(1 hunks)templates/agentic/travel-planner/README.md(1 hunks)templates/agentic/travel-planner/actions/orchestrate.ts(1 hunks)templates/agentic/travel-planner/app/globals.css(1 hunks)templates/agentic/travel-planner/app/layout.tsx(1 hunks)templates/agentic/travel-planner/app/page.tsx(1 hunks)templates/agentic/travel-planner/components.json(1 hunks)templates/agentic/travel-planner/components/theme-provider.tsx(1 hunks)templates/agentic/travel-planner/components/ui/accordion.tsx(1 hunks)templates/agentic/travel-planner/components/ui/alert-dialog.tsx(1 hunks)templates/agentic/travel-planner/components/ui/alert.tsx(1 hunks)templates/agentic/travel-planner/components/ui/aspect-ratio.tsx(1 hunks)templates/agentic/travel-planner/components/ui/avatar.tsx(1 hunks)templates/agentic/travel-planner/components/ui/badge.tsx(1 hunks)templates/agentic/travel-planner/components/ui/breadcrumb.tsx(1 hunks)templates/agentic/travel-planner/components/ui/button.tsx(1 hunks)templates/agentic/travel-planner/components/ui/calendar.tsx(1 hunks)templates/agentic/travel-planner/components/ui/card.tsx(1 hunks)templates/agentic/travel-planner/components/ui/carousel.tsx(1 hunks)templates/agentic/travel-planner/components/ui/chart.tsx(1 hunks)templates/agentic/travel-planner/components/ui/checkbox.tsx(1 hunks)templates/agentic/travel-planner/components/ui/collapsible.tsx(1 hunks)templates/agentic/travel-planner/components/ui/command.tsx(1 hunks)templates/agentic/travel-planner/components/ui/context-menu.tsx(1 hunks)templates/agentic/travel-planner/components/ui/dialog.tsx(1 hunks)templates/agentic/travel-planner/components/ui/drawer.tsx(1 hunks)templates/agentic/travel-planner/components/ui/dropdown-menu.tsx(1 hunks)templates/agentic/travel-planner/components/ui/form.tsx(1 hunks)templates/agentic/travel-planner/components/ui/hover-card.tsx(1 hunks)templates/agentic/travel-planner/components/ui/input-otp.tsx(1 hunks)templates/agentic/travel-planner/components/ui/input.tsx(1 hunks)templates/agentic/travel-planner/components/ui/label.tsx(1 hunks)templates/agentic/travel-planner/components/ui/menubar.tsx(1 hunks)templates/agentic/travel-planner/components/ui/navigation-menu.tsx(1 hunks)templates/agentic/travel-planner/components/ui/pagination.tsx(1 hunks)templates/agentic/travel-planner/components/ui/popover.tsx(1 hunks)templates/agentic/travel-planner/components/ui/progress.tsx(1 hunks)templates/agentic/travel-planner/components/ui/radio-group.tsx(1 hunks)templates/agentic/travel-planner/components/ui/resizable.tsx(1 hunks)templates/agentic/travel-planner/components/ui/scroll-area.tsx(1 hunks)templates/agentic/travel-planner/components/ui/select.tsx(1 hunks)templates/agentic/travel-planner/components/ui/separator.tsx(1 hunks)templates/agentic/travel-planner/components/ui/sheet.tsx(1 hunks)templates/agentic/travel-planner/components/ui/sidebar.tsx(1 hunks)templates/agentic/travel-planner/components/ui/skeleton.tsx(1 hunks)templates/agentic/travel-planner/components/ui/slider.tsx(1 hunks)templates/agentic/travel-planner/components/ui/sonner.tsx(1 hunks)templates/agentic/travel-planner/components/ui/switch.tsx(1 hunks)templates/agentic/travel-planner/components/ui/table.tsx(1 hunks)templates/agentic/travel-planner/components/ui/tabs.tsx(1 hunks)templates/agentic/travel-planner/components/ui/textarea.tsx(1 hunks)templates/agentic/travel-planner/components/ui/toast.tsx(1 hunks)templates/agentic/travel-planner/components/ui/toaster.tsx(1 hunks)templates/agentic/travel-planner/components/ui/toggle-group.tsx(1 hunks)templates/agentic/travel-planner/components/ui/toggle.tsx(1 hunks)templates/agentic/travel-planner/components/ui/tooltip.tsx(1 hunks)templates/agentic/travel-planner/components/ui/use-mobile.tsx(1 hunks)templates/agentic/travel-planner/components/ui/use-toast.ts(1 hunks)templates/agentic/travel-planner/hooks/use-mobile.ts(1 hunks)templates/agentic/travel-planner/hooks/use-toast.ts(1 hunks)templates/agentic/travel-planner/lamatic-config.json(1 hunks)templates/agentic/travel-planner/lib/lamatic-client.ts(1 hunks)templates/agentic/travel-planner/lib/utils.ts(1 hunks)templates/agentic/travel-planner/next.config.mjs(1 hunks)templates/agentic/travel-planner/package.json(1 hunks)templates/agentic/travel-planner/postcss.config.mjs(1 hunks)templates/agentic/travel-planner/scripts/deploy.sh(1 hunks)templates/agentic/travel-planner/styles/globals.css(1 hunks)templates/agentic/travel-planner/tsconfig.json(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (11)
templates/agentic/travel-planner/hooks/use-mobile.ts (1)
templates/agentic/travel-planner/components/ui/use-mobile.tsx (1)
useIsMobile(5-19)
templates/agentic/travel-planner/components/ui/form.tsx (1)
templates/agentic/travel-planner/components/ui/label.tsx (1)
Label(24-24)
templates/agentic/travel-planner/components/ui/sonner.tsx (1)
templates/agentic/travel-planner/components/ui/toaster.tsx (1)
Toaster(13-35)
templates/agentic/travel-planner/components/ui/command.tsx (1)
templates/agentic/travel-planner/components/ui/dialog.tsx (5)
Dialog(133-133)DialogHeader(138-138)DialogTitle(141-141)DialogDescription(136-136)DialogContent(135-135)
templates/agentic/travel-planner/components/ui/toaster.tsx (2)
templates/agentic/travel-planner/hooks/use-toast.ts (1)
useToast(194-194)templates/agentic/travel-planner/components/ui/toast.tsx (6)
ToastProvider(122-122)Toast(124-124)ToastTitle(125-125)ToastDescription(126-126)ToastClose(127-127)ToastViewport(123-123)
templates/agentic/travel-planner/components/ui/sidebar.tsx (1)
templates/agentic/travel-planner/hooks/use-mobile.ts (1)
useIsMobile(5-19)
templates/agentic/travel-planner/app/page.tsx (1)
templates/agentic/travel-planner/actions/orchestrate.ts (3)
NormalizedTravelPlan(124-141)planTrip(793-852)FlightLeg(113-117)
templates/agentic/travel-planner/hooks/use-toast.ts (1)
templates/agentic/travel-planner/components/ui/toast.tsx (3)
ToastProps(120-120)ToastActionElement(121-121)Toast(124-124)
templates/agentic/travel-planner/components/ui/use-toast.ts (1)
templates/agentic/travel-planner/components/ui/toast.tsx (3)
ToastProps(120-120)ToastActionElement(121-121)Toast(124-124)
templates/agentic/travel-planner/actions/orchestrate.ts (2)
templates/assistant/grammar-extension/sidepanel.js (1)
response(130-138)templates/agentic/travel-planner/lib/lamatic-client.ts (1)
lamaticClient(10-14)
templates/agentic/travel-planner/components/ui/alert-dialog.tsx (1)
templates/agentic/travel-planner/components/ui/button.tsx (1)
buttonVariants(59-59)
🪛 ast-grep (0.39.6)
templates/agentic/travel-planner/components/ui/chart.tsx
[warning] 82-82: Usage of dangerouslySetInnerHTML detected. This bypasses React's built-in XSS protection. Always sanitize HTML content using libraries like DOMPurify before injecting it into the DOM to prevent XSS attacks.
Context: dangerouslySetInnerHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
- https://cwe.mitre.org/data/definitions/79.html
(react-unsafe-html-injection)
templates/agentic/travel-planner/app/page.tsx
[warning] 1422-1422: Usage of dangerouslySetInnerHTML detected. This bypasses React's built-in XSS protection. Always sanitize HTML content using libraries like DOMPurify before injecting it into the DOM to prevent XSS attacks.
Context: dangerouslySetInnerHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
- https://cwe.mitre.org/data/definitions/79.html
(react-unsafe-html-injection)
🪛 Biome (2.1.2)
templates/agentic/travel-planner/components/ui/chart.tsx
[error] 83-83: Avoid passing content using the dangerouslySetInnerHTML prop.
Setting content using code can expose users to cross-site scripting (XSS) attacks
(lint/security/noDangerouslySetInnerHtml)
templates/agentic/travel-planner/app/page.tsx
[error] 1423-1424: Avoid passing content using the dangerouslySetInnerHTML prop.
Setting content using code can expose users to cross-site scripting (XSS) attacks
(lint/security/noDangerouslySetInnerHtml)
🔇 Additional comments (49)
templates/agentic/travel-planner/components/ui/aspect-ratio.tsx (1)
1-11: LGTM! Clean wrapper implementation.The AspectRatio component is a well-structured wrapper around Radix UI's primitive. The type safety, prop forwarding, and
data-slotattribute follow the established pattern for UI components in this codebase.templates/agentic/travel-planner/components/ui/skeleton.tsx (2)
1-2: LGTM!The import is clean and follows standard patterns for className merging utilities.
13-13: LGTM!The named export follows standard patterns for component libraries.
templates/agentic/travel-planner/components/ui/radio-group.tsx (3)
1-8: LGTM! Clean imports and proper Next.js client directive.The imports are well-organized, and the "use client" directive is correctly placed for this interactive component.
9-20: LGTM! Well-structured RadioGroup wrapper.The component properly forwards all props (including refs) and provides sensible default styling with the grid layout.
22-43: LGTM! Comprehensive styling with excellent accessibility.The RadioGroupItem component includes proper focus states, invalid states, disabled states, and dark mode support. The CircleIcon indicator is correctly centered.
templates/agentic/travel-planner/components/ui/drawer.tsx (1)
1-135: LGTM! Well-structured drawer component wrapper.The implementation follows established UI component patterns and properly wraps the vaul library primitives with consistent styling, data-slot attributes, and TypeScript typing. The directional styling logic correctly handles all four drawer directions (top, bottom, left, right), and the handle indicator appropriately displays only for bottom drawers.
templates/agentic/travel-planner/components/ui/toggle.tsx (2)
1-8: LGTM! Clean setup with proper directives.The imports and "use client" directive are correctly structured for a Next.js client component using Radix UI primitives.
31-47: LGTM! Correct export of both component and variants.The Toggle component properly wraps the Radix primitive and exports toggleVariants, which is essential for the ToggleGroup component to maintain consistent styling.
templates/agentic/travel-planner/components/ui/toggle-group.tsx (3)
1-15: LGTM! Well-designed context for variant inheritance.The ToggleGroupContext with default values enables child items to inherit variant and size from the parent group, promoting consistent styling and reducing prop duplication.
17-41: LGTM! Clean provider implementation.The ToggleGroup properly wraps children in a context provider and applies appropriate data attributes for styling hooks. The
group/toggle-groupclass enables group-based styling of descendants.
43-73: LGTM! Context consumption with proper fallback.The ToggleGroupItem correctly consumes context values with fallback to explicit props, enabling flexible usage. The styling on line 63 effectively creates a visually connected toggle group with:
- Collapsed borders between items (
border-l-0withfirst:border-l)- Selective rounding (
first:rounded-l-md,last:rounded-r-md)- Proper focus layering (
focus:z-10)The consistent fallback pattern (
context.variant || variant) across data attributes and variant application ensures predictable behavior.templates/agentic/travel-planner/components/ui/carousel.tsx (1)
174-232: Navigation buttons are well-implemented.Both CarouselPrevious and CarouselNext correctly consume context, adjust positioning based on orientation, respect scroll boundaries via the disabled state, and include accessible sr-only labels.
templates/agentic/travel-planner/components/ui/menubar.tsx (1)
114-138: No changes required. The review comment is incorrect.Both
outline-hiddenandrounded-xsare valid standard utilities in Tailwind CSS v4.1.9 (the version in use):
outline-hiddenintentionally preserves outlines in forced-colors/accessibility mode while hiding them visually, making it more accessible thanoutline-none(which completely removes outlines).rounded-xsis a standard v4 utility applying 0.125rem border-radius, not a custom class.The code is correct as written.
Likely an incorrect or invalid review comment.
templates/agentic/travel-planner/components/ui/context-menu.tsx (1)
236-252: LGTM!All components are properly exported and the export structure is clean.
templates/agentic/travel-planner/components/ui/sidebar.tsx (2)
264-278: Ensure the trigger doesn’t submit forms.If your
Buttoncomponent doesn’t default totype="button", clicking this inside a form will submit it. Consider explicitly setting the type.- <Button + <Button + type="button"
173-176: Based on my verification of the codebase and Tailwind v4 documentation:Tailwind CSS v4 exposes CSS variables and allows consuming them with syntax like
bg-(--color-primary), confirming thatw-(--sidebar-width)andmax-w-(--skeleton-width)are native Tailwind v4 features, not nonstandard or requiring custom plugins.The search results showed these patterns used consistently across many template files—not hypothetical edge cases. The w-(--gutter-width) syntax is documented Tailwind v4 for arbitrary CSS custom property values.
The other mentioned patterns (
[[data-*]]compound selectors,in-data-variants, and complex data attribute selectors) appear throughout the codebase in actual use, and Tailwind v4 supports data- prefixed variants likedata-selected:bg-emerald-600. The absence of visible build errors or style failures across multiple templates suggests these patterns are working as intended.No issues found. These are intentional Tailwind v4 syntax choices, not nonstandard patterns requiring verification.
templates/agentic/travel-planner/.eslintrc.json (1)
1-6: LGTM!The ESLint configuration correctly extends Next.js core web vitals and TypeScript rules, which is the recommended setup for Next.js projects with TypeScript.
templates/agentic/travel-planner/LICENSE (1)
1-21: LGTM!Standard MIT License with appropriate copyright notice for 2025.
templates/agentic/travel-planner/README.md (1)
1-107: LGTM! Excellent documentation.The README provides comprehensive, well-organized documentation covering setup, architecture, customization, and validation. The inclusion of specific workflow details, file path references, and a validation checklist makes this template immediately usable.
templates/agentic/travel-planner/components.json (1)
1-21: LGTM!The shadcn/ui configuration is properly set up with React Server Components, TypeScript, Tailwind CSS variables, and appropriate path aliases that align with the project structure.
templates/agentic/travel-planner/components/ui/label.tsx (1)
1-24: LGTM!Well-structured Label component following best practices:
- Proper "use client" directive for client-side interactivity
- Type-safe prop forwarding using Radix UI's component props
- Comprehensive accessibility styling including disabled and peer states
- Clean composition pattern using the cn utility
templates/agentic/travel-planner/.gitignore (1)
36-37: Verify the lamatic config filename.The
.gitignoreignoresconfig.json, but the README and other documentation consistently referencelamatic-config.json. Please verify:
- If
config.jsonis a different file that should be ignored- Or if this should be
lamatic-config.jsoninsteadGiven that the README instructs users to include
lamatic-config.jsonin their deployments and states it should be "uploaded to Vercel," this discrepancy could lead to confusion or accidental commits of the wrong file.If this should ignore
lamatic-config.json, apply this diff:#lamatic -config.json +lamatic-config.jsontemplates/agentic/travel-planner/components/ui/separator.tsx (1)
1-28: LGTM!Well-implemented Separator component with:
- Proper client directive for Radix UI
- Sensible defaults (
decorative=true,orientation="horizontal")- Data-attribute-based styling for both orientations
- Clean prop forwarding pattern
templates/agentic/travel-planner/postcss.config.mjs (1)
1-6: No changes needed—configuration is correct for Tailwind v4The template specifies Tailwind CSS v4.1.9, and the postcss.config.mjs correctly uses
'@tailwindcss/postcss'as the plugin. For Tailwind CSS v4, the PostCSS plugin is the separate package "@tailwindcss/postcss". The code is already properly configured.templates/agentic/travel-planner/components/ui/popover.tsx (1)
1-48: LGTM!Clean implementation of Radix UI Popover primitives with proper prop forwarding, Portal rendering, and consistent styling patterns.
templates/agentic/travel-planner/.env.example (1)
1-1: LGTM!Appropriate environment variable placeholder for Lamatic API integration.
templates/agentic/travel-planner/components/ui/checkbox.tsx (1)
1-32: LGTM!Well-structured Radix UI Checkbox wrapper with comprehensive state styling (checked, disabled, focus, invalid) and proper accessibility attributes.
templates/agentic/travel-planner/lib/utils.ts (1)
1-6: LGTM!Standard utility for merging Tailwind CSS classes using clsx and tailwind-merge. This pattern properly handles conditional classes and resolves Tailwind class conflicts.
templates/agentic/travel-planner/components/ui/toaster.tsx (1)
13-35: LGTM — toast mapping and provider/viewport wiring look correct.templates/agentic/travel-planner/styles/globals.css (1)
2-2: Now let me verify the Tailwind version being used:Remove the review comment; current animation import is correct for Tailwind v4.
tw-animate-css is a replacement for tailwindcss-animate that embraces the new CSS-first architecture. The correct usage for Tailwind v4 is
@import "tw-animate-css", which matches the current code exactly. shadcn/ui deprecated tailwindcss-animate in favor of tw-animate-css, with new projects defaulting to tw-animate-css. The original suggestion to use@plugin "tailwindcss-animate"is the legacy JavaScript configuration approach, not compatible with Tailwind v4's CSS-first model.templates/agentic/travel-planner/components/ui/progress.tsx (1)
8-29: LGTM!Clean implementation of a Radix UI Progress component. The transform calculation correctly positions the indicator based on the value prop, and the component properly forwards all standard ProgressRoot props.
templates/agentic/travel-planner/lamatic-config.json (1)
1-29: LGTM!Well-structured Lamatic configuration that properly defines the travel planner workflow schema. The workflowId matches the one referenced in the PR description, and the input/output schemas are comprehensive.
templates/agentic/travel-planner/app/globals.css (1)
1-125: Well-structured theme system.The CSS establishes a comprehensive design system with proper light/dark mode support and Tailwind integration. The theme token mapping is clean and maintainable.
templates/agentic/travel-planner/app/layout.tsx (1)
15-27: LGTM!Clean root layout implementation with proper font configuration, Suspense boundary, and analytics integration.
templates/agentic/travel-planner/components/ui/slider.tsx (1)
8-61: LGTM!Well-implemented Slider component that properly handles both single and multi-value scenarios. The
useMemooptimization for_valuesis appropriate, and the dynamic thumb rendering based on array length is clean.templates/agentic/travel-planner/components/ui/badge.tsx (1)
7-44: LGTM!Clean Badge component implementation with well-defined variants using
class-variance-authority. TheasChildprop support enables flexible composition, and the variant styles are comprehensive.templates/agentic/travel-planner/app/page.tsx (1)
6-13: The API key is properly protected; the current server action pattern is safe.The concern in the review is technically incorrect. While
page.tsxis a client component, it correctly importsplanTripas a server action (which has"use server"at the top oforchestrate.ts). In Next.js, when a client component calls a server action, the action's dependencies—includinglamatic-client.ts—execute only on the server.process.env.LAMATIC_API_KEYis never exposed to the browser.That said, adding explicit
"use server"or"server-only"tolib/lamatic-client.tsis a best practice to prevent accidental client-side bundling:"use server"; import { Lamatic } from "lamatic"; import config from "@/lamatic-config.json"; // ... rest of fileThis clarifies intent and hardens the boundary without changing runtime behavior.
Likely an incorrect or invalid review comment.
templates/agentic/travel-planner/components/ui/resizable.tsx (1)
1-56: LGTM!The resizable components correctly wrap the react-resizable-panels primitives with consistent data-slot attributes and className composition. The ResizableHandle's orientation-aware styling and optional grip icon are well implemented.
templates/agentic/travel-planner/components/ui/accordion.tsx (1)
1-66: LGTM!The accordion components correctly implement the Radix UI primitives with appropriate semantic structure, ARIA attributes, and animation states. The trigger includes accessible focus indicators and the chevron rotation enhances user experience.
templates/agentic/travel-planner/components/ui/dropdown-menu.tsx (1)
1-257: LGTM!The dropdown menu components provide a comprehensive and well-structured API surface. All wrappers correctly forward props to Radix UI primitives, apply consistent data-slot attributes, and include appropriate indicators (checkmarks, radio dots, chevrons). The variant system and inset support add useful flexibility.
templates/agentic/travel-planner/components/ui/table.tsx (1)
1-116: LGTM!The table components correctly wrap semantic HTML table elements with appropriate styling. The overflow container in the
Tablecomponent ensures responsive behavior, and the checkbox-aware selectors inTableHeadandTableCelldemonstrate thoughtful design for common use cases.templates/agentic/travel-planner/components/ui/input-otp.tsx (1)
1-77: LGTM!The OTP input components correctly integrate with the input-otp library. The
InputOTPSlotimplementation includes proper defensive coding with optional chaining (line 47) and comprehensive state-aware styling for active and invalid states. The visual caret indicator enhances user experience.templates/agentic/travel-planner/components/ui/card.tsx (1)
1-92: LGTM!The card component structure is well-organized with consistent use of data-slot attributes and proper prop forwarding. All seven card components follow the established patterns in the UI library.
templates/agentic/travel-planner/components/ui/select.tsx (1)
1-185: LGTM!The select component suite is comprehensive and well-implemented. It properly wraps Radix UI primitives with consistent styling, includes size variants, and maintains good accessibility with keyboard navigation and screen reader support.
templates/agentic/travel-planner/components/ui/navigation-menu.tsx (1)
1-168: LGTM!The navigation menu implementation is comprehensive and follows best practices. It includes proper animations, keyboard navigation support, and a flexible API with optional viewport rendering. The use of
cvafor styling variants maintains consistency across the component.templates/agentic/travel-planner/components/ui/alert-dialog.tsx (1)
1-157: LGTM with minor suggestion for consistency.The alert dialog implementation is solid and follows the established patterns. All components properly wrap Radix UI primitives with appropriate styling and accessibility features.
For consistency, consider adding
data-slotattributes toAlertDialogActionandAlertDialogCancel(lines 121-143) to match the pattern used by other components in this file:function AlertDialogAction({ className, ...props }: React.ComponentProps<typeof AlertDialogPrimitive.Action>) { return ( <AlertDialogPrimitive.Action + data-slot="alert-dialog-action" className={cn(buttonVariants(), className)} {...props} /> ) } function AlertDialogCancel({ className, ...props }: React.ComponentProps<typeof AlertDialogPrimitive.Cancel>) { return ( <AlertDialogPrimitive.Cancel + data-slot="alert-dialog-cancel" className={cn(buttonVariants({ variant: "outline" }), className)} {...props} /> ) }templates/agentic/travel-planner/actions/orchestrate.ts (1)
6-11: Config structure matches code expectations—no issues foundThe code at lines 6-11 correctly extracts
travelPlannerFlowfromconfig.flows.travelPlannerwith proper error handling. Verification confirms thelamatic-config.jsoncontainsflows.travelPlanner.workflowIdas expected, and theplanTripfunction correctly accesses this key at line 819.templates/agentic/travel-planner/components/ui/dialog.tsx (1)
71-76: Review comment is incorrect — both utilities are valid Tailwind v4 standards.The utilities
rounded-xsandoutline-hiddenare not non-standard. Both are core Tailwind utilities:
rounded-xsis the standard name for the smallest border-radius in Tailwind v4 (previouslyrounded-smin v3). It's a native utility.outline-hiddenis a standard Tailwind utility that outputs a transparent outline with offset, preserving accessibility for forced-colors mode while visually removing the default outline.The code at lines 71–76 in
templates/agentic/travel-planner/components/ui/dialog.tsxcorrectly uses these supported utilities.Likely an incorrect or invalid review comment.
| @@ -0,0 +1 @@ | |||
| force=true No newline at end of file | |||
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.
Remove force=true to prevent dependency conflicts.
The force=true flag forces npm to overwrite files and ignore conflicts, which can mask version mismatches and make builds less reproducible. This is particularly problematic in CI/CD environments and can lead to hard-to-debug issues.
Remove this line unless there's a specific, documented reason for forcing installations:
-force=true📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| force=true |
🤖 Prompt for AI Agents
In templates/agentic/travel-planner/.npmrc around lines 1 to 1, remove the
"force=true" setting because it forces npm to overwrite files and mask
dependency/version conflicts; delete that line (or comment it out) so installs
use default conflict handling and add a brief comment in the file if there was a
previous reason so future maintainers know why it was removed.
| } catch (error) { | ||
| console.error( | ||
| "[travel-planner] Failed to execute travel planner flow", | ||
| error | ||
| ); | ||
| const message = | ||
| error instanceof Error | ||
| ? error.message | ||
| : "Unexpected error while executing travel planner workflow."; | ||
|
|
||
| return { | ||
| success: false, | ||
| error: message, | ||
| }; | ||
| } |
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.
Log hygiene: avoid dumping full error objects (risk of sensitive data)
Remote client errors can include request/headers. Log minimal message and a safe code instead.
- console.error(
- "[travel-planner] Failed to execute travel planner flow",
- error
- );
+ const msg =
+ error instanceof Error ? error.message : "Unknown error"
+ console.error("[travel-planner] Failed to execute travel planner flow:", msg)📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| } catch (error) { | |
| console.error( | |
| "[travel-planner] Failed to execute travel planner flow", | |
| error | |
| ); | |
| const message = | |
| error instanceof Error | |
| ? error.message | |
| : "Unexpected error while executing travel planner workflow."; | |
| return { | |
| success: false, | |
| error: message, | |
| }; | |
| } | |
| } catch (error) { | |
| const msg = | |
| error instanceof Error ? error.message : "Unknown error" | |
| console.error("[travel-planner] Failed to execute travel planner flow:", msg) | |
| const message = | |
| error instanceof Error | |
| ? error.message | |
| : "Unexpected error while executing travel planner workflow."; | |
| return { | |
| success: false, | |
| error: message, | |
| }; | |
| } |
🤖 Prompt for AI Agents
templates/agentic/travel-planner/actions/orchestrate.ts around lines 837 to 851:
current catch block prints the full error object and returns the raw error
message; change it to log only a minimal, non-sensitive message (e.g.,
console.error("[travel-planner] Failed to execute travel planner flow:",
error?.message || "unknown error")) and do NOT log stack, request headers, or
the full error object; return a safe error code/string (for example success:
false, errorCode: "TRAVEL_PLANNER_EXECUTION_FAILED") and optionally include a
non-sensitive short message for users (e.g., "An internal error occurred"); if
you need to preserve specific error types, map known errors to distinct safe
codes instead of returning raw error text.
| --destructive: oklch(0.577 0.245 27.325); | ||
| --destructive-foreground: oklch(0.577 0.245 27.325); |
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.
Fix contrast issue: destructive foreground color.
The --destructive and --destructive-foreground are set to the same color value in light mode, which would result in insufficient contrast (text would be invisible on destructive buttons/alerts). The dark mode correctly uses different values (lines 57-58).
Apply this diff to fix the contrast:
--destructive: oklch(0.577 0.245 27.325);
- --destructive-foreground: oklch(0.577 0.245 27.325);
+ --destructive-foreground: oklch(0.985 0 0);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| --destructive: oklch(0.577 0.245 27.325); | |
| --destructive-foreground: oklch(0.577 0.245 27.325); | |
| --destructive: oklch(0.577 0.245 27.325); | |
| --destructive-foreground: oklch(0.985 0 0); |
🤖 Prompt for AI Agents
In templates/agentic/travel-planner/app/globals.css around lines 21-22, the
light-mode CSS variables set --destructive and --destructive-foreground to the
exact same oklch value which yields insufficient contrast; change
--destructive-foreground to a contrasting color (use a much darker oklch
lightness or the same foreground value used for dark mode at lines 57-58) so
text on destructive elements meets contrast requirements.
| export const metadata: Metadata = { | ||
| title: "Agent Kit Reasoning", | ||
| description: "AI-powered search and chat interface by Lamatic.ai", | ||
| generator: "v0.app", | ||
| } |
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.
Update metadata to reflect the travel planner purpose.
The metadata describes a "search and chat interface" with title "Agent Kit Reasoning", but this PR introduces a multi-agent travel planner. The metadata should accurately reflect the application's purpose for SEO and user clarity.
Apply this diff to update the metadata:
export const metadata: Metadata = {
- title: "Agent Kit Reasoning",
- description: "AI-powered search and chat interface by Lamatic.ai",
+ title: "Agent Kit Travel Planner",
+ description: "Multi-agent travel researcher and booking assistant by Lamatic.ai",
generator: "v0.app",
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| export const metadata: Metadata = { | |
| title: "Agent Kit Reasoning", | |
| description: "AI-powered search and chat interface by Lamatic.ai", | |
| generator: "v0.app", | |
| } | |
| export const metadata: Metadata = { | |
| title: "Agent Kit Travel Planner", | |
| description: "Multi-agent travel researcher and booking assistant by Lamatic.ai", | |
| generator: "v0.app", | |
| } |
🤖 Prompt for AI Agents
In templates/agentic/travel-planner/app/layout.tsx around lines 9 to 13, the
exported metadata currently references "Agent Kit Reasoning" and a generic
"search and chat interface"; update the metadata to reflect this app as a travel
planner by changing the title to something like "Travel Planner — Agentic
Multi‑Agent Planner" and the description to "AI-powered multi-agent travel
planner by Lamatic.ai" (leave generator unchanged), so replace the existing
title and description strings with these travel-planner-specific values.
| const today = useMemo(() => new Date().toISOString().split("T")[0], []); | ||
|
|
||
| const endDateMin = useMemo(() => { | ||
| if (form.startDate) { | ||
| return form.startDate > today ? form.startDate : today; | ||
| } | ||
| return today; | ||
| }, [form.startDate, today]); |
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.
Fix off‑by‑one dates: compute “today” in local time, not UTC.
toISOString().split("T")[0] uses UTC; in US time zones this can shift to tomorrow and block selecting the correct date.
Apply this diff:
- const today = useMemo(() => new Date().toISOString().split("T")[0], []);
+ const today = useMemo(() => {
+ const d = new Date();
+ const y = d.getFullYear();
+ const m = String(d.getMonth() + 1).padStart(2, "0");
+ const day = String(d.getDate()).padStart(2, "0");
+ return `${y}-${m}-${day}`;
+ }, []);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const today = useMemo(() => new Date().toISOString().split("T")[0], []); | |
| const endDateMin = useMemo(() => { | |
| if (form.startDate) { | |
| return form.startDate > today ? form.startDate : today; | |
| } | |
| return today; | |
| }, [form.startDate, today]); | |
| const today = useMemo(() => { | |
| const d = new Date(); | |
| const y = d.getFullYear(); | |
| const m = String(d.getMonth() + 1).padStart(2, "0"); | |
| const day = String(d.getDate()).padStart(2, "0"); | |
| return `${y}-${m}-${day}`; | |
| }, []); | |
| const endDateMin = useMemo(() => { | |
| if (form.startDate) { | |
| return form.startDate > today ? form.startDate : today; | |
| } | |
| return today; | |
| }, [form.startDate, today]); |
🤖 Prompt for AI Agents
In templates/agentic/travel-planner/app/page.tsx around lines 172 to 179,
replace the UTC-based today computation that uses toISOString().split("T")[0]
with a local-date string built from new Date() using getFullYear(), getMonth()+1
and getDate() with zero-padding to produce YYYY-MM-DD; keep it memoized the same
way and ensure endDateMin logic remains unchanged but uses this local today
value so dates in local timezones (e.g. US) don't shift to the wrong day.
| if (!process.env.LAMATIC_API_KEY) { | ||
| throw new Error( | ||
| "LAMATIC_API_KEY environment variable is not set. Please add it to your .env.local file." | ||
| ); | ||
| } |
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.
🧩 Analysis chain
Harden server-only usage and fix apiKey typing; avoid client-bundle hazards.
- Add server-only guard to prevent accidental client imports.
- Narrow env var to a string via a local const to satisfy TS strict.
Apply within this file:
import { Lamatic } from "lamatic";
import config from "@/lamatic-config.json";
-if (!process.env.LAMATIC_API_KEY) {
- throw new Error(
- "LAMATIC_API_KEY environment variable is not set. Please add it to your .env.local file."
- );
-}
+const apiKey = process.env.LAMATIC_API_KEY;
+if (!apiKey) {
+ throw new Error(
+ "LAMATIC_API_KEY environment variable is not set. Please add it to your .env.local file."
+ );
+}
export const lamaticClient = new Lamatic({
endpoint: config.api.endpoint,
projectId: config.api.projectId,
- apiKey: process.env.LAMATIC_API_KEY,
+ apiKey,
});Also add at the very top (outside the ranges above):
import "server-only"Run to ensure no client components import this module:
- Add LAMATIC_API_KEY to the Vercel project env (Production/Preview/Development).
- Include it in templates/agentic/travel-planner/.env.example for DX.
Also applies to: 10-14
🏁 Script executed:
#!/bin/bash
# Find "use client" files that import lamatic-client
rg -nP -C2 '^"use client"' templates/agentic/travel-planner | awk -F: '{print $1}' | sort -u | while read f; do
rg -n 'lamatic-client' "$f" && echo "Potential client import -> $f"
doneLength of output: 0
I need to gather more information about the codebase structure and verify the claims more thoroughly. Let me run targeted verification scripts.
---
🏁 Script executed:
```shell
#!/bin/bash
# 1. Check if the file exists and show its content
echo "=== Checking lamatic-client.ts existence ==="
if [ -f "templates/agentic/travel-planner/lib/lamatic-client.ts" ]; then
echo "File exists. Content:"
cat "templates/agentic/travel-planner/lib/lamatic-client.ts"
else
echo "File not found"
fi
echo ""
echo "=== Checking for 'use client' files in travel-planner ==="
fd -e ts -e tsx "." "templates/agentic/travel-planner" | while read f; do
head -1 "$f" | grep -q '"use client"' && echo "$f"
done
echo ""
echo "=== All imports of lamatic-client in travel-planner ==="
rg -n "lamatic-client" "templates/agentic/travel-planner" -g "*.ts" -g "*.tsx"
Length of output: 368
The specified directory and file don't exist. Let me search more broadly to locate them.
---
🏁 Script executed:
```shell
#!/bin/bash
# Search for lamatic-client file anywhere
echo "=== Searching for lamatic-client file ==="
find . -name "*lamatic-client*" -type f 2>/dev/null
echo ""
echo "=== Searching for travel-planner directory ==="
find . -name "travel-planner" -type d 2>/dev/null
echo ""
echo "=== Checking what's in templates/agentic if it exists ==="
if [ -d "templates/agentic" ]; then
ls -la "templates/agentic"
else
echo "templates/agentic does not exist"
fi
echo ""
echo "=== Top-level directory structure ==="
ls -la | head -20
Length of output: 1751
Now let me inspect the actual file content and verify the claims.
---
🏁 Script executed:
```shell
#!/bin/bash
echo "=== Content of travel-planner lamatic-client.ts ==="
cat "./templates/agentic/travel-planner/lib/lamatic-client.ts"
echo ""
echo "=== Checking if server-only is imported ==="
grep -n "server-only" "./templates/agentic/travel-planner/lib/lamatic-client.ts" || echo "Not found"
echo ""
echo "=== Checking tsconfig or similar for strict mode ==="
find "./templates/agentic/travel-planner" -name "tsconfig.json" -o -name "tsconfig.*.json" | head -1 | xargs cat 2>/dev/null || echo "No tsconfig found"
Length of output: 1146
Let me verify if this file is imported by client components and check the Next.js context.
---
🏁 Script executed:
```shell
#!/bin/bash
echo "=== Searching for imports of lamatic-client in travel-planner ==="
rg -n "lamatic-client" "./templates/agentic/travel-planner" --type ts --type tsx
echo ""
echo "=== Checking for 'use client' directives ==="
rg -n '"use client"' "./templates/agentic/travel-planner" --type ts --type tsx
echo ""
echo "=== Verifying if this is a Next.js project ==="
if [ -f "./templates/agentic/travel-planner/package.json" ]; then
echo "Found package.json:"
grep -E '"next"|"react"' "./templates/agentic/travel-planner/package.json" | head -5
else
echo "No package.json in travel-planner"
fi
echo ""
echo "=== Checking directory structure ==="
ls -la "./templates/agentic/travel-planner/app" 2>/dev/null || echo "No app directory"
ls -la "./templates/agentic/travel-planner/pages" 2>/dev/null || echo "No pages directory"
Length of output: 699
Let me retry the search with proper file glob patterns.
---
🏁 Script executed:
```shell
#!/bin/bash
echo "=== Searching for imports of lamatic-client ==="
rg -n "lamatic-client" "./templates/agentic/travel-planner" -g "*.ts" -g "*.tsx"
echo ""
echo "=== Checking page.tsx for 'use client' ==="
head -5 "./templates/agentic/travel-planner/app/page.tsx"
echo ""
echo "=== Checking layout.tsx for 'use client' ==="
head -5 "./templates/agentic/travel-planner/app/layout.tsx"
echo ""
echo "=== Full search for lamatic imports with context ==="
rg -n -B2 -A2 "lamatic-client" "./templates/agentic/travel-planner" -g "*.ts" -g "*.tsx"
Length of output: 1073
Type narrowing is essential with strict mode; server-only is defensive but actual client-import risk is already mitigated.
The concern about TypeScript typing is valid: with strict: true enabled, passing process.env.LAMATIC_API_KEY (typed as string | undefined) to the Lamatic constructor creates a type safety issue. The narrowing via a local const is necessary.
However, the client-import risk is overstated: lamatic-client is currently imported only by actions/orchestrate.ts (a "use server" server action), not by any client components. Adding "server-only" is still good defensive practice to prevent future accidental misuse, but the architectural risk is already mitigated.
Apply this diff:
import { Lamatic } from "lamatic";
import config from "@/lamatic-config.json";
+import "server-only";
+
+const apiKey = process.env.LAMATIC_API_KEY;
-if (!process.env.LAMATIC_API_KEY) {
+if (!apiKey) {
throw new Error(
"LAMATIC_API_KEY environment variable is not set. Please add it to your .env.local file."
);
}
export const lamaticClient = new Lamatic({
endpoint: config.api.endpoint,
projectId: config.api.projectId,
- apiKey: process.env.LAMATIC_API_KEY,
+ apiKey,
});This satisfies TypeScript strict mode and adds the defensive boundary.
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In templates/agentic/travel-planner/lib/lamatic-client.ts around lines 4–8,
narrow process.env.LAMATIC_API_KEY into a local const and add the defensive
server-only boundary: import "server-only"; const lamaticApiKey =
process.env.LAMATIC_API_KEY; if (!lamaticApiKey) { throw new
Error("LAMATIC_API_KEY environment variable is not set. Please add it to your
.env.local file."); } then pass lamaticApiKey (typed as string at runtime) to
the Lamatic constructor instead of using process.env.LAMATIC_API_KEY directly.
| eslint: { | ||
| ignoreDuringBuilds: true, | ||
| }, | ||
| typescript: { | ||
| ignoreBuildErrors: true, | ||
| }, | ||
| images: { | ||
| unoptimized: true, | ||
| }, | ||
| } |
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.
Don’t ship with lint/type errors ignored
Ignoring ESLint and TypeScript at build hides real defects and makes CI green when it shouldn’t. Gate these behind an env flag so templates dev fast locally but CI still protects main.
Apply:
/** @type {import('next').NextConfig} */
const nextConfig = {
- eslint: {
- ignoreDuringBuilds: true,
- },
- typescript: {
- ignoreBuildErrors: true,
- },
+ eslint: {
+ ignoreDuringBuilds: process.env.TEMPLATE_RELAXED_BUILD === "1",
+ },
+ typescript: {
+ ignoreBuildErrors: process.env.TEMPLATE_RELAXED_BUILD === "1",
+ },
images: {
unoptimized: true,
},
}Also ensure CI sets TEMPLATE_RELAXED_BUILD=0 (default).
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| eslint: { | |
| ignoreDuringBuilds: true, | |
| }, | |
| typescript: { | |
| ignoreBuildErrors: true, | |
| }, | |
| images: { | |
| unoptimized: true, | |
| }, | |
| } | |
| eslint: { | |
| ignoreDuringBuilds: process.env.TEMPLATE_RELAXED_BUILD === "1", | |
| }, | |
| typescript: { | |
| ignoreBuildErrors: process.env.TEMPLATE_RELAXED_BUILD === "1", | |
| }, | |
| images: { | |
| unoptimized: true, | |
| }, | |
| } |
🤖 Prompt for AI Agents
In templates/agentic/travel-planner/next.config.mjs around lines 3 to 12, don’t
unconditionally disable ESLint and TypeScript build checks; instead read an env
var (e.g. TEMPLATE_RELAXED_BUILD) and only set eslint.ignoreDuringBuilds and
typescript.ignoreBuildErrors to true when that var explicitly enables relaxed
builds (true/1), otherwise keep them false so CI fails on lint/type errors;
ensure the default behavior is strict (env var off) and update CI to set
TEMPLATE_RELAXED_BUILD=0.
| "@radix-ui/react-toggle": "1.1.1", | ||
| "@radix-ui/react-toggle-group": "1.1.1", | ||
| "@radix-ui/react-tooltip": "1.1.6", | ||
| "@vercel/analytics": "latest", |
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.
🧩 Analysis chain
Pin dependency versions instead of using "latest".
Three dependencies use "latest" instead of specific versions, which can cause non-deterministic builds and unexpected breaking changes:
- Line 47:
@vercel/analytics - Line 54:
geist - Line 64:
react-markdown
Consider pinning to specific versions for reproducible builds:
- "@vercel/analytics": "latest",
+ "@vercel/analytics": "^1.4.1",- "geist": "latest",
+ "geist": "^1.3.1",- "react-markdown": "latest",
+ "react-markdown": "^9.0.1",Run this script to check the latest stable versions:
Also applies to: 54-54, 64-64
🏁 Script executed:
#!/bin/bash
# Check latest versions for the unpinned packages
npm view @vercel/analytics version
npm view geist version
npm view react-markdown versionLength of output: 18
I need to verify the current state of the package.json file to confirm the lines mentioned.
---
🏁 Script executed:
```shell
#!/bin/bash
# Check the current state of package.json at the specified lines
sed -n '45,50p;52,56p;62,66p' templates/agentic/travel-planner/package.json
Length of output: 526
Pin dependency versions instead of using "latest".
Three dependencies use "latest" instead of specific versions, which can cause non-deterministic builds and unexpected breaking changes:
- Line 47:
@vercel/analytics - Line 54:
geist - Line 64:
react-markdown
Consider pinning to specific versions for reproducible builds:
- "@vercel/analytics": "latest",
+ "@vercel/analytics": "^1.5.0",- "geist": "latest",
+ "geist": "^1.5.1",- "react-markdown": "latest",
+ "react-markdown": "^10.1.0",Also applies to: 54-54, 64-64
🤖 Prompt for AI Agents
In templates/agentic/travel-planner/package.json (lines 47, 54, 64) the
dependencies use "latest"; replace each "latest" with a specific semver version
string to ensure deterministic builds: determine the desired version by running
`npm view <package> version` (or checking your repo policy), update
@vercel/analytics, geist, and react-markdown to those exact version numbers in
package.json, then run your package manager to regenerate and commit the
lockfile (package-lock.json or yarn.lock) alongside the changes.
| #!/bin/bash | ||
|
|
||
| npm install | ||
|
|
||
| # Run the dev server | ||
| npm run dev No newline at end of file |
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.
Misleading script name for development server.
The script is named deploy.sh but runs the development server (npm run dev) rather than performing a production deployment. This naming could confuse users about the script's purpose.
Consider renaming to dev.sh or start-dev.sh, or alternatively, adjust the script to perform actual deployment tasks.
Additionally, consider adding error handling:
#!/bin/bash
+set -e
npm install
# Run the dev server
npm run dev🤖 Prompt for AI Agents
In templates/agentic/travel-planner/scripts/deploy.sh around lines 1 to 6, the
script name is misleading because it runs the development server (npm run dev)
instead of performing a production deploy; either rename the file to dev.sh or
start-dev.sh to reflect its purpose, or change its contents to perform
deployment steps (install dependencies, build, and start a production server or
create artifacts); also add basic error handling (exit on failure and check npm
commands' exit codes) so the script fails fast and surfaces errors.
| --destructive: oklch(0.577 0.245 27.325); | ||
| --destructive-foreground: oklch(0.577 0.245 27.325); |
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.
Destructive colors have no contrast in light theme.
--destructive and --destructive-foreground are identical, harming readability and a11y. Use a high-contrast foreground.
- --destructive: oklch(0.577 0.245 27.325);
- --destructive-foreground: oklch(0.577 0.245 27.325);
+ /* Red-ish background + near-white foreground for contrast (~WCAG AA on buttons) */
+ --destructive: oklch(0.6 0.25 27);
+ --destructive-foreground: oklch(0.98 0.02 27);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| --destructive: oklch(0.577 0.245 27.325); | |
| --destructive-foreground: oklch(0.577 0.245 27.325); | |
| /* Red-ish background + near-white foreground for contrast (~WCAG AA on buttons) */ | |
| --destructive: oklch(0.6 0.25 27); | |
| --destructive-foreground: oklch(0.98 0.02 27); |
🤖 Prompt for AI Agents
templates/agentic/travel-planner/styles/globals.css lines 21-22: the two CSS
variables --destructive and --destructive-foreground are identical which
provides no contrast and breaks accessibility; change --destructive-foreground
to a high-contrast value against --destructive (e.g., a light foreground if
--destructive is dark or a dark foreground if --destructive is light), ensure
the chosen foreground meets WCAG contrast ratios (>=4.5:1 for normal text), and
update any usages or comments to reflect the new semantic intent.
|
Thanks for your contribution @jeloskaisabel , reviewing it now. |
|
@amanintech @d-pamneja can you review my PR please? |
|
@jeloskaisabel we are in process to review it I'll get back to you shortly!! |
|
Hi @d-pamneja , just checking in to see if there are any updates on the review. Please let me know if there’s anything I can clarify or adjust on my side. Thanks again for your time! :) |
|
Hello @amanintech @d-pamneja any updates for this PR? |
|
Hi @jeloskaisabel |
|
@d-pamneja thanks. I'll review it and make the changes this week. |
What: Introduces a multi-agent travel researcher and booking assistant that coordinates flight, hotel, and activity agents to deliver a shareable itinerary, budget, and booking checklist for any destination.
Prerequisites: Lamatic account with a valid LAMATIC_API_KEY.
Steps to run locally:
Live preview: https://agent-kit-vert.vercel.app/
Lamatic flow ID: 3cc791a2-ca33-4e27-8791-ff386cef14b2
Closes #31
Summary by CodeRabbit
New Features