Repository: aipotheosis-labs/aci
File: frontend/src/components/apps/configure-app/configure-app-step.tsx (TypeScript)

Code before:
import { DialogFooter } from "@/components/ui/dialog";
import { useState, useEffect } from "react";
import { Switch } from "@/components/ui/switch";
import { Label } from "@/components/ui/label";
import { Input } from "@/components/ui/input";
import {
  Tooltip,
  TooltipTrigger,
  TooltipContent,
} from "@/components/ui/tooltip";
import { BsQuestionCircle, BsAsterisk } from "react-icons/bs";
import { Badge } from "@/components/ui/badge";
import { IdDisplay } from "@/components/apps/id-display";
import { Checkbox } from "@/components/ui/checkbox";
import { useCreateAppConfig } from "@/hooks/use-app-config";
import { toast } from "sonner";
import { AppAlreadyConfiguredError } from "@/lib/api/appconfig";

export const ConfigureAppFormSchema = z.object({
  security_scheme: z.string().min(1, "Security Scheme is required"),
  client_id: z.string().optional().default(""),
  client_secret: z.string().optional().default(""),
  redirect_url: z
    .string()
    .optional()
    .default("")
    .refine(
      (value) => {
        if (!value || value.trim() === "") return true; // Empty is valid (optional field)
        try {
          const url = new URL(value.trim());
          return url.protocol === "http:" || url.protocol === "https:";
        } catch {
          return false;
        }
      },
      {
        message: "Please enter a valid URL starting with http:// or https://",
      },
    )
    .refine(
      (value) => {
        if (!value || value.trim() === "") return true;
        return value.trim() !== defaultRedirectUrl;
      },
      // to catch likely user error
      {
        message:
          "Custom redirect URL cannot be the same as ACI.dev's redirect URL",
      },
    ),

Review comment (suggestion):
you can use `.transform((val) => val?.trim() ?? "")` instead of specifying trim() on every access of `redirect_url`

Code after:
import { DialogFooter } from "@/components/ui/dialog";
import { useState, useEffect } from "react";
import { Switch } from "@/components/ui/switch";
import { Label } from "@/components/ui/label";
import { Input } from "@/components/ui/input";
import {
  Tooltip,
  TooltipTrigger,
  TooltipContent,
} from "@/components/ui/tooltip";
import { BsQuestionCircle, BsAsterisk } from "react-icons/bs";
import { Badge } from "@/components/ui/badge";
import { IdDisplay } from "@/components/apps/id-display";
import { Checkbox } from "@/components/ui/checkbox";
import { useCreateAppConfig } from "@/hooks/use-app-config";
import { toast } from "sonner";
import { AppAlreadyConfiguredError } from "@/lib/api/appconfig";

export const ConfigureAppFormSchema = z.object({
  security_scheme: z.string().min(1, "Security Scheme is required"),
  client_id: z.string().optional().default(""),
  client_secret: z.string().optional().default(""),
  redirect_url: z
    .string()
    .optional()
    .default("")
    .transform((value) => value.trim())
    .refine(
      (value) => {
        if (!value) return true; // Empty is valid (optional field)
        try {
          const url = new URL(value);
          return url.protocol === "http:" || url.protocol === "https:";
        } catch {
          return false;
        }
      },
      {
        message: "Please enter a valid URL starting with http:// or https://",
      },
    )
    .refine(
      (value) => {
        if (!value) return true;
        return value !== defaultRedirectUrl;
      },
      // to catch likely user error
      {
        message:
          "Custom redirect URL cannot be the same as ACI.dev's redirect URL",
      },