From 438e6ae2301917b591143b0a22aab4e3281e3e45 Mon Sep 17 00:00:00 2001 From: Syed Ali Abbas Zaidi Date: Wed, 29 Jul 2026 12:54:49 +0500 Subject: [PATCH 1/2] feat: add rich text description to modules --- apps/api/plane/db/models/asset.py | 1 + .../modules/analytics-sidebar/root.tsx | 41 +++++-- .../components/modules/description-editor.tsx | 103 ++++++++++++++++++ apps/web/core/components/modules/form.tsx | 43 ++++---- packages/types/src/enums.ts | 4 +- packages/utils/src/module.ts | 43 +++++++- 6 files changed, 201 insertions(+), 34 deletions(-) create mode 100644 apps/web/core/components/modules/description-editor.tsx diff --git a/apps/api/plane/db/models/asset.py b/apps/api/plane/db/models/asset.py index 55efff7f41d..47c389cdb70 100644 --- a/apps/api/plane/db/models/asset.py +++ b/apps/api/plane/db/models/asset.py @@ -44,6 +44,7 @@ class EntityTypeContext(models.TextChoices): PROJECT_COVER = "PROJECT_COVER" DRAFT_ISSUE_ATTACHMENT = "DRAFT_ISSUE_ATTACHMENT" DRAFT_ISSUE_DESCRIPTION = "DRAFT_ISSUE_DESCRIPTION" + MODULE_DESCRIPTION = "MODULE_DESCRIPTION" attributes = models.JSONField(default=dict) asset = models.FileField(upload_to=get_upload_path, max_length=800) diff --git a/apps/web/core/components/modules/analytics-sidebar/root.tsx b/apps/web/core/components/modules/analytics-sidebar/root.tsx index d5844e6a0eb..268bac5adcc 100644 --- a/apps/web/core/components/modules/analytics-sidebar/root.tsx +++ b/apps/web/core/components/modules/analytics-sidebar/root.tsx @@ -23,14 +23,17 @@ import { ChevronRightIcon, } from "@plane/propel/icons"; import { TOAST_TYPE, setToast } from "@plane/propel/toast"; -import type { ILinkDetails, IModule, ModuleLink } from "@plane/types"; +import type { ILinkDetails, IModule, ModuleLink, TNameDescriptionLoader } from "@plane/types"; +import { EFileAssetType } from "@plane/types"; // plane ui -import { Loader, CustomSelect, TextArea } from "@plane/ui"; +import { Loader, CustomSelect } from "@plane/ui"; // components // helpers -import { getDate, renderFormattedPayloadDate } from "@plane/utils"; +import { getDate, getModuleDescriptionInitialValue, renderFormattedPayloadDate } from "@plane/utils"; import { DateRangeDropdown } from "@/components/dropdowns/date-range"; import { MemberDropdown } from "@/components/dropdowns/member/dropdown"; +import { DescriptionInput } from "@/components/editor/rich-text/description-input"; +import { NameDescriptionUpdateStatus } from "@/components/issues/issue-update-status"; import { CreateUpdateModuleLinkModal, ModuleAnalyticsProgress, ModuleLinksList } from "@/components/modules"; // hooks import { useProjectEstimates } from "@/hooks/store/estimates"; @@ -57,6 +60,7 @@ export const ModuleAnalyticsSidebar = observer(function ModuleAnalyticsSidebar(p // states const [moduleLinkModal, setModuleLinkModal] = useState(false); const [selectedLinkToUpdate, setSelectedLinkToUpdate] = useState(null); + const [isSubmittingDescription, setIsSubmittingDescription] = useState("saved"); // router const { workspaceSlug, projectId } = useParams(); @@ -71,7 +75,7 @@ export const ModuleAnalyticsSidebar = observer(function ModuleAnalyticsSidebar(p const moduleDetails = getModuleById(moduleId); const areEstimateEnabled = projectId && areEstimateEnabledByProjectId(projectId.toString()); const estimateType = areEstimateEnabled && currentActiveEstimateId && estimateById(currentActiveEstimateId); - const isEstimatePointValid = estimateType && estimateType?.type == EEstimateSystem.POINTS ? true : false; + const isEstimatePointValid = !!(estimateType && estimateType?.type == EEstimateSystem.POINTS); const { reset, control } = useForm({ defaultValues, @@ -215,8 +219,8 @@ export const ModuleAnalyticsSidebar = observer(function ModuleAnalyticsSidebar(p } value={value} - onChange={(value: any) => { - submitChanges({ status: value }); + onChange={(selectedStatus: any) => { + submitChanges({ status: selectedStatus }); }} disabled={!isEditingAllowed || isArchived} > @@ -235,14 +239,27 @@ export const ModuleAnalyticsSidebar = observer(function ModuleAnalyticsSidebar(p

{moduleDetails.name}

- {moduleDetails.description && ( -