"use client"; import { ArrowLeft } from "lucide-react"; import { TagInput, type Tag as TagType } from "emblor"; import { useAtom } from "jotai"; import { Loader2 } from "lucide-react"; import { type FC, useState } from "react"; import { useRouter } from "next/navigation"; import { useTranslations } from "next-intl"; import { toast } from "sonner"; import { createDocumentMutationAtom } from "@/atoms/documents/document-mutation.atoms"; import { Button } from "@/components/ui/button"; import { Label } from "@/components/ui/label"; import { EnumConnectorName } from "@/contracts/enums/connector"; import { getConnectorIcon } from "@/contracts/enums/connectorIcons"; const youtubeRegex = /^(https:\/\/)?(www\.)?(youtube\.com\/watch\?v=|youtu\.be\/)([a-zA-Z0-9_-]{11})$/; interface YouTubeCrawlerViewProps { searchSpaceId: string; onBack: () => void; } export const YouTubeCrawlerView: FC = ({ searchSpaceId, onBack }) => { const t = useTranslations("add_youtube"); const router = useRouter(); const [videoTags, setVideoTags] = useState([]); const [activeTagIndex, setActiveTagIndex] = useState(null); const [error, setError] = useState(null); // Use the createDocumentMutationAtom const [createDocumentMutation] = useAtom(createDocumentMutationAtom); const { mutate: createYouTubeDocument, isPending: isSubmitting } = createDocumentMutation; const isValidYoutubeUrl = (url: string): boolean => { return youtubeRegex.test(url); }; const extractVideoId = (url: string): string | null => { const match = url.match(/(?:youtube\.com\/watch\?v=|youtu\.be\/)([a-zA-Z0-9_-]{11})/); return match ? match[1] : null; }; const handleSubmit = async () => { if (videoTags.length === 0) { setError(t("error_no_video")); return; } const invalidUrls = videoTags.filter((tag) => !isValidYoutubeUrl(tag.text)); if (invalidUrls.length > 0) { setError(t("error_invalid_urls", { urls: invalidUrls.map((tag) => tag.text).join(", ") })); return; } setError(null); toast(t("processing_toast"), { description: t("processing_toast_desc"), }); const videoUrls = videoTags.map((tag) => tag.text); // Use the mutation to create YouTube documents createYouTubeDocument( { document_type: "YOUTUBE_VIDEO", content: videoUrls, search_space_id: parseInt(searchSpaceId, 10), }, { onSuccess: () => { toast(t("success_toast"), { description: t("success_toast_desc"), }); // Close the popup and navigate to documents onBack(); router.push(`/dashboard/${searchSpaceId}/documents`); }, onError: (error: unknown) => { const errorMessage = error instanceof Error ? error.message : t("error_generic"); setError(errorMessage); toast(t("error_toast"), { description: `${t("error_toast_desc")}: ${errorMessage}`, }); }, } ); }; const handleAddTag = (text: string) => { if (!isValidYoutubeUrl(text)) { toast(t("invalid_url_toast"), { description: t("invalid_url_toast_desc"), }); return; } if (videoTags.some((tag) => tag.text === text)) { toast(t("duplicate_url_toast"), { description: t("duplicate_url_toast_desc"), }); return; } const newTag: TagType = { id: Date.now().toString(), text: text, }; setVideoTags([...videoTags, newTag]); }; return (
{/* Header */}
{getConnectorIcon(EnumConnectorName.YOUTUBE_CONNECTOR, "h-7 w-7")}

{t("title")}

{t("subtitle")}

{/* Form Content - Scrollable */}

{t("hint")}

{error &&
{error}
}

{t("tips_title")}

  • {t("tip_1")}
  • {t("tip_2")}
  • {t("tip_3")}
  • {t("tip_4")}
{videoTags.length > 0 && (

{t("preview")}:

{videoTags.map((tag, _index) => { const videoId = extractVideoId(tag.text); return videoId ? (