/** * Copyright (c) 2023-present Plane Software, Inc. and contributors * SPDX-License-Identifier: AGPL-3.0-only * See the LICENSE file for details. */ import type { SyntheticEvent } from "react"; import React, { useRef } from "react"; import { observer } from "mobx-react"; import Link from "next/link"; import { useParams, usePathname, useSearchParams } from "next/navigation"; import { InfoOutline, UserAltOutline, WorkItemsOutline } from "@makeplane/propel/icons"; // plane package imports import { MODULE_STATUS, EUserPermissions, EUserPermissionsLevel, IS_FAVORITE_MENU_OPEN } from "@plane/constants"; import { useLocalStorage } from "@plane/hooks"; import { LinearProgress } from "@makeplane/propel/components/linear-progress"; import { TOAST_TYPE, setPromiseToast, setToast } from "@plane/propel/toast"; import { Tooltip } from "@makeplane/propel/components/tooltip"; import type { IModule } from "@plane/types"; import { Card, FavoriteStar } from "@plane/ui"; import { getDate, renderFormattedPayloadDate, generateQueryParams } from "@plane/utils"; // components import { DateRangeDropdown } from "@/components/dropdowns/date-range"; import { ButtonAvatars } from "@/components/dropdowns/member/avatar"; import { ModuleQuickActions } from "@/components/modules"; import { ModuleStatusDropdown } from "@/components/modules/module-status-dropdown"; // hooks import { useMember } from "@/hooks/store/use-member"; import { useModule } from "@/hooks/store/use-module"; import { useUserPermissions } from "@/hooks/store/user"; import { useAppRouter } from "@/hooks/use-app-router"; import { usePlatformOS } from "@/hooks/use-platform-os"; type Props = { moduleId: string; }; export const ModuleCardItem = observer(function ModuleCardItem(props: 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(); // local storage const { setValue: toggleFavoriteMenu, storedValue } = useLocalStorage(IS_FAVORITE_MENU_OPEN, false); // 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( () => { if (!storedValue) toggleFavoriteMenu(true); } ); 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 ); 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 = moduleDetails ? !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 progressValue = moduleTotalIssues > 0 ? (moduleCompletedIssues / moduleTotalIssues) * 100 : 0; 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 && ( )}
); });