refactor: divided modules into multiple components

This commit is contained in:
dakshesh14 2023-10-04 19:24:24 +05:30
parent 547a265169
commit b49b1bf8fa
4 changed files with 168 additions and 120 deletions

View File

@ -5,4 +5,5 @@ export * from "./form";
export * from "./gantt-chart"; export * from "./gantt-chart";
export * from "./modal"; export * from "./modal";
export * from "./sidebar"; export * from "./sidebar";
export * from "./single-module-card"; export * from "./module-detail-card";
export * from "./module-list";

View File

@ -12,16 +12,10 @@ import useToast from "hooks/use-toast";
// components // components
import { DeleteModuleModal } from "components/modules"; import { DeleteModuleModal } from "components/modules";
// ui // ui
import { AssigneesList, Avatar, CustomMenu, Tooltip } from "components/ui"; import { AssigneesList, CustomMenu, Tooltip } from "components/ui";
// icons // icons
import { import { CalendarDaysIcon, LinkIcon, PencilIcon, StarIcon, TrashIcon } from "@heroicons/react/24/outline";
CalendarDaysIcon, import { TargetIcon } from "components/icons";
LinkIcon,
PencilIcon,
StarIcon,
TrashIcon,
} from "@heroicons/react/24/outline";
import { CalendarMonthIcon, TargetIcon } from "components/icons";
// helpers // helpers
import { copyTextToClipboard, truncateText } from "helpers/string.helper"; import { copyTextToClipboard, truncateText } from "helpers/string.helper";
@ -37,7 +31,7 @@ type Props = {
user: ICurrentUserResponse | undefined; user: ICurrentUserResponse | undefined;
}; };
export const SingleModuleCard: React.FC<Props> = ({ module, handleEditModule, user }) => { export const ModuleDetailCard: React.FC<Props> = ({ module, handleEditModule, user }) => {
const [moduleDeleteModal, setModuleDeleteModal] = useState(false); const [moduleDeleteModal, setModuleDeleteModal] = useState(false);
const router = useRouter(); const router = useRouter();
@ -45,8 +39,7 @@ export const SingleModuleCard: React.FC<Props> = ({ module, handleEditModule, us
const { setToastAlert } = useToast(); const { setToastAlert } = useToast();
const completionPercentage = const completionPercentage = ((module.completed_issues + module.cancelled_issues) / module.total_issues) * 100;
((module.completed_issues + module.cancelled_issues) / module.total_issues) * 100;
const handleDeleteModule = () => { const handleDeleteModule = () => {
if (!module) return; if (!module) return;
@ -93,9 +86,7 @@ export const SingleModuleCard: React.FC<Props> = ({ module, handleEditModule, us
false false
); );
modulesService modulesService.removeModuleFromFavorites(workspaceSlug as string, projectId as string, module.id).catch(() => {
.removeModuleFromFavorites(workspaceSlug as string, projectId as string, module.id)
.catch(() => {
setToastAlert({ setToastAlert({
type: "error", type: "error",
title: "Error!", title: "Error!",
@ -105,12 +96,9 @@ export const SingleModuleCard: React.FC<Props> = ({ module, handleEditModule, us
}; };
const handleCopyText = () => { const handleCopyText = () => {
const originURL = const originURL = typeof window !== "undefined" && window.location.origin ? window.location.origin : "";
typeof window !== "undefined" && window.location.origin ? window.location.origin : "";
copyTextToClipboard( copyTextToClipboard(`${originURL}/${workspaceSlug}/projects/${projectId}/modules/${module.id}`).then(() => {
`${originURL}/${workspaceSlug}/projects/${projectId}/modules/${module.id}`
).then(() => {
setToastAlert({ setToastAlert({
type: "success", type: "success",
title: "Link Copied!", title: "Link Copied!",
@ -125,12 +113,7 @@ export const SingleModuleCard: React.FC<Props> = ({ module, handleEditModule, us
return ( return (
<> <>
<DeleteModuleModal <DeleteModuleModal isOpen={moduleDeleteModal} setIsOpen={setModuleDeleteModal} data={module} user={user} />
isOpen={moduleDeleteModal}
setIsOpen={setModuleDeleteModal}
data={module}
user={user}
/>
<div className="flex flex-col divide-y divide-custom-border-200 overflow-hidden rounded-[10px] border border-custom-border-200 bg-custom-background-100 text-xs"> <div className="flex flex-col divide-y divide-custom-border-200 overflow-hidden rounded-[10px] border border-custom-border-200 bg-custom-background-100 text-xs">
<div className="p-4"> <div className="p-4">
<div className="flex w-full flex-col gap-5"> <div className="flex w-full flex-col gap-5">

View File

@ -0,0 +1,131 @@
import React, { useEffect, useState } from "react";
import { useRouter } from "next/router";
import useSWR from "swr";
// hooks
import useUserAuth from "hooks/use-user-auth";
// services
import modulesService from "services/modules.service";
// components
import { CreateUpdateModuleModal, ModulesListGanttChartView, ModuleDetailCard } from "components/modules";
// ui
import { EmptyState, Loader } from "components/ui";
// icons
import { PlusIcon } from "@heroicons/react/24/outline";
// images
import emptyModule from "public/empty-state/module.svg";
// types
import type { IModule, SelectModuleType } from "types/modules";
// fetch-keys
import { MODULE_LIST } from "constants/fetch-keys";
type Props = {
modulesView: string;
modules?: IModule[] | null;
};
export const ModuleList: React.FC<Props> = (props) => {
const { modulesView, modules } = props;
// router
const router = useRouter();
const { workspaceSlug, projectId } = router.query;
// states
const [createUpdateModule, setCreateUpdateModule] = useState(false);
const [selectedModule, setSelectedModule] = useState<SelectModuleType>();
// hooks
const { user } = useUserAuth();
// TODO: remove this
const { mutate: mutateModules } = useSWR(
workspaceSlug && projectId ? MODULE_LIST(projectId as string) : null,
workspaceSlug && projectId ? () => modulesService.getModules(workspaceSlug as string, projectId as string) : null
);
const handleEditModule = (module: IModule) => {
setSelectedModule({ ...module, actionType: "edit" });
setCreateUpdateModule(true);
};
/**
* Close the create/update module
* modal and reset the selected module
*/
useEffect(() => {
if (createUpdateModule) return;
const timer = setTimeout(() => {
setSelectedModule(undefined);
clearTimeout(timer);
}, 500);
}, [createUpdateModule]);
return (
<>
<CreateUpdateModuleModal
isOpen={createUpdateModule}
setIsOpen={setCreateUpdateModule}
data={selectedModule}
user={user}
/>
{/* modules are loaded & there number is more than 0 */}
{modules && modules.length > 0 && (
<>
{modulesView === "grid" && (
<div className="h-full overflow-y-auto p-8">
<div className="grid grid-cols-1 gap-9 lg:grid-cols-2 xl:grid-cols-3">
{modules.map((moduleDetail) => (
<ModuleDetailCard
key={moduleDetail.id}
module={moduleDetail}
handleEditModule={() => handleEditModule(moduleDetail)}
user={user}
/>
))}
</div>
</div>
)}
{modulesView === "gantt_chart" && (
<ModulesListGanttChartView modules={modules} mutateModules={mutateModules} />
)}
</>
)}
{/* modules are still loading */}
{!modules && (
<Loader className="grid grid-cols-3 gap-4 p-8">
<Loader.Item height="182px" />
<Loader.Item height="182px" />
<Loader.Item height="182px" />
<Loader.Item height="182px" />
<Loader.Item height="182px" />
<Loader.Item height="182px" />
</Loader>
)}
{/* modules are loaded & there number is 0 */}
{modules && modules.length === 0 && (
<EmptyState
title="Manage your project with modules"
description="Modules are smaller, focused projects that help you group and organize issues."
image={emptyModule}
primaryButton={{
icon: <PlusIcon className="h-4 w-4" />,
text: "New Module",
onClick: () => {
const e = new KeyboardEvent("keydown", {
key: "m",
});
document.dispatchEvent(e);
},
}}
/>
)}
</>
);
};

View File

@ -1,4 +1,4 @@
import React, { useEffect, useState } from "react"; import React, { useState } from "react";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
@ -6,29 +6,26 @@ import useSWR from "swr";
// layouts // layouts
import { ProjectAuthorizationWrapper } from "layouts/auth-layout-legacy"; import { ProjectAuthorizationWrapper } from "layouts/auth-layout-legacy";
// hooks
import useUserAuth from "hooks/use-user-auth";
// services // services
import projectService from "services/project.service"; import projectService from "services/project.service";
import modulesService from "services/modules.service"; // store
import { observer } from "mobx-react-lite";
import { useMobxStore } from "lib/mobx/store-provider";
// components // components
import { CreateUpdateModuleModal, ModulesListGanttChartView, SingleModuleCard } from "components/modules"; import { ModuleList } from "components/modules";
// ui // ui
import { EmptyState, Icon, Loader, PrimaryButton, Tooltip } from "components/ui"; import { Icon, PrimaryButton, Tooltip } from "components/ui";
import { BreadcrumbItem, Breadcrumbs } from "components/breadcrumbs"; import { BreadcrumbItem, Breadcrumbs } from "components/breadcrumbs";
// icons // icons
import { PlusIcon } from "@heroicons/react/24/outline"; import { PlusIcon } from "@heroicons/react/24/outline";
// images
import emptyModule from "public/empty-state/module.svg";
// types // types
import { IModule, SelectModuleType } from "types/modules";
import type { NextPage } from "next"; import type { NextPage } from "next";
// fetch-keys // fetch-keys
import { MODULE_LIST, PROJECT_DETAILS } from "constants/fetch-keys"; import { MODULE_LIST, PROJECT_DETAILS } from "constants/fetch-keys";
// helper // helper
import { replaceUnderscoreIfSnakeCase, truncateText } from "helpers/string.helper"; import { replaceUnderscoreIfSnakeCase, truncateText } from "helpers/string.helper";
const moduleViewOptions: { type: "grid" | "gantt_chart"; icon: any }[] = [ const moduleViewOptions = [
{ {
type: "gantt_chart", type: "gantt_chart",
icon: "view_timeline", icon: "view_timeline",
@ -37,42 +34,31 @@ const moduleViewOptions: { type: "grid" | "gantt_chart"; icon: any }[] = [
type: "grid", type: "grid",
icon: "table_rows", icon: "table_rows",
}, },
]; ] as const;
const ProjectModules: NextPage = () => { const ProjectModules: NextPage = () => {
const [selectedModule, setSelectedModule] = useState<SelectModuleType>(); // router
const [createUpdateModule, setCreateUpdateModule] = useState(false);
const [modulesView, setModulesView] = useState<"grid" | "gantt_chart">("grid");
const router = useRouter(); const router = useRouter();
const { workspaceSlug, projectId } = router.query; const { workspaceSlug, projectId } = router.query;
const { user } = useUserAuth(); // store
const { module: moduleStore } = useMobxStore();
// states
const [modulesView, setModulesView] = useState<"grid" | "gantt_chart">("grid");
const { data: activeProject } = useSWR( const { data: activeProject } = useSWR(
workspaceSlug && projectId ? PROJECT_DETAILS(projectId as string) : null, workspaceSlug && projectId ? PROJECT_DETAILS(projectId as string) : null,
workspaceSlug && projectId ? () => projectService.getProject(workspaceSlug as string, projectId as string) : null workspaceSlug && projectId ? () => projectService.getProject(workspaceSlug as string, projectId as string) : null
); );
const { data: modules, mutate: mutateModules } = useSWR( // TODO: remove + "tesings"
workspaceSlug && projectId ? MODULE_LIST(projectId as string) : null, useSWR(
workspaceSlug && projectId ? () => modulesService.getModules(workspaceSlug as string, projectId as string) : null workspaceSlug && projectId ? MODULE_LIST(projectId.toString()) + "tesings" : null,
workspaceSlug && projectId ? () => moduleStore.fetchModules(workspaceSlug.toString(), projectId.toString()) : null
); );
const handleEditModule = (module: IModule) => { const modules = moduleStore.modules[projectId?.toString()!];
setSelectedModule({ ...module, actionType: "edit" });
setCreateUpdateModule(true);
};
useEffect(() => {
if (createUpdateModule) return;
const timer = setTimeout(() => {
setSelectedModule(undefined);
clearTimeout(timer);
}, 500);
}, [createUpdateModule]);
return ( return (
<ProjectAuthorizationWrapper <ProjectAuthorizationWrapper
@ -114,62 +100,9 @@ const ProjectModules: NextPage = () => {
</div> </div>
} }
> >
<CreateUpdateModuleModal <ModuleList modulesView={modulesView} modules={modules} />
isOpen={createUpdateModule}
setIsOpen={setCreateUpdateModule}
data={selectedModule}
user={user}
/>
{modules ? (
modules.length > 0 ? (
<>
{modulesView === "grid" && (
<div className="h-full overflow-y-auto p-8">
<div className="grid grid-cols-1 gap-9 lg:grid-cols-2 xl:grid-cols-3">
{modules.map((module) => (
<SingleModuleCard
key={module.id}
module={module}
handleEditModule={() => handleEditModule(module)}
user={user}
/>
))}
</div>
</div>
)}
{modulesView === "gantt_chart" && (
<ModulesListGanttChartView modules={modules} mutateModules={mutateModules} />
)}
</>
) : (
<EmptyState
title="Manage your project with modules"
description="Modules are smaller, focused projects that help you group and organize issues."
image={emptyModule}
primaryButton={{
icon: <PlusIcon className="h-4 w-4" />,
text: "New Module",
onClick: () => {
const e = new KeyboardEvent("keydown", {
key: "m",
});
document.dispatchEvent(e);
},
}}
/>
)
) : (
<Loader className="grid grid-cols-3 gap-4 p-8">
<Loader.Item height="100px" />
<Loader.Item height="100px" />
<Loader.Item height="100px" />
<Loader.Item height="100px" />
<Loader.Item height="100px" />
<Loader.Item height="100px" />
</Loader>
)}
</ProjectAuthorizationWrapper> </ProjectAuthorizationWrapper>
); );
}; };
export default ProjectModules; export default observer(ProjectModules);