"use client"; import React, { SyntheticEvent, useRef } from "react"; import { observer } from "mobx-react"; import Link from "next/link"; import { useParams, usePathname, useSearchParams } from "next/navigation"; import { Info, SquareUser } from "lucide-react"; // plane package imports import { MODULE_STATUS, PROGRESS_STATE_GROUPS_DETAILS, MODULE_FAVORITED, MODULE_UNFAVORITED, EUserPermissions, EUserPermissionsLevel, } from "@plane/constants"; import { IModule } from "@plane/types"; import { Card, FavoriteStar, LayersIcon, LinearProgressIndicator, TOAST_TYPE, Tooltip, setPromiseToast, setToast, } from "@plane/ui"; // components import { DateRangeDropdown } from "@/components/dropdowns"; import { ButtonAvatars } from "@/components/dropdowns/member/avatar"; import { ModuleQuickActions } from "@/components/modules"; import { ModuleStatusDropdown } from "@/components/modules/module-status-dropdown"; // constants // helpers import { getDate, renderFormattedPayloadDate } from "@/helpers/date-time.helper"; import { generateQueryParams } from "@/helpers/router.helper"; // hooks import { useEventTracker, useMember, useModule, useUserPermissions } from "@/hooks/store"; import { useAppRouter } from "@/hooks/use-app-router"; import { usePlatformOS } from "@/hooks/use-platform-os"; // plane web constants type Props = { moduleId: string; }; export const ModuleCardItem: React.FC = observer((props) => { const { moduleId } = props; // refs const parentRef = useRef(null); // router const router = useAppRouter(); const { workspaceSlug, projectId } = useParams(); const searchParams = useSearchParams(); const pathname = usePathname(); // store hooks const { allowPermissions } = useUserPermissions(); const { getModuleById, addModuleToFavorites, removeModuleFromFavorites, updateModuleDetails } = useModule(); const { getUserDetails } = useMember(); const { captureEvent } = useEventTracker(); // derived values const moduleDetails = getModuleById(moduleId); const isEditingAllowed = allowPermissions( [EUserPermissions.ADMIN, EUserPermissions.MEMBER], EUserPermissionsLevel.PROJECT ); const isDisabled = !isEditingAllowed || !!moduleDetails?.archived_at; const renderIcon = Boolean(moduleDetails?.start_date) || Boolean(moduleDetails?.target_date); const { isMobile } = usePlatformOS(); const handleAddToFavorites = (e: React.MouseEvent) => { e.stopPropagation(); e.preventDefault(); if (!workspaceSlug || !projectId) return; const addToFavoritePromise = addModuleToFavorites(workspaceSlug.toString(), projectId.toString(), moduleId).then( () => { captureEvent(MODULE_FAVORITED, { module_id: moduleId, element: "Grid layout", state: "SUCCESS", }); } ); setPromiseToast(addToFavoritePromise, { loading: "Adding module to favorites...", success: { title: "Success!", message: () => "Module added to favorites.", }, error: { title: "Error!", message: () => "Couldn't add the module to favorites. Please try again.", }, }); }; const handleRemoveFromFavorites = (e: React.MouseEvent) => { e.stopPropagation(); e.preventDefault(); if (!workspaceSlug || !projectId) return; const removeFromFavoritePromise = removeModuleFromFavorites( workspaceSlug.toString(), projectId.toString(), moduleId ).then(() => { captureEvent(MODULE_UNFAVORITED, { module_id: moduleId, element: "Grid layout", state: "SUCCESS", }); }); setPromiseToast(removeFromFavoritePromise, { loading: "Removing module from favorites...", success: { title: "Success!", message: () => "Module removed from favorites.", }, error: { title: "Error!", message: () => "Couldn't remove the module from favorites. Please try again.", }, }); }; const handleEventPropagation = (e: SyntheticEvent) => { e.stopPropagation(); e.preventDefault(); }; const handleModuleDetailsChange = async (payload: Partial) => { if (!workspaceSlug || !projectId) return; await updateModuleDetails(workspaceSlug.toString(), projectId.toString(), moduleId, payload) .then(() => { setToast({ type: TOAST_TYPE.SUCCESS, title: "Success!", message: "Module updated successfully.", }); }) .catch((err) => { setToast({ type: TOAST_TYPE.ERROR, title: "Error!", message: err?.detail ?? "Module could not be updated. Please try again.", }); }); }; const openModuleOverview = (e: React.MouseEvent) => { e.stopPropagation(); e.preventDefault(); const query = generateQueryParams(searchParams, ["peekModule"]); if (searchParams.has("peekModule") && searchParams.get("peekModule") === moduleId) { router.push(`${pathname}?${query}`); } else { router.push(`${pathname}?${query && `${query}&`}peekModule=${moduleId}`); } }; if (!moduleDetails) return null; const moduleTotalIssues = moduleDetails.backlog_issues + moduleDetails.unstarted_issues + moduleDetails.started_issues + moduleDetails.completed_issues + moduleDetails.cancelled_issues; const moduleCompletedIssues = moduleDetails.completed_issues; // const areYearsEqual = startDate.getFullYear() === endDate.getFullYear(); const moduleStatus = MODULE_STATUS.find((status) => status.value === moduleDetails.status); const issueCount = module ? !moduleTotalIssues || moduleTotalIssues === 0 ? `0 work items` : moduleTotalIssues === moduleCompletedIssues ? `${moduleTotalIssues} Work item${moduleTotalIssues > 1 ? `s` : ``}` : `${moduleCompletedIssues}/${moduleTotalIssues} Work items` : `0 work items`; const moduleLeadDetails = moduleDetails.lead_id ? getUserDetails(moduleDetails.lead_id) : undefined; const progressIndicatorData = PROGRESS_STATE_GROUPS_DETAILS.map((group, index) => ({ id: index, name: group.title, value: moduleTotalIssues > 0 ? (moduleDetails[group.key as keyof IModule] as number) : 0, color: group.color, })); return (
{moduleDetails.name}
{moduleStatus && ( )}
{issueCount ?? "0 Work item"}
{moduleLeadDetails ? ( ) : ( )}
{ handleModuleDetailsChange({ start_date: val?.from ? renderFormattedPayloadDate(val.from) : null, target_date: val?.to ? renderFormattedPayloadDate(val.to) : null, }); }} placeholder={{ from: "Start date", to: "End date", }} disabled={isDisabled} hideIcon={{ from: renderIcon ?? true, to: renderIcon }} />
{isEditingAllowed && ( { if (moduleDetails.is_favorite) handleRemoveFromFavorites(e); else handleAddToFavorites(e); }} selected={!!moduleDetails.is_favorite} /> )} {workspaceSlug && projectId && ( )}
); });