chore: implemented CRUD operations in all the layouts (#2505)
* chore: basic crud operations added to the list view * refactor: cycle details page * refactor: module details page * chore: added quick actions to kanban issue block * chore: implement quick actions in calendar layout * fix: custom menu component * chore: separate quick action dropdowns implemented * style: loader for calendar * fix: build errors
This commit is contained in:
parent
9bddd2eb67
commit
d78b4dccf3
76 changed files with 2336 additions and 1153 deletions
|
|
@ -9,14 +9,17 @@ import { Spinner } from "@plane/ui";
|
|||
// types
|
||||
import { ICalendarWeek } from "./types";
|
||||
import { IIssueGroupedStructure } from "store/issue";
|
||||
import { IIssue } from "types";
|
||||
|
||||
type Props = {
|
||||
issues: IIssueGroupedStructure | null;
|
||||
layout: "month" | "week" | undefined;
|
||||
showWeekends: boolean;
|
||||
quickActions: (issue: IIssue) => React.ReactNode;
|
||||
};
|
||||
|
||||
export const CalendarChart: React.FC<Props> = observer((props) => {
|
||||
const { issues, layout } = props;
|
||||
const { issues, layout, showWeekends, quickActions } = props;
|
||||
|
||||
const { calendar: calendarStore } = useMobxStore();
|
||||
|
||||
|
|
@ -35,17 +38,17 @@ export const CalendarChart: React.FC<Props> = observer((props) => {
|
|||
<>
|
||||
<div className="h-full w-full flex flex-col overflow-hidden">
|
||||
<CalendarHeader />
|
||||
<CalendarWeekHeader />
|
||||
<CalendarWeekHeader isLoading={!issues} showWeekends={showWeekends} />
|
||||
<div className="h-full w-full overflow-y-auto">
|
||||
{layout === "month" ? (
|
||||
<div className="h-full w-full grid grid-cols-1 divide-y-[0.5px] divide-custom-border-200">
|
||||
{allWeeksOfActiveMonth &&
|
||||
Object.values(allWeeksOfActiveMonth).map((week: ICalendarWeek, weekIndex) => (
|
||||
<CalendarWeekDays key={weekIndex} week={week} issues={issues} />
|
||||
<CalendarWeekDays key={weekIndex} week={week} issues={issues} quickActions={quickActions} />
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<CalendarWeekDays week={calendarStore.allDaysOfActiveWeek} issues={issues} />
|
||||
<CalendarWeekDays week={calendarStore.allDaysOfActiveWeek} issues={issues} quickActions={quickActions} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -11,11 +11,16 @@ import { renderDateFormat } from "helpers/date-time.helper";
|
|||
import { IIssueGroupedStructure } from "store/issue";
|
||||
// constants
|
||||
import { MONTHS_LIST } from "constants/calendar";
|
||||
import { IIssue } from "types";
|
||||
|
||||
type Props = { date: ICalendarDate; issues: IIssueGroupedStructure | null };
|
||||
type Props = {
|
||||
date: ICalendarDate;
|
||||
issues: IIssueGroupedStructure | null;
|
||||
quickActions: (issue: IIssue) => React.ReactNode;
|
||||
};
|
||||
|
||||
export const CalendarDayTile: React.FC<Props> = observer((props) => {
|
||||
const { date, issues } = props;
|
||||
const { date, issues, quickActions } = props;
|
||||
|
||||
const { issueFilter: issueFilterStore } = useMobxStore();
|
||||
|
||||
|
|
@ -48,7 +53,7 @@ export const CalendarDayTile: React.FC<Props> = observer((props) => {
|
|||
{date.date.getDate() === 1 && MONTHS_LIST[date.date.getMonth() + 1].shortTitle + " "}
|
||||
{date.date.getDate()}
|
||||
</div>
|
||||
<CalendarIssueBlocks issues={issuesList} />
|
||||
<CalendarIssueBlocks issues={issuesList} quickActions={quickActions} />
|
||||
{provided.placeholder}
|
||||
</>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import React from "react";
|
||||
import React, { useState } from "react";
|
||||
import { Popover, Transition } from "@headlessui/react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
|
||||
import { usePopper } from "react-popper";
|
||||
// mobx store
|
||||
import { useMobxStore } from "lib/mobx/store-provider";
|
||||
// icons
|
||||
|
|
@ -14,6 +14,21 @@ export const CalendarMonthsDropdown: React.FC = observer(() => {
|
|||
|
||||
const calendarLayout = issueFilterStore.userDisplayFilters.calendar?.layout ?? "month";
|
||||
|
||||
const [referenceElement, setReferenceElement] = useState<HTMLButtonElement | null>(null);
|
||||
const [popperElement, setPopperElement] = useState<HTMLDivElement | null>(null);
|
||||
|
||||
const { styles, attributes } = usePopper(referenceElement, popperElement, {
|
||||
placement: "auto",
|
||||
modifiers: [
|
||||
{
|
||||
name: "preventOverflow",
|
||||
options: {
|
||||
padding: 12,
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const { activeMonthDate } = calendarStore.calendarFilters;
|
||||
|
||||
const getWeekLayoutHeader = (): string => {
|
||||
|
|
@ -47,10 +62,17 @@ export const CalendarMonthsDropdown: React.FC = observer(() => {
|
|||
|
||||
return (
|
||||
<Popover className="relative">
|
||||
<Popover.Button className="outline-none text-xl font-semibold" disabled={calendarLayout === "week"}>
|
||||
{calendarLayout === "month"
|
||||
? `${MONTHS_LIST[activeMonthDate.getMonth() + 1].title} ${activeMonthDate.getFullYear()}`
|
||||
: getWeekLayoutHeader()}
|
||||
<Popover.Button as={React.Fragment}>
|
||||
<button
|
||||
type="button"
|
||||
ref={setReferenceElement}
|
||||
className="outline-none text-xl font-semibold"
|
||||
disabled={calendarLayout === "week"}
|
||||
>
|
||||
{calendarLayout === "month"
|
||||
? `${MONTHS_LIST[activeMonthDate.getMonth() + 1].title} ${activeMonthDate.getFullYear()}`
|
||||
: getWeekLayoutHeader()}
|
||||
</button>
|
||||
</Popover.Button>
|
||||
<Transition
|
||||
as={React.Fragment}
|
||||
|
|
@ -61,8 +83,13 @@ export const CalendarMonthsDropdown: React.FC = observer(() => {
|
|||
leaveFrom="opacity-100 translate-y-0"
|
||||
leaveTo="opacity-0 translate-y-1"
|
||||
>
|
||||
<Popover.Panel>
|
||||
<div className="absolute left-0 z-10 mt-1 bg-custom-background-100 border border-custom-border-200 shadow-custom-shadow-rg rounded w-56 p-3 divide-y divide-custom-border-200">
|
||||
<Popover.Panel className="fixed z-50">
|
||||
<div
|
||||
ref={setPopperElement}
|
||||
style={styles.popper}
|
||||
{...attributes.popper}
|
||||
className="bg-custom-background-100 border border-custom-border-200 shadow-custom-shadow-rg rounded w-56 p-3 divide-y divide-custom-border-200"
|
||||
>
|
||||
<div className="flex items-center justify-between gap-2 pb-3">
|
||||
<button
|
||||
type="button"
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import React from "react";
|
||||
import React, { useState } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import { Popover, Transition } from "@headlessui/react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
|
||||
import { usePopper } from "react-popper";
|
||||
// mobx store
|
||||
import { useMobxStore } from "lib/mobx/store-provider";
|
||||
// ui
|
||||
|
|
@ -20,6 +20,21 @@ export const CalendarOptionsDropdown: React.FC = observer(() => {
|
|||
|
||||
const { issueFilter: issueFilterStore, calendar: calendarStore } = useMobxStore();
|
||||
|
||||
const [referenceElement, setReferenceElement] = useState<HTMLButtonElement | null>(null);
|
||||
const [popperElement, setPopperElement] = useState<HTMLDivElement | null>(null);
|
||||
|
||||
const { styles, attributes } = usePopper(referenceElement, popperElement, {
|
||||
placement: "auto",
|
||||
modifiers: [
|
||||
{
|
||||
name: "preventOverflow",
|
||||
options: {
|
||||
padding: 12,
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const calendarLayout = issueFilterStore.userDisplayFilters.calendar?.layout ?? "month";
|
||||
const showWeekends = issueFilterStore.userDisplayFilters.calendar?.show_weekends ?? false;
|
||||
|
||||
|
|
@ -57,12 +72,12 @@ export const CalendarOptionsDropdown: React.FC = observer(() => {
|
|||
|
||||
return (
|
||||
<Popover className="relative">
|
||||
{({ open }) => {
|
||||
if (open) {
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<Popover.Button
|
||||
{({ open }) => (
|
||||
<>
|
||||
<Popover.Button as={React.Fragment}>
|
||||
<button
|
||||
type="button"
|
||||
ref={setReferenceElement}
|
||||
className={`outline-none bg-custom-background-80 text-xs rounded flex items-center gap-1.5 px-2.5 py-1 hover:bg-custom-background-80 ${
|
||||
open ? "text-custom-text-100" : "text-custom-text-200"
|
||||
}`}
|
||||
|
|
@ -73,45 +88,50 @@ export const CalendarOptionsDropdown: React.FC = observer(() => {
|
|||
>
|
||||
<ChevronUp width={12} strokeWidth={2} />
|
||||
</div>
|
||||
</Popover.Button>
|
||||
<Transition
|
||||
as={React.Fragment}
|
||||
enter="transition ease-out duration-200"
|
||||
enterFrom="opacity-0 translate-y-1"
|
||||
enterTo="opacity-100 translate-y-0"
|
||||
leave="transition ease-in duration-150"
|
||||
leaveFrom="opacity-100 translate-y-0"
|
||||
leaveTo="opacity-0 translate-y-1"
|
||||
>
|
||||
<Popover.Panel>
|
||||
<div className="absolute right-0 z-10 mt-1 bg-custom-background-100 border border-custom-border-200 shadow-custom-shadow-rg rounded min-w-[12rem] p-1 overflow-hidden">
|
||||
<div>
|
||||
{Object.entries(CALENDAR_LAYOUTS).map(([layout, layoutDetails]) => (
|
||||
<button
|
||||
key={layout}
|
||||
type="button"
|
||||
className="text-xs hover:bg-custom-background-80 w-full text-left px-1 py-1.5 rounded flex items-center justify-between gap-2"
|
||||
onClick={() => handleLayoutChange(layoutDetails.key)}
|
||||
>
|
||||
{layoutDetails.title}
|
||||
{calendarLayout === layout && <Check size={12} strokeWidth={2} />}
|
||||
</button>
|
||||
))}
|
||||
</button>
|
||||
</Popover.Button>
|
||||
<Transition
|
||||
as={React.Fragment}
|
||||
enter="transition ease-out duration-200"
|
||||
enterFrom="opacity-0 translate-y-1"
|
||||
enterTo="opacity-100 translate-y-0"
|
||||
leave="transition ease-in duration-150"
|
||||
leaveFrom="opacity-100 translate-y-0"
|
||||
leaveTo="opacity-0 translate-y-1"
|
||||
>
|
||||
<Popover.Panel className="fixed z-50">
|
||||
<div
|
||||
ref={setPopperElement}
|
||||
style={styles.popper}
|
||||
{...attributes.popper}
|
||||
className="absolute right-0 z-10 mt-1 bg-custom-background-100 border border-custom-border-200 shadow-custom-shadow-sm rounded min-w-[12rem] p-1 overflow-hidden"
|
||||
>
|
||||
<div>
|
||||
{Object.entries(CALENDAR_LAYOUTS).map(([layout, layoutDetails]) => (
|
||||
<button
|
||||
key={layout}
|
||||
type="button"
|
||||
className="text-xs hover:bg-custom-background-80 w-full text-left px-1 py-1.5 rounded flex items-center justify-between gap-2"
|
||||
onClick={handleToggleWeekends}
|
||||
onClick={() => handleLayoutChange(layoutDetails.key)}
|
||||
>
|
||||
Show weekends
|
||||
<ToggleSwitch value={showWeekends} onChange={() => {}} />
|
||||
{layoutDetails.title}
|
||||
{calendarLayout === layout && <Check size={12} strokeWidth={2} />}
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
<button
|
||||
type="button"
|
||||
className="text-xs hover:bg-custom-background-80 w-full text-left px-1 py-1.5 rounded flex items-center justify-between gap-2"
|
||||
onClick={handleToggleWeekends}
|
||||
>
|
||||
Show weekends
|
||||
<ToggleSwitch value={showWeekends} onChange={() => {}} />
|
||||
</button>
|
||||
</div>
|
||||
</Popover.Panel>
|
||||
</Transition>
|
||||
</>
|
||||
);
|
||||
}}
|
||||
</div>
|
||||
</Popover.Panel>
|
||||
</Transition>
|
||||
</>
|
||||
)}
|
||||
</Popover>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,15 +1,17 @@
|
|||
import Link from "next/link";
|
||||
import { useRouter } from "next/router";
|
||||
import { Draggable } from "@hello-pangea/dnd";
|
||||
import { observer } from "mobx-react-lite";
|
||||
|
||||
import { Draggable } from "@hello-pangea/dnd";
|
||||
// types
|
||||
import { IIssue } from "types";
|
||||
|
||||
type Props = { issues: IIssue[] | null };
|
||||
type Props = {
|
||||
issues: IIssue[] | null;
|
||||
quickActions: (issue: IIssue) => React.ReactNode;
|
||||
};
|
||||
|
||||
export const CalendarIssueBlocks: React.FC<Props> = observer((props) => {
|
||||
const { issues } = props;
|
||||
const { issues, quickActions } = props;
|
||||
|
||||
const router = useRouter();
|
||||
const { workspaceSlug } = router.query;
|
||||
|
|
@ -21,7 +23,7 @@ export const CalendarIssueBlocks: React.FC<Props> = observer((props) => {
|
|||
{(provided, snapshot) => (
|
||||
<Link href={`/${workspaceSlug?.toString()}/projects/${issue.project}/issues/${issue.id}`}>
|
||||
<a
|
||||
className={`h-8 w-full shadow-custom-shadow-2xs rounded py-1.5 px-1 flex items-center gap-1.5 border-[0.5px] border-custom-border-100 ${
|
||||
className={`group/calendar-block h-8 w-full shadow-custom-shadow-2xs rounded py-1.5 px-1 flex items-center gap-1.5 border-[0.5px] border-custom-border-100 ${
|
||||
snapshot.isDragging
|
||||
? "shadow-custom-shadow-rg bg-custom-background-90"
|
||||
: "bg-custom-background-100 hover:bg-custom-background-90"
|
||||
|
|
@ -40,6 +42,12 @@ export const CalendarIssueBlocks: React.FC<Props> = observer((props) => {
|
|||
{issue.project_detail.identifier}-{issue.sequence_id}
|
||||
</div>
|
||||
<h6 className="text-xs flex-grow truncate">{issue.name}</h6>
|
||||
<div className="hidden group-hover/calendar-block:block">{quickActions(issue)}</div>
|
||||
{/* <IssueQuickActions
|
||||
issue={issue}
|
||||
handleDelete={async () => handleIssues(issue.target_date ?? "", issue, "delete")}
|
||||
handleUpdate={async (data) => handleIssues(issue.target_date ?? "", data, "update")}
|
||||
/> */}
|
||||
</a>
|
||||
</Link>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -1,15 +1,20 @@
|
|||
import { useCallback } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { DragDropContext, DropResult } from "@hello-pangea/dnd";
|
||||
|
||||
// mobx store
|
||||
import { useMobxStore } from "lib/mobx/store-provider";
|
||||
// components
|
||||
import { CalendarChart } from "components/issues";
|
||||
import { CalendarChart, CycleIssueQuickActions } from "components/issues";
|
||||
// types
|
||||
import { IIssueGroupedStructure } from "store/issue";
|
||||
import { IIssue } from "types";
|
||||
|
||||
export const CycleCalendarLayout: React.FC = observer(() => {
|
||||
const { cycleIssue: cycleIssueStore, issueFilter: issueFilterStore } = useMobxStore();
|
||||
const { cycleIssue: cycleIssueStore, issueFilter: issueFilterStore, issueDetail: issueDetailStore } = useMobxStore();
|
||||
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, cycleId } = router.query;
|
||||
|
||||
// TODO: add drag and drop functionality
|
||||
const onDragEnd = (result: DropResult) => {
|
||||
|
|
@ -26,12 +31,43 @@ export const CycleCalendarLayout: React.FC = observer(() => {
|
|||
|
||||
const issues = cycleIssueStore.getIssues;
|
||||
|
||||
const handleIssues = useCallback(
|
||||
(date: string, issue: IIssue, action: "update" | "delete" | "remove") => {
|
||||
if (!workspaceSlug || !cycleId) return;
|
||||
|
||||
if (action === "update") {
|
||||
cycleIssueStore.updateIssueStructure(date, null, issue);
|
||||
issueDetailStore.updateIssue(workspaceSlug.toString(), issue.project, issue.id, issue);
|
||||
}
|
||||
if (action === "delete") cycleIssueStore.deleteIssue(date, null, issue);
|
||||
if (action === "remove" && issue.bridge_id) {
|
||||
cycleIssueStore.deleteIssue(date, null, issue);
|
||||
cycleIssueStore.removeIssueFromCycle(
|
||||
workspaceSlug.toString(),
|
||||
issue.project,
|
||||
cycleId.toString(),
|
||||
issue.bridge_id
|
||||
);
|
||||
}
|
||||
},
|
||||
[cycleIssueStore, issueDetailStore, cycleId, workspaceSlug]
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="h-full w-full pt-4 bg-custom-background-100 overflow-hidden">
|
||||
<DragDropContext onDragEnd={onDragEnd}>
|
||||
<CalendarChart
|
||||
issues={issues as IIssueGroupedStructure | null}
|
||||
layout={issueFilterStore.userDisplayFilters.calendar?.layout}
|
||||
showWeekends={issueFilterStore.userDisplayFilters.calendar?.show_weekends ?? false}
|
||||
quickActions={(issue) => (
|
||||
<CycleIssueQuickActions
|
||||
issue={issue}
|
||||
handleDelete={async () => handleIssues(issue.target_date ?? "", issue, "delete")}
|
||||
handleUpdate={async (data) => handleIssues(issue.target_date ?? "", data, "update")}
|
||||
handleRemoveFromCycle={async () => handleIssues(issue.target_date ?? "", issue, "remove")}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</DragDropContext>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,15 +1,24 @@
|
|||
import { useCallback } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { DragDropContext, DropResult } from "@hello-pangea/dnd";
|
||||
|
||||
// mobx store
|
||||
import { useMobxStore } from "lib/mobx/store-provider";
|
||||
// components
|
||||
import { CalendarChart } from "components/issues";
|
||||
import { CalendarChart, ModuleIssueQuickActions } from "components/issues";
|
||||
// types
|
||||
import { IIssueGroupedStructure } from "store/issue";
|
||||
import { IIssue } from "types";
|
||||
|
||||
export const ModuleCalendarLayout: React.FC = observer(() => {
|
||||
const { moduleIssue: moduleIssueStore, issueFilter: issueFilterStore } = useMobxStore();
|
||||
const {
|
||||
moduleIssue: moduleIssueStore,
|
||||
issueFilter: issueFilterStore,
|
||||
issueDetail: issueDetailStore,
|
||||
} = useMobxStore();
|
||||
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, moduleId } = router.query;
|
||||
|
||||
// TODO: add drag and drop functionality
|
||||
const onDragEnd = (result: DropResult) => {
|
||||
|
|
@ -26,12 +35,45 @@ export const ModuleCalendarLayout: React.FC = observer(() => {
|
|||
|
||||
const issues = moduleIssueStore.getIssues;
|
||||
|
||||
const handleIssues = useCallback(
|
||||
(date: string, issue: IIssue, action: "update" | "delete" | "remove") => {
|
||||
if (!workspaceSlug || !moduleId) return;
|
||||
|
||||
if (action === "update") {
|
||||
moduleIssueStore.updateIssueStructure(date, null, issue);
|
||||
issueDetailStore.updateIssue(workspaceSlug.toString(), issue.project, issue.id, issue);
|
||||
} else {
|
||||
moduleIssueStore.deleteIssue(date, null, issue);
|
||||
issueDetailStore.deleteIssue(workspaceSlug.toString(), issue.project, issue.id);
|
||||
}
|
||||
if (action === "remove" && issue.bridge_id) {
|
||||
moduleIssueStore.deleteIssue(date, null, issue);
|
||||
moduleIssueStore.removeIssueFromModule(
|
||||
workspaceSlug.toString(),
|
||||
issue.project,
|
||||
moduleId.toString(),
|
||||
issue.bridge_id
|
||||
);
|
||||
}
|
||||
},
|
||||
[moduleIssueStore, issueDetailStore, moduleId, workspaceSlug]
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="h-full w-full pt-4 bg-custom-background-100 overflow-hidden">
|
||||
<DragDropContext onDragEnd={onDragEnd}>
|
||||
<CalendarChart
|
||||
issues={issues as IIssueGroupedStructure | null}
|
||||
layout={issueFilterStore.userDisplayFilters.calendar?.layout}
|
||||
showWeekends={issueFilterStore.userDisplayFilters.calendar?.show_weekends ?? false}
|
||||
quickActions={(issue) => (
|
||||
<ModuleIssueQuickActions
|
||||
issue={issue}
|
||||
handleDelete={async () => handleIssues(issue.target_date ?? "", issue, "delete")}
|
||||
handleUpdate={async (data) => handleIssues(issue.target_date ?? "", data, "update")}
|
||||
handleRemoveFromModule={async () => handleIssues(issue.target_date ?? "", issue, "remove")}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</DragDropContext>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,15 +1,20 @@
|
|||
import { useCallback } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { DragDropContext, DropResult } from "@hello-pangea/dnd";
|
||||
|
||||
// mobx store
|
||||
import { useMobxStore } from "lib/mobx/store-provider";
|
||||
// components
|
||||
import { CalendarChart } from "components/issues";
|
||||
import { CalendarChart, ProjectIssueQuickActions } from "components/issues";
|
||||
// types
|
||||
import { IIssueGroupedStructure } from "store/issue";
|
||||
import { IIssue } from "types";
|
||||
|
||||
export const CalendarLayout: React.FC = observer(() => {
|
||||
const { issue: issueStore, issueFilter: issueFilterStore } = useMobxStore();
|
||||
const { issue: issueStore, issueFilter: issueFilterStore, issueDetail: issueDetailStore } = useMobxStore();
|
||||
|
||||
const router = useRouter();
|
||||
const { workspaceSlug } = router.query;
|
||||
|
||||
// TODO: add drag and drop functionality
|
||||
const onDragEnd = (result: DropResult) => {
|
||||
|
|
@ -26,12 +31,35 @@ export const CalendarLayout: React.FC = observer(() => {
|
|||
|
||||
const issues = issueStore.getIssues;
|
||||
|
||||
const handleIssues = useCallback(
|
||||
(date: string, issue: IIssue, action: "update" | "delete") => {
|
||||
if (!workspaceSlug) return;
|
||||
|
||||
if (action === "update") {
|
||||
issueStore.updateIssueStructure(date, null, issue);
|
||||
issueDetailStore.updateIssue(workspaceSlug.toString(), issue.project, issue.id, issue);
|
||||
} else {
|
||||
issueStore.deleteIssue(date, null, issue);
|
||||
issueDetailStore.deleteIssue(workspaceSlug.toString(), issue.project, issue.id);
|
||||
}
|
||||
},
|
||||
[issueStore, issueDetailStore, workspaceSlug]
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="h-full w-full pt-4 bg-custom-background-100 overflow-hidden">
|
||||
<DragDropContext onDragEnd={onDragEnd}>
|
||||
<CalendarChart
|
||||
issues={issues as IIssueGroupedStructure | null}
|
||||
layout={issueFilterStore.userDisplayFilters.calendar?.layout}
|
||||
showWeekends={issueFilterStore.userDisplayFilters.calendar?.show_weekends ?? false}
|
||||
quickActions={(issue) => (
|
||||
<ProjectIssueQuickActions
|
||||
issue={issue}
|
||||
handleDelete={async () => handleIssues(issue.target_date ?? "", issue, "delete")}
|
||||
handleUpdate={async (data) => handleIssues(issue.target_date ?? "", data, "update")}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</DragDropContext>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,15 +1,24 @@
|
|||
import { useCallback } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { DragDropContext, DropResult } from "@hello-pangea/dnd";
|
||||
|
||||
// mobx store
|
||||
import { useMobxStore } from "lib/mobx/store-provider";
|
||||
// components
|
||||
import { CalendarChart } from "components/issues";
|
||||
import { CalendarChart, ProjectIssueQuickActions } from "components/issues";
|
||||
// types
|
||||
import { IIssueGroupedStructure } from "store/issue";
|
||||
import { IIssue } from "types";
|
||||
|
||||
export const ProjectViewCalendarLayout: React.FC = observer(() => {
|
||||
const { projectViewIssues: projectViewIssuesStore, issueFilter: issueFilterStore } = useMobxStore();
|
||||
const {
|
||||
projectViewIssues: projectViewIssuesStore,
|
||||
issueFilter: issueFilterStore,
|
||||
issueDetail: issueDetailStore,
|
||||
} = useMobxStore();
|
||||
|
||||
const router = useRouter();
|
||||
const { workspaceSlug } = router.query;
|
||||
|
||||
// TODO: add drag and drop functionality
|
||||
const onDragEnd = (result: DropResult) => {
|
||||
|
|
@ -26,12 +35,35 @@ export const ProjectViewCalendarLayout: React.FC = observer(() => {
|
|||
|
||||
const issues = projectViewIssuesStore.getIssues;
|
||||
|
||||
const handleIssues = useCallback(
|
||||
(date: string, issue: IIssue, action: "update" | "delete") => {
|
||||
if (!workspaceSlug) return;
|
||||
|
||||
if (action === "update") {
|
||||
projectViewIssuesStore.updateIssueStructure(date, null, issue);
|
||||
issueDetailStore.updateIssue(workspaceSlug.toString(), issue.project, issue.id, issue);
|
||||
} else {
|
||||
projectViewIssuesStore.deleteIssue(date, null, issue);
|
||||
issueDetailStore.deleteIssue(workspaceSlug.toString(), issue.project, issue.id);
|
||||
}
|
||||
},
|
||||
[projectViewIssuesStore, issueDetailStore, workspaceSlug]
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="h-full w-full pt-4 bg-custom-background-100 overflow-hidden">
|
||||
<DragDropContext onDragEnd={onDragEnd}>
|
||||
<CalendarChart
|
||||
issues={issues as IIssueGroupedStructure | null}
|
||||
layout={issueFilterStore.userDisplayFilters.calendar?.layout}
|
||||
showWeekends={issueFilterStore.userDisplayFilters.calendar?.show_weekends ?? false}
|
||||
quickActions={(issue) => (
|
||||
<ProjectIssueQuickActions
|
||||
issue={issue}
|
||||
handleDelete={async () => handleIssues(issue.target_date ?? "", issue, "delete")}
|
||||
handleUpdate={async (data) => handleIssues(issue.target_date ?? "", data, "update")}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</DragDropContext>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -9,14 +9,16 @@ import { renderDateFormat } from "helpers/date-time.helper";
|
|||
// types
|
||||
import { ICalendarDate, ICalendarWeek } from "./types";
|
||||
import { IIssueGroupedStructure } from "store/issue";
|
||||
import { IIssue } from "types";
|
||||
|
||||
type Props = {
|
||||
issues: IIssueGroupedStructure | null;
|
||||
week: ICalendarWeek | undefined;
|
||||
quickActions: (issue: IIssue) => React.ReactNode;
|
||||
};
|
||||
|
||||
export const CalendarWeekDays: React.FC<Props> = observer((props) => {
|
||||
const { issues, week } = props;
|
||||
const { issues, week, quickActions } = props;
|
||||
|
||||
const { issueFilter: issueFilterStore } = useMobxStore();
|
||||
|
||||
|
|
@ -34,7 +36,9 @@ export const CalendarWeekDays: React.FC<Props> = observer((props) => {
|
|||
{Object.values(week).map((date: ICalendarDate) => {
|
||||
if (!showWeekends && (date.date.getDay() === 0 || date.date.getDay() === 6)) return null;
|
||||
|
||||
return <CalendarDayTile key={renderDateFormat(date.date)} date={date} issues={issues} />;
|
||||
return (
|
||||
<CalendarDayTile key={renderDateFormat(date.date)} date={date} issues={issues} quickActions={quickActions} />
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,21 +1,25 @@
|
|||
import { observer } from "mobx-react-lite";
|
||||
|
||||
// mobx store
|
||||
import { useMobxStore } from "lib/mobx/store-provider";
|
||||
// constants
|
||||
import { DAYS_LIST } from "constants/calendar";
|
||||
|
||||
export const CalendarWeekHeader: React.FC = observer(() => {
|
||||
const { issueFilter: issueFilterStore } = useMobxStore();
|
||||
type Props = {
|
||||
isLoading: boolean;
|
||||
showWeekends: boolean;
|
||||
};
|
||||
|
||||
const showWeekends = issueFilterStore.userDisplayFilters.calendar?.show_weekends ?? false;
|
||||
export const CalendarWeekHeader: React.FC<Props> = observer((props) => {
|
||||
const { isLoading, showWeekends } = props;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`grid text-sm font-medium divide-x-[0.5px] divide-custom-border-200 ${
|
||||
className={`relative grid text-sm font-medium divide-x-[0.5px] divide-custom-border-200 ${
|
||||
showWeekends ? "grid-cols-7" : "grid-cols-5"
|
||||
}`}
|
||||
>
|
||||
{isLoading && (
|
||||
<div className="absolute h-[1.5px] w-3/4 bg-custom-primary-100 animate-[bar-loader_2s_linear_infinite]" />
|
||||
)}
|
||||
{Object.values(DAYS_LIST).map((day) => {
|
||||
if (!showWeekends && (day.shortTitle === "Sat" || day.shortTitle === "Sun")) return null;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue