Skip to content

Conversation

@jeloskaisabel
Copy link

@jeloskaisabel jeloskaisabel commented Oct 28, 2025

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:

cd templates/agentic/travel-planner
npm install
Create .env.local with LAMATIC_API_KEY=your_key
npm run dev and open http://localhost:3000/

Live preview: https://agent-kit-vert.vercel.app/
Lamatic flow ID: 3cc791a2-ca33-4e27-8791-ff386cef14b2

Closes #31

Summary by CodeRabbit

New Features

  • Added a new travel planner application template featuring a comprehensive multi-agent AI workflow that generates customized trip plans based on user preferences.
  • Implemented an interactive travel planning interface with form inputs for origins, destinations, dates, budget, and interests.
  • Added detailed trip plan visualizations including flight options, accommodation recommendations, daily itineraries, activity suggestions, budget breakdowns, and booking tasks.
  • Included theme support for light and dark modes with responsive mobile design.
  • Integrated toast notifications for user feedback throughout the planning process.

@coderabbitai
Copy link

coderabbitai bot commented Oct 28, 2025

Caution

Review failed

Failed to post review comments

Walkthrough

This 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

Cohort / File(s) Summary
Configuration & Environment
templates/agentic/travel-planner/.env.example, .eslintrc.json, .gitignore, .npmrc, LICENSE
Environment variables placeholder, ESLint config, gitignore patterns for Node.js/Next.js, npm force flag, and MIT license with Lamatic.ai copyright.
Build & Deployment Configuration
templates/agentic/travel-planner/next.config.mjs, tsconfig.json, postcss.config.mjs, package.json
Next.js config disabling ESLint/TypeScript checks and image optimization; TypeScript strict mode with Next.js integration; PostCSS Tailwind plugin; project metadata, scripts (dev/build/lint/deploy), and dependencies (Radix UI, Next.js, React, Tailwind, zod, lamatic).
Application Layout & Page
templates/agentic/travel-planner/app/layout.tsx, app/page.tsx, app/globals.css
Root layout with Geist fonts and Analytics; full-featured travel planner page with form inputs (origin, destination, dates, budget, travelers, flight class, interests), preset trips, plan submission, and comprehensive itinerary display (flights, stays, activities, budget, visualizations); global CSS with Tailwind theme, color variables, and dark mode support.
UI Component Library
templates/agentic/travel-planner/components/ui/*.tsx
50+ shadcn/ui-based components: accessibility primitives (Accordion, AlertDialog, Alert, etc.), form controls (Button, Input, Checkbox, Select, RadioGroup, etc.), layout (Card, Sidebar, Sheet, Drawer, etc.), dialogs (Dialog, Popover, Tooltip, HoverCard), navigation (Breadcrumb, NavigationMenu, Pagination), data display (Table, Carousel, Progress), and specialized (Chart, OTP Input, Toggle, Toast). All use Radix UI primitives, Tailwind styling, and data-slot attributes for composition.
Theme & Utilities
templates/agentic/travel-planner/components/theme-provider.tsx, lib/utils.ts, lib/lamatic-client.ts
Theme provider wrapper around next-themes; cn() utility combining clsx and tailwind-merge; Lamatic client initialization with API key validation and config injection.
Hooks
templates/agentic/travel-planner/hooks/use-mobile.ts, hooks/use-toast.ts, components/ui/use-mobile.tsx, components/ui/use-toast.ts
Mobile breakpoint detection (768px); in-memory toast system with reducer, single-toast display limit, and delayed removal queue; subscription-based state management for toast notifications.
Core Business Logic
templates/agentic/travel-planner/actions/orchestrate.ts
Server-side travel planning orchestration: 16+ TypeScript interfaces for travel preferences, flight options, accommodations, activities, budgets, and booking tasks; extensive parsing utilities for normalizing heterogeneous input structures into a canonical NormalizedTravelPlan; planTrip() function that builds payloads, calls lamaticClient.executeFlow(), handles errors, and returns structured responses.
Lamatic Configuration & Documentation
templates/agentic/travel-planner/lamatic-config.json, README.md, scripts/deploy.sh
Lamatic workflow config defining "Multi-Agent Travel Planner" flow with input/output schemas and API details; comprehensive README with quick start, architecture, customization, and validation checklist; bash deployment script for npm install and dev startup.

Sequence Diagram

sequenceDiagram
    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
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Orchestration logic (orchestrate.ts): Dense data normalization, multiple parsing utilities, and complex type definitions require careful validation of edge cases and error handling.
  • Page component (app/page.tsx): Large, stateful component with form validation, async submission, multiple UI sections, and derived display logic across form and plan states.
  • Toast system (hooks/use-toast.ts): Custom in-memory state management with reducer, listener-based subscriptions, and removal queue scheduling—needs verification of memory leaks and state consistency.
  • UI components: While numerous (50+), they follow repetitive Radix UI wrapping patterns with consistent styling, reducing per-file review burden.
  • Breadth: Changes span multiple architectural layers (config, build, UI, logic, hooks), requiring context-switching across domains.

Areas requiring extra attention:

  • actions/orchestrate.ts: Verify parsing robustness for malformed nested structures, especially in parseBudget, parseVisualizations, and parseBookingTasks functions.
  • app/page.tsx: Confirm form validation logic, activity limit enforcement, and proper state cleanup to prevent memory leaks or duplicate submissions.
  • hooks/use-toast.ts: Check for race conditions in the removal queue, listener cleanup, and edge cases with rapid toast add/dismiss cycles.
  • lib/lamatic-client.ts: Validate API key validation error messaging and config loading.

Possibly related PRs

  • Fix : Config Removal #42: Related to Lamatic configuration and client initialization for agent templates; shares lamatic-config.json and .env setup patterns.

Poem

🐰 A journey begins with a hop and a click,
From forms to flights, the planner stays quick!
With Lamatic's wisdom and shadcn's grace,
Travel dreams land in the perfect place. ✈️🌍

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.39% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The pull request title "Feat/agentic travel planner" is directly related to the main change in the changeset, which introduces a complete multi-agent travel planning template. The title clearly indicates this is a feature addition ("Feat/") for an agentic (multi-agent) travel planner system. The title is concise, specific, and conveys the primary change without being misleading or overly vague. While the branch prefix naming convention in the title is somewhat unconventional for a PR title, it effectively communicates the purpose and nature of the changes to a teammate scanning through the history.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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

❤️ Share

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

Copy link

@coderabbitai coderabbitai bot left a 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: Invalid outline-hidden class.

Same issue as line 69 - outline-hidden should be outline-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 with inset prop.

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: Invalid outline-hidden class.

Same issue as line 69 - outline-hidden should be outline-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: Invalid outline-hidden class.

Same issue as line 69 - outline-hidden should be outline-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 with inset prop.

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 since DrawerPortal already 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 matchMedia matches property for consistency.

The code creates a MediaQueryList object but doesn't leverage its matches property in the onChange handler. Instead, it recalculates using window.innerWidth. While functional, this is inconsistent and slightly less efficient.

Apply this diff to use the event's matches property:

   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 undefined and 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: Use outline-none instead of outline-hidden (likely invalid).

outline-hidden isn’t a standard Tailwind utility; use outline-none and rely on focus-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} and title only. If you intend it to be mouse-only, fine; otherwise add aria-pressed/focus handling for keyboard users.


183-206: Mobile cookie semantics.

Toggling on mobile updates openMobile only (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 import

This 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 listener

Use 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 libs

Expose 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 focus

Forwarding 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 consistency

Adopt 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 label

Unknown 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: Require alt on AvatarImage for accessibility.

Ensure alt text 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 ref through 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, and CollapsibleContent.


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-describedby using the title/description IDs for stricter semantics.

templates/agentic/travel-planner/app/page.tsx (1)

188-197: Remove unused rawOutput (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 Tooltip component always creates its own TooltipProvider, which prevents users from sharing a provider across multiple tooltips (a common Radix UI pattern for managing delay and state). This means the exported TooltipProvider is 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 modifiers object may be recreated on each render, causing this effect to run more frequently than necessary. Since you only need modifiers.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-hidden should 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.value risks duplicates. Use dataKey or index fallback.
  • If item.color is 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 branch

Add a default returning current state for robustness.

   switch (action.type) {
@@
-  }
+  default:
+    return state
+  }

11-13: Hard-coded TOAST_LIMIT/TOAST_REMOVE_DELAY

Consider 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 wrappers

Without 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 composition

Enables 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 time

VisualizationResource.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 call

Prevents 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.

📥 Commits

Reviewing files that changed from the base of the PR and between 06d0dfb and e8853a4.

⛔ Files ignored due to path filters (3)
  • templates/agentic/travel-planner/app/icon.png is excluded by !**/*.png
  • templates/agentic/travel-planner/public/lamatic-logo.png is excluded by !**/*.png
  • templates/agentic/travel-planner/public/placeholder.svg is 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-slot attribute 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-group class 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-0 with first: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-hidden and rounded-xs are valid standard utilities in Tailwind CSS v4.1.9 (the version in use):

  • outline-hidden intentionally preserves outlines in forced-colors/accessibility mode while hiding them visually, making it more accessible than outline-none (which completely removes outlines).
  • rounded-xs is 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 Button component doesn’t default to type="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 that w-(--sidebar-width) and max-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 like data-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 .gitignore ignores config.json, but the README and other documentation consistently reference lamatic-config.json. Please verify:

  • If config.json is a different file that should be ignored
  • Or if this should be lamatic-config.json instead

Given that the README instructs users to include lamatic-config.json in 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.json
templates/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 v4

The 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 useMemo optimization for _values is 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. The asChild prop 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.tsx is a client component, it correctly imports planTrip as a server action (which has "use server" at the top of orchestrate.ts). In Next.js, when a client component calls a server action, the action's dependencies—including lamatic-client.ts—execute only on the server. process.env.LAMATIC_API_KEY is never exposed to the browser.

That said, adding explicit "use server" or "server-only" to lib/lamatic-client.ts is a best practice to prevent accidental client-side bundling:

"use server";
import { Lamatic } from "lamatic";
import config from "@/lamatic-config.json";
// ... rest of file

This 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 Table component ensures responsive behavior, and the checkbox-aware selectors in TableHead and TableCell demonstrate 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 InputOTPSlot implementation 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 cva for 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-slot attributes to AlertDialogAction and AlertDialogCancel (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 found

The code at lines 6-11 correctly extracts travelPlannerFlow from config.flows.travelPlanner with proper error handling. Verification confirms the lamatic-config.json contains flows.travelPlanner.workflowId as expected, and the planTrip function 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-xs and outline-hidden are not non-standard. Both are core Tailwind utilities:

  • rounded-xs is the standard name for the smallest border-radius in Tailwind v4 (previously rounded-sm in v3). It's a native utility.
  • outline-hidden is 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.tsx correctly uses these supported utilities.

Likely an incorrect or invalid review comment.

@@ -0,0 +1 @@
force=true No newline at end of file
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

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.

Suggested change
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.

Comment on lines +837 to +851
} 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,
};
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

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.

Suggested change
} 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.

Comment on lines +21 to +22
--destructive: oklch(0.577 0.245 27.325);
--destructive-foreground: oklch(0.577 0.245 27.325);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

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.

Suggested change
--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.

Comment on lines +9 to +13
export const metadata: Metadata = {
title: "Agent Kit Reasoning",
description: "AI-powered search and chat interface by Lamatic.ai",
generator: "v0.app",
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

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.

Suggested change
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.

Comment on lines +172 to +179
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]);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

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.

Suggested change
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.

Comment on lines +4 to +8
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."
);
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 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"
done

Length 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.

Comment on lines +3 to +12
eslint: {
ignoreDuringBuilds: true,
},
typescript: {
ignoreBuildErrors: true,
},
images: {
unoptimized: true,
},
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

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.

Suggested change
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",
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 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 version

Length 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.

Comment on lines +1 to +6
#!/bin/bash

npm install

# Run the dev server
npm run dev No newline at end of file
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

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.

Comment on lines +21 to +22
--destructive: oklch(0.577 0.245 27.325);
--destructive-foreground: oklch(0.577 0.245 27.325);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

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.

Suggested change
--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.

@amanintech
Copy link
Member

Thanks for your contribution @jeloskaisabel , reviewing it now.

@jeloskaisabel
Copy link
Author

@amanintech @d-pamneja can you review my PR please?

@d-pamneja
Copy link
Contributor

@jeloskaisabel we are in process to review it I'll get back to you shortly!!

@jeloskaisabel
Copy link
Author

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! :)

@jeloskaisabel
Copy link
Author

Hello @amanintech @d-pamneja any updates for this PR?

@d-pamneja
Copy link
Contributor

Hi @jeloskaisabel
Apologies for the delay. We have made a system now for you to publish the kit directly on our platform and show your contributions. You could explore the contribution guide and see how our kits are made and change your current one (needs no major lift and minor changes in your libs and actions folder) to complete this and merge. We can connect on slack as well if you have any doubts here. https://lamatic.ai/docs/slack

@jeloskaisabel
Copy link
Author

@d-pamneja thanks. I'll review it and make the changes this week.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Requested: agentic/travelplanner

3 participants