Skip to content

Refine agent visibility controls and browsing filters#1609

Open
ivaavimusic wants to merge 4 commits intorecoupable:mainfrom
ivaavimusic:agents-visibility-filters
Open

Refine agent visibility controls and browsing filters#1609
ivaavimusic wants to merge 4 commits intorecoupable:mainfrom
ivaavimusic:agents-visibility-filters

Conversation

@ivaavimusic
Copy link
Copy Markdown

@ivaavimusic ivaavimusic commented Mar 30, 2026

Summary

This PR refines how public and private agent visibility is managed and viewed.

Changes

  • replaces the agents page public/private switch with a smaller segmented filter
  • simplifies create and edit agent visibility to a compact inline row
  • keeps email-sharing inputs directly below when private mode is enabled
  • adds fallback category tags so the modal remains usable when local data is sparse
  • refactors create and edit forms to share a dedicated agent form hook

Why

The previous visibility controls were harder to scan and took up too much space in the modal. This makes visibility feel like a normal form field while improving browsing on the agents page.

Risk

Low to medium. The change is mostly UI and state orchestration around existing visibility behavior.

Summary by CodeRabbit

  • New Features

    • Added help text for email sharing to clarify how to grant private agent access.
    • Introduced redesigned visibility control with public/private buttons for improved clarity.
  • Improvements

    • Refactored agent form handling for better state management.
    • Optimized tag selection to exclude system tags from user-selectable options.

@vercel
Copy link
Copy Markdown
Contributor

vercel bot commented Mar 30, 2026

@ivaavimusic is attempting to deploy a commit to the Recoupable Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 30, 2026

Warning

Rate limit exceeded

@ivaavimusic has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 15 minutes and 55 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 15 minutes and 55 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 21310721-53c1-42e8-b888-f9ade86d481b

📥 Commits

Reviewing files that changed from the base of the PR and between 2a2d670 and 81b6179.

📒 Files selected for processing (1)
  • components/Agents/AgentEditDialog.tsx
📝 Walkthrough

Walkthrough

This PR refactors agent form state management by extracting form initialization logic into a dedicated useAgentForm hook and creating new visibility control components. The form creation responsibility shifts from individual dialogs to parent components, while agent editing logic moves into a new useEditAgentTemplate hook. Visibility toggling is now encapsulated in reusable components (AgentVisibilityControl and AgentsVisibilityFilter).

Changes

Cohort / File(s) Summary
New Hook Infrastructure
hooks/useAgentForm.ts, hooks/useEditAgentTemplate.ts
Introduced two new hooks: useAgentForm handles form initialization with validation and reactive shareEmails clearing when privacy mode changes; useEditAgentTemplate wraps mutation logic for template updates via PATCH /api/agent-templates with cache invalidation.
Visibility Control Components
components/Agents/AgentVisibilityControl.tsx, components/Agents/AgentsVisibilityFilter.tsx
Added two new components for visibility toggling: AgentVisibilityControl integrates with react-hook-form for form-based privacy state; AgentsVisibilityFilter provides a button-based public/private toggle for filter UI.
Form Architecture Refactoring
components/Agents/CreateAgentForm.tsx, components/Agents/CreateAgentDialog.tsx, components/Agents/AgentEditDialog.tsx
Refactored form ownership: CreateAgentForm now receives form instance from parent instead of creating its own; dialogs now initialize forms via useAgentForm() hook and reset on dialog state changes; AgentEditDialog uses new useEditAgentTemplate for mutations.
Component Updates & Integration
components/Agents/PrivacySection.tsx, components/Agents/Agents.tsx, components/Agents/TagSelector.tsx, components/Agents/EmailShareInput.tsx
Updated components to use new visibility controls and architecture: PrivacySection replaced inline Switch with AgentVisibilityControl; Agents.tsx replaced inline toggle with AgentsVisibilityFilter; TagSelector precomputes non-"Recommended" tags; EmailShareInput added help text.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🎯 Forms now flow from parent's gentle hand,
Hooks orchestrate the schema and the land,
Visibility dances in components clean,
State management finally serene—
Architecture blooms, more organized and grand! ✨

