[WEB-3759] chore: header revamp for cycles, modules, pages and views (#6875)

* chore: header revamp for cycles, modules, pages and views

* chore: moved list fetch to layout level
This commit is contained in:
Vamsi Krishna 2025-04-09 14:56:57 +05:30 committed by GitHub
parent 2b411de1e3
commit 993c7899b6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 269 additions and 453 deletions

View file

@ -4,18 +4,7 @@ import React, { useEffect, useState } from "react";
import { observer } from "mobx-react";
import { useParams } from "next/navigation";
import { Controller, useForm } from "react-hook-form";
import {
ArchiveRestoreIcon,
CalendarClock,
ChevronDown,
ChevronRight,
Info,
LinkIcon,
Plus,
SquareUser,
Trash2,
Users,
} from "lucide-react";
import { CalendarClock, ChevronDown, ChevronRight, Info, Plus, SquareUser, Users } from "lucide-react";
import { Disclosure, Transition } from "@headlessui/react";
// plane types
import {
@ -30,18 +19,7 @@ import {
import { useTranslation } from "@plane/i18n";
import { ILinkDetails, IModule, ModuleLink } from "@plane/types";
// plane ui
import {
CustomMenu,
Loader,
LayersIcon,
CustomSelect,
ModuleStatusIcon,
TOAST_TYPE,
setToast,
ArchiveIcon,
TextArea,
} from "@plane/ui";
import { copyUrlToClipboard } from "@plane/utils";
import { Loader, LayersIcon, CustomSelect, ModuleStatusIcon, TOAST_TYPE, setToast, TextArea } from "@plane/ui";
// components
import { DateRangeDropdown, MemberDropdown } from "@/components/dropdowns";
import {
@ -55,7 +33,6 @@ import {
import { getDate, renderFormattedPayloadDate } from "@/helpers/date-time.helper";
// hooks
import { useModule, useEventTracker, useProjectEstimates, useUserPermissions } from "@/hooks/store";
import { useAppRouter } from "@/hooks/use-app-router";
// plane web constants
import { EEstimateSystem } from "@/plane-web/constants/estimates";
@ -82,23 +59,18 @@ export const ModuleAnalyticsSidebar: React.FC<Props> = observer((props) => {
const [moduleLinkModal, setModuleLinkModal] = useState(false);
const [selectedLinkToUpdate, setSelectedLinkToUpdate] = useState<ILinkDetails | null>(null);
// router
const router = useAppRouter();
const { workspaceSlug, projectId } = useParams();
// store hooks
const { t } = useTranslation();
const { allowPermissions } = useUserPermissions();
const { getModuleById, updateModuleDetails, createModuleLink, updateModuleLink, deleteModuleLink, restoreModule } =
useModule();
const { setTrackElement, captureModuleEvent, captureEvent } = useEventTracker();
const { getModuleById, updateModuleDetails, createModuleLink, updateModuleLink, deleteModuleLink } = useModule();
const { captureModuleEvent, captureEvent } = useEventTracker();
const { areEstimateEnabledByProjectId, currentActiveEstimateId, estimateById } = useProjectEstimates();
// derived values
const moduleDetails = getModuleById(moduleId);
const moduleState = moduleDetails?.status?.toLocaleLowerCase();
const isInArchivableGroup = !!moduleState && ["completed", "cancelled"].includes(moduleState);
const areEstimateEnabled = projectId && areEstimateEnabledByProjectId(projectId.toString());
const estimateType = areEstimateEnabled && currentActiveEstimateId && estimateById(currentActiveEstimateId);
const isEstimatePointValid = estimateType && estimateType?.type == EEstimateSystem.POINTS ? true : false;
@ -175,24 +147,6 @@ export const ModuleAnalyticsSidebar: React.FC<Props> = observer((props) => {
});
};
const handleCopyText = () => {
copyUrlToClipboard(`${workspaceSlug}/projects/${projectId}/modules/${moduleId}`)
.then(() => {
setToast({
type: TOAST_TYPE.SUCCESS,
title: "Link copied",
message: "Module link copied to clipboard",
});
})
.catch(() => {
setToast({
type: TOAST_TYPE.ERROR,
title: "Error!",
message: "Some error occurred",
});
});
};
const handleDateChange = async (startDate: Date | undefined, targetDate: Date | undefined) => {
submitChanges({
start_date: startDate ? renderFormattedPayloadDate(startDate) : null,
@ -205,30 +159,6 @@ export const ModuleAnalyticsSidebar: React.FC<Props> = observer((props) => {
});
};
const handleRestoreModule = async (e: React.MouseEvent<HTMLButtonElement>) => {
e.preventDefault();
e.stopPropagation();
if (!workspaceSlug || !projectId || !moduleId) return;
await restoreModule(workspaceSlug.toString(), projectId.toString(), moduleId)
.then(() => {
setToast({
type: TOAST_TYPE.SUCCESS,
title: "Restore success",
message: "Your module can be found in project modules.",
});
router.push(`/${workspaceSlug}/projects/${projectId}/archives/modules`);
})
.catch(() =>
setToast({
type: TOAST_TYPE.ERROR,
title: "Error!",
message: "Module could not be restored. Please try again.",
})
);
};
useEffect(() => {
if (moduleDetails)
reset({
@ -309,56 +239,6 @@ export const ModuleAnalyticsSidebar: React.FC<Props> = observer((props) => {
<ChevronRight className="h-3 w-3 stroke-2 text-white" />
</button>
</div>
<div className="flex items-center gap-3.5">
{!isArchived && (
<button onClick={handleCopyText}>
<LinkIcon className="h-3 w-3 text-custom-text-300" />
</button>
)}
{isEditingAllowed && (
<CustomMenu placement="bottom-end" ellipsis>
{!isArchived && (
<CustomMenu.MenuItem onClick={() => setArchiveModuleModal(true)} disabled={!isInArchivableGroup}>
{isInArchivableGroup ? (
<div className="flex items-center gap-2">
<ArchiveIcon className="h-3 w-3" />
{t("project_module.archive_module")}
</div>
) : (
<div className="flex items-start gap-2">
<ArchiveIcon className="h-3 w-3" />
<div className="-mt-1">
<p>Archive module</p>
<p className="text-xs text-custom-text-400">
{t("project_module.quick_actions.archive_module_description")}
</p>
</div>
</div>
)}
</CustomMenu.MenuItem>
)}
{isArchived && (
<CustomMenu.MenuItem onClick={handleRestoreModule}>
<span className="flex items-center justify-start gap-2">
<ArchiveRestoreIcon className="h-3 w-3" />
<span>{t("project_module.restore_module")}</span>
</span>
</CustomMenu.MenuItem>
)}
<CustomMenu.MenuItem
onClick={() => {
setTrackElement("Module peek-overview");
setModuleDeleteModal(true);
}}
>
<span className="flex items-center justify-start gap-2">
<Trash2 className="h-3 w-3" />
<span>{t("project_module.delete_module")}</span>
</span>
</CustomMenu.MenuItem>
</CustomMenu>
)}
</div>
</div>
<div className="flex flex-col gap-3">

View file

@ -24,10 +24,11 @@ type Props = {
moduleId: string;
projectId: string;
workspaceSlug: string;
customClassName?: string;
};
export const ModuleQuickActions: React.FC<Props> = observer((props) => {
const { parentRef, moduleId, projectId, workspaceSlug } = props;
const { parentRef, moduleId, projectId, workspaceSlug, customClassName } = props;
// router
const router = useAppRouter();
// states
@ -167,7 +168,7 @@ export const ModuleQuickActions: React.FC<Props> = observer((props) => {
</div>
)}
<ContextMenu parentRef={parentRef} items={MENU_ITEMS} />
<CustomMenu ellipsis placement="bottom-end" closeOnSelect>
<CustomMenu ellipsis placement="bottom-end" closeOnSelect buttonClassName={customClassName}>
{MENU_ITEMS.map((item) => {
if (item.shouldRender === false) return null;
return (