Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 16 additions & 61 deletions web/components/cycles/sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,7 @@ import {
import { ExclamationIcon } from "components/icons";
// helpers
import { capitalizeFirstLetter, copyTextToClipboard } from "helpers/string.helper";
import {
isDateGreaterThanToday,
renderDateFormat,
renderShortDateWithYearFormat,
} from "helpers/date-time.helper";
import { isDateGreaterThanToday, renderDateFormat, renderShortDateWithYearFormat } from "helpers/date-time.helper";
// types
import { ICurrentUserResponse, ICycle } from "types";
// fetch-keys
Expand All @@ -50,13 +46,7 @@ type Props = {
user: ICurrentUserResponse | undefined;
};

export const CycleDetailsSidebar: React.FC<Props> = ({
cycle,
isOpen,
cycleStatus,
isCompleted,
user,
}) => {
export const CycleDetailsSidebar: React.FC<Props> = ({ cycle, isOpen, cycleStatus, isCompleted, user }) => {
const [cycleDeleteModal, setCycleDeleteModal] = useState(false);

const router = useRouter();
Expand All @@ -76,11 +66,7 @@ export const CycleDetailsSidebar: React.FC<Props> = ({
const submitChanges = (data: Partial<ICycle>) => {
if (!workspaceSlug || !projectId || !cycleId) return;

mutate<ICycle>(
CYCLE_DETAILS(cycleId as string),
(prevData) => ({ ...(prevData as ICycle), ...data }),
false
);
mutate<ICycle>(CYCLE_DETAILS(cycleId as string), (prevData) => ({ ...(prevData as ICycle), ...data }), false);

cyclesService
.patchCycle(workspaceSlug as string, projectId as string, cycleId as string, data, user)
Expand All @@ -89,8 +75,7 @@ export const CycleDetailsSidebar: React.FC<Props> = ({
};

const handleCopyText = () => {
const originURL =
typeof window !== "undefined" && window.location.origin ? window.location.origin : "";
const originURL = typeof window !== "undefined" && window.location.origin ? window.location.origin : "";

copyTextToClipboard(`${originURL}/${workspaceSlug}/projects/${projectId}/cycles/${cycle?.id}`)
.then(() => {
Expand All @@ -116,11 +101,7 @@ export const CycleDetailsSidebar: React.FC<Props> = ({

const dateChecker = async (payload: any) => {
try {
const res = await cyclesService.cycleDateCheck(
workspaceSlug as string,
projectId as string,
payload
);
const res = await cyclesService.cycleDateCheck(workspaceSlug as string, projectId as string, payload);
return res.status;
} catch (err) {
return false;
Expand Down Expand Up @@ -277,20 +258,13 @@ export const CycleDetailsSidebar: React.FC<Props> = ({
const isStartValid = new Date(`${cycle?.start_date}`) <= new Date();
const isEndValid = new Date(`${cycle?.end_date}`) >= new Date(`${cycle?.start_date}`);

const progressPercentage = cycle
? Math.round((cycle.completed_issues / cycle.total_issues) * 100)
: null;
const progressPercentage = cycle ? Math.round((cycle.completed_issues / cycle.total_issues) * 100) : null;

return (
<>
<DeleteCycleModal
isOpen={cycleDeleteModal}
setIsOpen={setCycleDeleteModal}
data={cycle}
user={user}
/>
<DeleteCycleModal isOpen={cycleDeleteModal} setIsOpen={setCycleDeleteModal} data={cycle} user={user} />
<div
className={`fixed top-[66px] ${
className={`fixed top-[66px] z-20 ${
isOpen ? "right-0" : "-right-[24rem]"
} h-full w-[24rem] overflow-y-auto border-l border-custom-border-200 bg-custom-sidebar-background-100 pt-5 pb-10 duration-300`}
>
Expand All @@ -316,9 +290,7 @@ export const CycleDetailsSidebar: React.FC<Props> = ({
<CalendarDaysIcon className="h-3 w-3" />
<span>
{renderShortDateWithYearFormat(
new Date(
`${watch("start_date") ? watch("start_date") : cycle?.start_date}`
),
new Date(`${watch("start_date") ? watch("start_date") : cycle?.start_date}`),
"Start date"
)}
</span>
Expand Down Expand Up @@ -367,9 +339,7 @@ export const CycleDetailsSidebar: React.FC<Props> = ({

<span>
{renderShortDateWithYearFormat(
new Date(
`${watch("end_date") ? watch("end_date") : cycle?.end_date}`
),
new Date(`${watch("end_date") ? watch("end_date") : cycle?.end_date}`),
"End date"
)}
</span>
Expand Down Expand Up @@ -409,9 +379,7 @@ export const CycleDetailsSidebar: React.FC<Props> = ({
<div className="flex w-full flex-col items-start justify-start gap-2">
<div className="flex w-full items-start justify-between gap-2">
<div className="max-w-[300px]">
<h4 className="text-xl font-semibold text-custom-text-100 break-words w-full">
{cycle.name}
</h4>
<h4 className="text-xl font-semibold text-custom-text-100 break-words w-full">{cycle.name}</h4>
</div>
<CustomMenu width="lg" ellipsis>
{!isCompleted && (
Expand Down Expand Up @@ -480,9 +448,7 @@ export const CycleDetailsSidebar: React.FC<Props> = ({
<div className="flex w-full flex-col items-center justify-start gap-2 border-t border-custom-border-200 p-6">
<Disclosure defaultOpen>
{({ open }) => (
<div
className={`relative flex h-full w-full flex-col ${open ? "" : "flex-row"}`}
>
<div className={`relative flex h-full w-full flex-col ${open ? "" : "flex-row"}`}>
<div className="flex w-full items-center justify-between gap-2 ">
<div className="flex items-center justify-start gap-2 text-sm">
<span className="font-medium text-custom-text-200">Progress</span>
Expand All @@ -503,11 +469,7 @@ export const CycleDetailsSidebar: React.FC<Props> = ({
</Disclosure.Button>
) : (
<div className="flex items-center gap-1">
<ExclamationIcon
height={14}
width={14}
className="fill-current text-custom-text-200"
/>
<ExclamationIcon height={14} width={14} className="fill-current text-custom-text-200" />
<span className="text-xs italic text-custom-text-200">
{cycleStatus === "upcoming"
? "Cycle is yet to start."
Expand All @@ -527,8 +489,7 @@ export const CycleDetailsSidebar: React.FC<Props> = ({
</span>
<span>
Pending Issues -{" "}
{cycle.total_issues -
(cycle.completed_issues + cycle.cancelled_issues)}
{cycle.total_issues - (cycle.completed_issues + cycle.cancelled_issues)}
</span>
</div>

Expand Down Expand Up @@ -564,9 +525,7 @@ export const CycleDetailsSidebar: React.FC<Props> = ({
<div className="flex w-full flex-col items-center justify-start gap-2 border-t border-custom-border-200 p-6">
<Disclosure defaultOpen>
{({ open }) => (
<div
className={`relative flex h-full w-full flex-col ${open ? "" : "flex-row"}`}
>
<div className={`relative flex h-full w-full flex-col ${open ? "" : "flex-row"}`}>
<div className="flex w-full items-center justify-between gap-2">
<div className="flex items-center justify-start gap-2 text-sm">
<span className="font-medium text-custom-text-200">Other Information</span>
Expand All @@ -581,11 +540,7 @@ export const CycleDetailsSidebar: React.FC<Props> = ({
</Disclosure.Button>
) : (
<div className="flex items-center gap-1">
<ExclamationIcon
height={14}
width={14}
className="fill-current text-custom-text-200"
/>
<ExclamationIcon height={14} width={14} className="fill-current text-custom-text-200" />
<span className="text-xs italic text-custom-text-200">
No issues found. Please add issue.
</span>
Expand Down
53 changes: 53 additions & 0 deletions web/components/issues/issue-layouts/cycle-layout-root.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import React from "react";
// next imports
import { useRouter } from "next/router";
// swr
import useSWR from "swr";
// mobx react lite
import { observer } from "mobx-react-lite";
// components
import { CycleListLayout } from "./list/cycle-root";
import { CycleKanBanLayout } from "./kanban/cycle-root";
// mobx store
import { useMobxStore } from "lib/mobx/store-provider";

export const CycleLayoutRoot: React.FC = observer(() => {
const router = useRouter();
const { workspaceSlug, projectId, cycleId } = router.query as {
workspaceSlug: string;
projectId: string;
cycleId: string;
};

const {
project: projectStore,
issueFilter: issueFilterStore,
cycleIssue: cycleIssueStore,
cycleIssueFilter: cycleIssueFilterStore,
} = useMobxStore();

useSWR(workspaceSlug && projectId && cycleId ? `CYCLE_ISSUES` : null, async () => {
if (workspaceSlug && projectId && cycleId) {
// fetching the project display filters and display properties
await issueFilterStore.fetchUserProjectFilters(workspaceSlug, projectId);
// fetching the cycle filters
await cycleIssueFilterStore.fetchCycleFilters(workspaceSlug, projectId, cycleId);

// fetching the project state, labels and members
await projectStore.fetchProjectStates(workspaceSlug, projectId);
await projectStore.fetchProjectLabels(workspaceSlug, projectId);
await projectStore.fetchProjectMembers(workspaceSlug, projectId);

// fetching the cycle issues
await cycleIssueStore.fetchIssues(workspaceSlug, projectId, cycleId);
}
});

const activeLayout = issueFilterStore.userDisplayFilters.layout;

return (
<div className="w-full h-full">
{activeLayout === "list" ? <CycleListLayout /> : activeLayout === "kanban" ? <CycleKanBanLayout /> : null}
</div>
);
});
10 changes: 9 additions & 1 deletion web/components/issues/issue-layouts/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
// filters
export * from "./filters";

// layouts
export * from "./list";
export * from "./calendar";
export * from "./filters";
export * from "./gantt";
export * from "./kanban";
export * from "./spreadsheet";

// cycle root layout
export * from "./cycle-layout-root";

// module root layout
export * from "./module-all-layouts";
85 changes: 85 additions & 0 deletions web/components/issues/issue-layouts/kanban/cycle-root.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import React from "react";
// react beautiful dnd
import { DragDropContext } from "@hello-pangea/dnd";
// mobx
import { observer } from "mobx-react-lite";
// components
import { KanBanSwimLanes } from "./swimlanes";
import { KanBan } from "./default";
// store
import { useMobxStore } from "lib/mobx/store-provider";
import { RootStore } from "store/root";

export interface ICycleKanBanLayout {}

export const CycleKanBanLayout: React.FC = observer(() => {
const {
cycleIssue: cycleIssueStore,
issueFilter: issueFilterStore,
cycleIssueKanBanView: cycleIssueKanBanViewStore,
}: RootStore = useMobxStore();

const issues = cycleIssueStore?.getIssues;

const sub_group_by: string | null = issueFilterStore?.userDisplayFilters?.sub_group_by || null;

const group_by: string | null = issueFilterStore?.userDisplayFilters?.group_by || null;

const display_properties = issueFilterStore?.userDisplayProperties || null;

const currentKanBanView: "swimlanes" | "default" = issueFilterStore?.userDisplayFilters?.sub_group_by
? "swimlanes"
: "default";

const onDragEnd = (result: any) => {
if (!result) return;

if (
result.destination &&
result.source &&
result.destination.droppableId === result.source.droppableId &&
result.destination.index === result.source.index
)
return;

currentKanBanView === "default"
? cycleIssueKanBanViewStore?.handleDragDrop(result.source, result.destination)
: cycleIssueKanBanViewStore?.handleSwimlaneDragDrop(result.source, result.destination);
};

const updateIssue = (sub_group_by: string | null, group_by: string | null, issue: any) => {
cycleIssueStore.updateIssueStructure(group_by, sub_group_by, issue);
};

const handleKanBanToggle = (toggle: "groupByHeaderMinMax" | "subgroupByIssuesVisibility", value: string) => {
cycleIssueKanBanViewStore.handleKanBanToggle(toggle, value);
};

return (
<div className={`relative min-w-full w-max min-h-full h-max bg-custom-background-90 px-3`}>
<DragDropContext onDragEnd={onDragEnd}>
{currentKanBanView === "default" ? (
<KanBan
issues={issues}
sub_group_by={sub_group_by}
group_by={group_by}
handleIssues={updateIssue}
display_properties={display_properties}
kanBanToggle={cycleIssueKanBanViewStore?.kanBanToggle}
handleKanBanToggle={handleKanBanToggle}
/>
) : (
<KanBanSwimLanes
issues={issues}
sub_group_by={sub_group_by}
group_by={group_by}
handleIssues={updateIssue}
display_properties={display_properties}
kanBanToggle={cycleIssueKanBanViewStore?.kanBanToggle}
handleKanBanToggle={handleKanBanToggle}
/>
)}
</DragDropContext>
</div>
);
});
Loading