"use client"; import { useEffect, useMemo, useState } from "react"; import { Button } from "@kandev/ui/button"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "@kandev/ui/dialog"; import { Input } from "@kandev/ui/input"; import { Label } from "@kandev/ui/label"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@kandev/ui/select"; import { useAppStore } from "@/components/state-provider"; import { useToast } from "@/components/toast-provider"; import { createTaskMR } from "@/lib/api/domains/gitlab-api"; import type { Repository } from "@/lib/types/http"; import { useTranslation } from "react-i18next"; const NO_REPOSITORY = "__none__"; type TaskRepositoryLink = { repository_id: string }; type RepositoryOption = { id: string; label: string }; function useRepositoryOptions( repositories: Repository[], taskRepositories: TaskRepositoryLink[], ): RepositoryOption[] { return useMemo(() => { const repositoryByID = new Map( repositories.map((repository) => [repository.id, repository]), ); return taskRepositories.map((link) => { const repository = repositoryByID.get(link.repository_id); const providerLabel = repository?.provider_owner && repository.provider_name ? `${repository.provider_owner}/${repository.provider_name}` : repository?.name; return { id: link.repository_id, label: providerLabel || link.repository_id }; }); }, [repositories, taskRepositories]); } function RepositoryField({ options, value, onChange, }: { options: RepositoryOption[]; value: string; onChange: (value: string) => void; }) { const { t } = useTranslation(); if (options.length <= 1) return null; return (
); } function LinkDialogFields({ mrURL, onMRURLChange, options, repositoryID, onRepositoryChange, }: { mrURL: string; onMRURLChange: (value: string) => void; options: RepositoryOption[]; repositoryID: string; onRepositoryChange: (value: string) => void; }) { const { t } = useTranslation(); return (
onMRURLChange(event.target.value)} placeholder="https://gitlab.example.com/group/project/-/merge_requests/42" autoComplete="off" />
); } export function TaskMRLinkDialog({ open, onOpenChange, taskId, workspaceId, taskRepositories, repositories, }: { open: boolean; onOpenChange: (open: boolean) => void; taskId: string; workspaceId: string; taskRepositories: TaskRepositoryLink[]; repositories: Repository[]; }) { const { t } = useTranslation(); const options = useRepositoryOptions(repositories, taskRepositories); const defaultRepositoryID = options[0]?.id ?? NO_REPOSITORY; const [mrURL, setMRURL] = useState(""); const [repositoryID, setRepositoryID] = useState(defaultRepositoryID); const [submitting, setSubmitting] = useState(false); const setTaskMR = useAppStore((state) => state.setTaskMR); const { toast } = useToast(); useEffect(() => { if (repositoryID === NO_REPOSITORY && defaultRepositoryID !== NO_REPOSITORY) { setRepositoryID(defaultRepositoryID); } }, [defaultRepositoryID, repositoryID]); useEffect(() => { if (open) return; setMRURL(""); setRepositoryID(defaultRepositoryID); }, [defaultRepositoryID, open]); const submit = async () => { if (!mrURL.trim() || submitting) return; setSubmitting(true); try { const association = await createTaskMR( { task_id: taskId, repository_id: repositoryID === NO_REPOSITORY ? undefined : repositoryID, mr_url: mrURL.trim(), }, workspaceId, ); setTaskMR(workspaceId, taskId, association); onOpenChange(false); } catch (error) { toast({ title: t("gitlab:failedToLinkMergeRequest"), description: error instanceof Error ? error.message : t("gitlab:gitlabRejectedTheMergeRequestLink"), variant: "error", }); } finally { setSubmitting(false); } }; return ( {t("gitlab:linkGitlabMergeRequest")} {t("gitlab:pasteAMergeRequestUrlFrom")} ); }