import { FC, useEffect, useState } from "react"; import { Controller, useForm } from "react-hook-form"; // hooks import { useEventTracker, useProject, useWorkspace } from "hooks/store"; import useToast from "hooks/use-toast"; // components import EmojiIconPicker from "components/emoji-icon-picker"; import { ImagePickerPopover } from "components/core"; import { Button, CustomSelect, Input, TextArea } from "@plane/ui"; // icons import { Lock } from "lucide-react"; // types import { IProject, IWorkspace } from "@plane/types"; // helpers import { renderEmoji } from "helpers/emoji.helper"; import { renderFormattedDate } from "helpers/date-time.helper"; // constants import { NETWORK_CHOICES } from "constants/project"; // services import { ProjectService } from "services/project"; export interface IProjectDetailsForm { project: IProject; workspaceSlug: string; isAdmin: boolean; } const projectService = new ProjectService(); export const ProjectDetailsForm: FC = (props) => { const { project, workspaceSlug, isAdmin } = props; // states const [isLoading, setIsLoading] = useState(false); // store hooks const { captureProjectEvent } = useEventTracker(); const { currentWorkspace } = useWorkspace(); const { updateProject } = useProject(); // toast alert const { setToastAlert } = useToast(); // form info const { handleSubmit, watch, control, setValue, setError, reset, formState: { errors }, } = useForm({ defaultValues: { ...project, emoji_and_icon: project.emoji ?? project.icon_prop, workspace: (project.workspace as IWorkspace).id, }, }); useEffect(() => { if (!project) return; reset({ ...project, emoji_and_icon: project.emoji ?? project.icon_prop, workspace: (project.workspace as IWorkspace).id, }); }, [project, reset]); const handleIdentifierChange = (event: React.ChangeEvent) => { const { value } = event.target; const alphanumericValue = value.replace(/[^a-zA-Z0-9]/g, ""); const formattedValue = alphanumericValue.toUpperCase(); setValue("identifier", formattedValue); }; const handleUpdateChange = async (payload: Partial) => { if (!workspaceSlug || !project) return; return updateProject(workspaceSlug.toString(), project.id, payload) .then((res) => { captureProjectEvent({ eventName: "Project updated", payload: { ...res, state: "SUCCESS", element: "Project general settings" }, group: { isGrouping: true, groupType: "Workspace_metrics", groupId: res.workspace, }, }); setToastAlert({ type: "success", title: "Success!", message: "Project updated successfully", }); }) .catch((error) => { captureProjectEvent({ eventName: "Project updated", payload: { ...payload, state: "FAILED", element: "Project general settings" }, group: { isGrouping: true, groupType: "Workspace_metrics", groupId: currentWorkspace?.id, }, }); setToastAlert({ type: "error", title: "Error!", message: error?.error ?? "Project could not be updated. Please try again.", }); }); }; const onSubmit = async (formData: IProject) => { if (!workspaceSlug) return; setIsLoading(true); const payload: Partial = { name: formData.name, network: formData.network, identifier: formData.identifier, description: formData.description, cover_image: formData.cover_image, }; if (typeof formData.emoji_and_icon === "object") { payload.emoji = null; payload.icon_prop = formData.emoji_and_icon; } else { payload.emoji = formData.emoji_and_icon; payload.icon_prop = null; } if (project.identifier !== formData.identifier) await projectService .checkProjectIdentifierAvailability(workspaceSlug as string, payload.identifier ?? "") .then(async (res) => { if (res.exists) setError("identifier", { message: "Identifier already exists" }); else await handleUpdateChange(payload); }); else await handleUpdateChange(payload); setTimeout(() => { setIsLoading(false); }, 300); }; const currentNetwork = NETWORK_CHOICES.find((n) => n.key === project?.network); const selectedNetwork = NETWORK_CHOICES.find((n) => n.key === watch("network")); return (
{watch("cover_image")!}
( )} />
{watch("name")} {watch("identifier")} . {project.network === 0 && } {currentNetwork?.label}
( )} />

Project Name

( )} />

Description

(