🚥 Pre-merge checks | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Solid & Clean Code ⚠️ Warning PR violates Single Responsibility Principle with useEditAgentTemplate combining four distinct responsibilities and DRY principle with form.setValue pattern repeated 5 times across files. Extract email computation logic to utility function, create reusable formSetField helper, separate field-watching side effects into dedicated useFieldDependency hook, and document field dependencies.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a04a94f268

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines 90 to +94
useEffect(() => {
if (open) {
setCurrentSharedEmails(agent.shared_emails || []);
form.reset({
title: agent.title,
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Limit edit-form reset to initial dialog open

The new reset effect now depends on the entire agent object, so while the dialog is open any background query refresh that replaces agent (for example React Query refetch on window focus) will call form.reset(...) and discard unsaved edits. This is a user-facing data-loss regression in AgentEditDialog because typing into the form can be silently wiped mid-edit; the reset should be keyed to opening the dialog (or a stable identity change like agent.id) rather than every object refresh.

Useful? React with 👍 / 👎.

Comment on lines +19 to +21
<Switch
id="isPrivate"
checked={isPrivate}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Add an accessible name to the visibility switch

This switch is rendered without an associated <label>/aria-label, so assistive technologies will announce an unlabeled control and users cannot tell it changes agent visibility. The previous implementation had an explicit label, so this introduces an accessibility regression for screen-reader users in the create/edit form.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor

@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: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
components/Agents/AgentEditDialog.tsx (1)

46-62: ⚠️ Potential issue | 🟠 Major

Clear shareEmails when the agent is saved as public.

finalShareEmails ignores values.isPrivate, so flipping a private agent to public still PATCHes currentSharedEmails. That leaves stale sharing metadata attached to a public template.

🧹 Suggested fix
-      const finalShareEmails = values.shareEmails && values.shareEmails.length > 0
-        ? [...currentSharedEmails, ...values.shareEmails]
-        : currentSharedEmails;
+      const finalShareEmails = values.isPrivate
+        ? [...currentSharedEmails, ...(values.shareEmails ?? [])]
+        : [];
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/Agents/AgentEditDialog.tsx` around lines 46 - 62, finalShareEmails
currently concatenates currentSharedEmails with values.shareEmails regardless of
privacy state, so when values.isPrivate is false (agent made public) stale
currentSharedEmails are still sent; update the logic in AgentEditDialog where
finalShareEmails is computed to clear shareEmails when values.isPrivate is false
(i.e., set finalShareEmails = [] if !values.isPrivate), otherwise preserve the
existing merge behavior (using currentSharedEmails and values.shareEmails), and
ensure the PATCH body uses this corrected finalShareEmails field.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@components/Agents/Agents.tsx`:
- Around line 38-66: Wrap the two visibility buttons in an accessible group
(e.g., add role="group" and an aria-label or aria-labelledby) and mark each
toggle button with aria-pressed reflecting the current state so assistive tech
can announce which is active; specifically, update the Public and Private button
elements in Agents.tsx to include aria-pressed={!isPrivate} for the Public
button and aria-pressed={isPrivate} for the Private button (keep existing
onClick handlers like togglePrivate and the isPrivate state logic intact) and
ensure the container element has a clear aria-label describing the control.

In `@components/Agents/AgentVisibilityControl.tsx`:
- Around line 13-34: The Switch with id "isPrivate" is currently unlabeled for
assistive tech; give it an accessible name that reflects the Public/Private
mapping by either setting aria-label dynamically (e.g., "Private" when checked,
"Public" when unchecked) or wire aria-labelledby to one of the visible spans and
ensure that the labeled span text updates with the isPrivate state; update the
Switch props (id "isPrivate", checked={isPrivate}) to include that aria
attribute so screen readers announce the correct state while keeping the
existing form.setValue handler intact.

---

Outside diff comments:
In `@components/Agents/AgentEditDialog.tsx`:
- Around line 46-62: finalShareEmails currently concatenates currentSharedEmails
with values.shareEmails regardless of privacy state, so when values.isPrivate is
false (agent made public) stale currentSharedEmails are still sent; update the
logic in AgentEditDialog where finalShareEmails is computed to clear shareEmails
when values.isPrivate is false (i.e., set finalShareEmails = [] if
!values.isPrivate), otherwise preserve the existing merge behavior (using
currentSharedEmails and values.shareEmails), and ensure the PATCH body uses this
corrected finalShareEmails field.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 20f5cec7-8619-4c70-b4ce-7aae99778032

📥 Commits

Reviewing files that changed from the base of the PR and between 0a6fc82 and a04a94f.

📒 Files selected for processing (8)
  • components/Agents/AgentEditDialog.tsx
  • components/Agents/AgentVisibilityControl.tsx
  • components/Agents/Agents.tsx
  • components/Agents/CreateAgentDialog.tsx
  • components/Agents/CreateAgentForm.tsx
  • components/Agents/PrivacySection.tsx
  • components/Agents/TagSelector.tsx
  • components/Agents/useAgentForm.ts

@vercel
Copy link
Copy Markdown
Contributor

vercel bot commented Mar 30, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
recoup-chat Ready Ready Preview Mar 30, 2026 4:34pm

Request Review

@@ -117,5 +128,3 @@ const AgentEditDialog: React.FC<AgentEditDialogProps> = ({ agent }) => {
};

export default AgentEditDialog;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Single Responsibility Principle

  • actual: this file is longer than 100 lines of code.
  • required: break this monolithic component into smaller sub-components so each file is less than 100 lines of code.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I’ll break this down into smaller pieces under 100 lines each.

Comment on lines +39 to +66
<button
type="button"
onClick={() => {
if (isPrivate) togglePrivate();
}}
className={cn(
"inline-flex items-center justify-center whitespace-nowrap rounded-md px-3 py-1 text-sm font-medium transition-all cursor-pointer",
!isPrivate
? "bg-background text-foreground shadow"
: "text-muted-foreground hover:text-foreground"
)}
>
Public
</button>
<button
type="button"
onClick={() => {
if (!isPrivate) togglePrivate();
}}
className={cn(
"inline-flex items-center justify-center whitespace-nowrap rounded-md px-3 py-1 text-sm font-medium transition-all cursor-pointer",
isPrivate
? "bg-background text-foreground shadow"
: "text-muted-foreground hover:text-foreground"
)}
>
Private
</button>
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

SRP / OCP

  • actual: button component defined within general Agents component.
  • required: new button component file.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Understood. I’ll extract that button into its own component file.

Comment on lines +27 to +29
<p className="text-sm text-muted-foreground">
Add email addresses for the people who should be able to view this private agent.
</p>
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Open question: Why add this in PrivacySection instead of within EmailShareInput?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Yes, fair point. I placed it in PrivacySection because I treated that component as the container for all private-sharing behavior, but I agree the more specific ownership is EmailShareInput. I’ll move it there.

import { useAgentData } from "./useAgentData";
import { CreateAgentFormData } from "./schemas";

const FALLBACK_TAGS = [
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Why are you adding Fallback tags?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I added the fallback tags to preserve the category options locally when the backing tag data was sparse, mainly so the modal remained testable and closer to hosted behavior during local UI work. That said, I agree this introduces hardcoded behavior in the UI, so I’ll remove it from this PR.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Hooks need to go in hooks directory.

  • actual: components/Agents/useAgentForm.ts
  • required: hooks/useAgentForm.ts

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Agreed. I’ll move this hook into the hooks directory.

Copy link
Copy Markdown
Contributor

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

🧹 Nitpick comments (5)
components/Agents/PrivacySection.tsx (1)

26-33: Minor: Inconsistent JSX indentation.

The EmailShareInput component and its props have extra leading spaces compared to the surrounding JSX. While functionally correct, aligning the indentation would improve readability.

✨ Aligned indentation
      {isPrivate && (
        <EmailShareInput
-            emails={form.watch("shareEmails") ?? []}
-            existingSharedEmails={existingSharedEmails}
-            onEmailsChange={(emails) => {
-              form.setValue("shareEmails", emails, { shouldDirty: true, shouldValidate: true });
-            }}
-            onExistingEmailsChange={onExistingEmailsChange}
-          />
+          emails={form.watch("shareEmails") ?? []}
+          existingSharedEmails={existingSharedEmails}
+          onEmailsChange={(emails) => {
+            form.setValue("shareEmails", emails, { shouldDirty: true, shouldValidate: true });
+          }}
+          onExistingEmailsChange={onExistingEmailsChange}
+        />
      )}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/Agents/PrivacySection.tsx` around lines 26 - 33, The JSX for
EmailShareInput is misaligned with extra leading spaces; open the component
block containing EmailShareInput and normalize indentation so the tag and all
its props (EmailShareInput, emails prop using form.watch("shareEmails"),
existingSharedEmails, onEmailsChange handler that calls form.setValue(...), and
onExistingEmailsChange) align with the surrounding JSX structure—ensure each
prop is indented consistently (same level as neighboring components) for
readable, consistent formatting.
hooks/useAgentForm.ts (1)

24-30: Simplify the useEffect - the else-if branch is unreachable.

Given that defaultValues.shareEmails is always initialized to initialValues?.shareEmails ?? [] on line 18, form.getValues("shareEmails") will never be falsy. The condition on line 27 is effectively dead code.

♻️ Simplified effect
  useEffect(() => {
    if (!isPrivate) {
      form.setValue("shareEmails", []);
-   } else if (!form.getValues("shareEmails")) {
-     form.setValue("shareEmails", []);
    }
  }, [isPrivate, form]);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@hooks/useAgentForm.ts` around lines 24 - 30, The else-if branch in the
useEffect is unreachable because defaultValues.shareEmails is always initialized
(via initialValues?.shareEmails ?? []), so simplify the effect: keep the check
on isPrivate and call form.setValue("shareEmails", []) when !isPrivate, and
remove the redundant else-if that checks form.getValues("shareEmails"); update
the useEffect that references isPrivate, form.setValue, and form.getValues
accordingly.
hooks/useEditAgentTemplate.ts (2)

22-24: Consider deduplicating emails before sending to API.

The merge of currentSharedEmails and values.shareEmails could theoretically produce duplicates if the same email appears in both arrays. While the current UI flow makes this unlikely (per context, form always resets shareEmails to []), adding deduplication would be a defensive measure against future edge cases.

♻️ Optional deduplication
      const finalShareEmails = values.isPrivate
-       ? [...currentSharedEmails, ...(values.shareEmails ?? [])]
+       ? [...new Set([...currentSharedEmails, ...(values.shareEmails ?? [])])]
        : [];
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@hooks/useEditAgentTemplate.ts` around lines 22 - 24, The merged email array
assigned to finalShareEmails in useEditAgentTemplate.ts can contain duplicates
from currentSharedEmails and values.shareEmails; update the assignment so when
values.isPrivate is true you deduplicate the combined list (e.g., use a Set or
filter to preserve first-occurrence order) before sending to the API, while
keeping the existing isPrivate branch behavior (and ensure the deduplication
happens only when computing finalShareEmails).

29-31: Consider guarding against undefined userId before making the request.

If userData hasn't loaded yet, userId will be undefined, and the API will reject with a 400 error (per the PATCH handler at app/api/agent-templates/route.ts:154-156). While the mutation will throw and error handling will catch it, the user experience would be clearer with an early guard or by disabling the submit action until user data is available.

🛡️ Proposed early validation
  return useMutation({
    mutationFn: async (values: CreateAgentFormData) => {
+     if (!userData?.id) {
+       throw new Error("User not authenticated");
+     }
+
      const finalShareEmails = values.isPrivate
        ? [...currentSharedEmails, ...(values.shareEmails ?? [])]
        : [];

      const res = await fetch("/api/agent-templates", {
        method: "PATCH",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          id: agent.id,
-         userId: userData?.id,
+         userId: userData.id,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@hooks/useEditAgentTemplate.ts` around lines 29 - 31, The request body may
include an undefined userId when userData hasn't loaded; update the mutation in
useEditAgentTemplate (the code that builds body: JSON.stringify({ id: agent.id,
userId: userData?.id, ... })) to guard early: if userData?.id is falsy, abort
the request and surface a clear error (or return early) so no PATCH is sent with
undefined userId; alternatively ensure the submit action is disabled until
userData is present—change the check in the mutation handler that constructs the
body to validate userData?.id and handle the missing user id before calling
fetch.
components/Agents/AgentsVisibilityFilter.tsx (1)

3-11: Refactor to a value-change API to support controlled + uncontrolled usage.

The current togglePrivate contract couples this component to flip semantics and forces click guards in the view layer. Prefer a standard value/defaultValue + onValueChange(next) shape so the component can be reused cleanly in both controlled and uncontrolled modes.

♻️ Proposed refactor
+import { useState } from "react";
 import { cn } from "@/lib/utils";

 interface AgentsVisibilityFilterProps {
-  isPrivate: boolean;
-  togglePrivate: () => void;
+  isPrivate?: boolean;
+  defaultIsPrivate?: boolean;
+  onIsPrivateChange?: (next: boolean) => void;
 }

 const AgentsVisibilityFilter = ({
-  isPrivate,
-  togglePrivate,
+  isPrivate: controlledIsPrivate,
+  defaultIsPrivate = false,
+  onIsPrivateChange,
 }: AgentsVisibilityFilterProps) => {
+  const [uncontrolledIsPrivate, setUncontrolledIsPrivate] = useState(defaultIsPrivate);
+  const isPrivate = controlledIsPrivate ?? uncontrolledIsPrivate;
+  const setIsPrivate = (next: boolean) => {
+    if (controlledIsPrivate === undefined) setUncontrolledIsPrivate(next);
+    onIsPrivateChange?.(next);
+  };
+
   return (
     <div
       role="group"
       aria-label="Agent visibility filter"
       className="inline-flex h-9 items-center justify-center rounded-lg bg-muted p-1 text-muted-foreground"
     >
       <button
         type="button"
         aria-pressed={!isPrivate}
-        onClick={() => {
-          if (isPrivate) togglePrivate();
-        }}
+        onClick={() => setIsPrivate(false)}
         className={cn(
           "inline-flex items-center justify-center whitespace-nowrap rounded-md px-3 py-1 text-sm font-medium transition-all cursor-pointer",
           !isPrivate
             ? "bg-background text-foreground shadow"
             : "text-muted-foreground hover:text-foreground"
         )}
       >
         Public
       </button>
       <button
         type="button"
         aria-pressed={isPrivate}
-        onClick={() => {
-          if (!isPrivate) togglePrivate();
-        }}
+        onClick={() => setIsPrivate(true)}
         className={cn(
           "inline-flex items-center justify-center whitespace-nowrap rounded-md px-3 py-1 text-sm font-medium transition-all cursor-pointer",
           isPrivate
             ? "bg-background text-foreground shadow"
             : "text-muted-foreground hover:text-foreground"
         )}
       >
         Private
       </button>
     </div>
   );
 };

As per coding guidelines: "Support both controlled and uncontrolled state in components" and "Provide defaultValue and onValueChange props for state management."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/Agents/AgentsVisibilityFilter.tsx` around lines 3 - 11, Change the
component API from isPrivate + togglePrivate to a value/defaultValue +
onValueChange(next: boolean) shape: update the AgentsVisibilityFilterProps to
accept value?: boolean, defaultValue?: boolean, and onValueChange?: (next:
boolean) => void, then inside AgentsVisibilityFilter use the controlled pattern
(use the passed value when defined otherwise keep internal state initialized
from defaultValue) and call onValueChange(next) whenever the visibility changes;
replace any togglePrivate usages/click handlers to compute the next boolean and
call onValueChange(next) (or update internal state when uncontrolled).
Reference: AgentsVisibilityFilter and AgentsVisibilityFilterProps (rename
togglePrivate → onValueChange, isPrivate → value/defaultValue) and ensure event
handlers use the new onValueChange(next) contract.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@components/Agents/AgentsVisibilityFilter.tsx`:
- Around line 3-11: Change the component API from isPrivate + togglePrivate to a
value/defaultValue + onValueChange(next: boolean) shape: update the
AgentsVisibilityFilterProps to accept value?: boolean, defaultValue?: boolean,
and onValueChange?: (next: boolean) => void, then inside AgentsVisibilityFilter
use the controlled pattern (use the passed value when defined otherwise keep
internal state initialized from defaultValue) and call onValueChange(next)
whenever the visibility changes; replace any togglePrivate usages/click handlers
to compute the next boolean and call onValueChange(next) (or update internal
state when uncontrolled). Reference: AgentsVisibilityFilter and
AgentsVisibilityFilterProps (rename togglePrivate → onValueChange, isPrivate →
value/defaultValue) and ensure event handlers use the new onValueChange(next)
contract.

In `@components/Agents/PrivacySection.tsx`:
- Around line 26-33: The JSX for EmailShareInput is misaligned with extra
leading spaces; open the component block containing EmailShareInput and
normalize indentation so the tag and all its props (EmailShareInput, emails prop
using form.watch("shareEmails"), existingSharedEmails, onEmailsChange handler
that calls form.setValue(...), and onExistingEmailsChange) align with the
surrounding JSX structure—ensure each prop is indented consistently (same level
as neighboring components) for readable, consistent formatting.

In `@hooks/useAgentForm.ts`:
- Around line 24-30: The else-if branch in the useEffect is unreachable because
defaultValues.shareEmails is always initialized (via initialValues?.shareEmails
?? []), so simplify the effect: keep the check on isPrivate and call
form.setValue("shareEmails", []) when !isPrivate, and remove the redundant
else-if that checks form.getValues("shareEmails"); update the useEffect that
references isPrivate, form.setValue, and form.getValues accordingly.

In `@hooks/useEditAgentTemplate.ts`:
- Around line 22-24: The merged email array assigned to finalShareEmails in
useEditAgentTemplate.ts can contain duplicates from currentSharedEmails and
values.shareEmails; update the assignment so when values.isPrivate is true you
deduplicate the combined list (e.g., use a Set or filter to preserve
first-occurrence order) before sending to the API, while keeping the existing
isPrivate branch behavior (and ensure the deduplication happens only when
computing finalShareEmails).
- Around line 29-31: The request body may include an undefined userId when
userData hasn't loaded; update the mutation in useEditAgentTemplate (the code
that builds body: JSON.stringify({ id: agent.id, userId: userData?.id, ... }))
to guard early: if userData?.id is falsy, abort the request and surface a clear
error (or return early) so no PATCH is sent with undefined userId; alternatively
ensure the submit action is disabled until userData is present—change the check
in the mutation handler that constructs the body to validate userData?.id and
handle the missing user id before calling fetch.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 917c403c-2831-47b4-9378-5e49ea309f67

📥 Commits

Reviewing files that changed from the base of the PR and between a04a94f and 2a2d670.

📒 Files selected for processing (10)
  • components/Agents/AgentEditDialog.tsx
  • components/Agents/AgentVisibilityControl.tsx
  • components/Agents/Agents.tsx
  • components/Agents/AgentsVisibilityFilter.tsx
  • components/Agents/CreateAgentDialog.tsx
  • components/Agents/EmailShareInput.tsx
  • components/Agents/PrivacySection.tsx
  • components/Agents/TagSelector.tsx
  • hooks/useAgentForm.ts
  • hooks/useEditAgentTemplate.ts
✅ Files skipped from review due to trivial changes (4)
  • components/Agents/EmailShareInput.tsx
  • components/Agents/TagSelector.tsx
  • components/Agents/Agents.tsx
  • components/Agents/AgentVisibilityControl.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • components/Agents/CreateAgentDialog.tsx
  • components/Agents/AgentEditDialog.tsx

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.

2 participants