mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
Merge branch 'fix/issues-layout-mobx' of github.com:makeplane/plane into fix/issues-layout-mobx
This commit is contained in:
commit
ccf6bd4e32
@ -14,6 +14,7 @@ import useUser from "hooks/use-user";
|
|||||||
import { useProjectMyMembership } from "contexts/project-member.context";
|
import { useProjectMyMembership } from "contexts/project-member.context";
|
||||||
// components
|
// components
|
||||||
import { AllLists, AllBoards, CalendarView, SpreadsheetView, GanttChartView } from "components/core";
|
import { AllLists, AllBoards, CalendarView, SpreadsheetView, GanttChartView } from "components/core";
|
||||||
|
import { KanBanLayout } from "components/issues/issue-layouts";
|
||||||
// ui
|
// ui
|
||||||
import { EmptyState, Spinner } from "components/ui";
|
import { EmptyState, Spinner } from "components/ui";
|
||||||
// icons
|
// icons
|
||||||
@ -27,6 +28,9 @@ import { getStatesList } from "helpers/state.helper";
|
|||||||
import { IIssue, IIssueViewProps } from "types";
|
import { IIssue, IIssueViewProps } from "types";
|
||||||
// fetch-keys
|
// fetch-keys
|
||||||
import { STATES_LIST } from "constants/fetch-keys";
|
import { STATES_LIST } from "constants/fetch-keys";
|
||||||
|
// store
|
||||||
|
import { useMobxStore } from "lib/mobx/store-provider";
|
||||||
|
import { RootStore } from "store/root";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
addIssueToDate: (date: string) => void;
|
addIssueToDate: (date: string) => void;
|
||||||
@ -71,7 +75,12 @@ export const AllViews: React.FC<Props> = ({
|
|||||||
viewProps,
|
viewProps,
|
||||||
}) => {
|
}) => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug, projectId, cycleId, moduleId } = router.query;
|
const { workspaceSlug, projectId, cycleId, moduleId } = router.query as {
|
||||||
|
workspaceSlug: string;
|
||||||
|
projectId: string;
|
||||||
|
cycleId: string;
|
||||||
|
moduleId: string;
|
||||||
|
};
|
||||||
|
|
||||||
const [myIssueProjectId, setMyIssueProjectId] = useState<string | null>(null);
|
const [myIssueProjectId, setMyIssueProjectId] = useState<string | null>(null);
|
||||||
|
|
||||||
@ -97,118 +106,135 @@ export const AllViews: React.FC<Props> = ({
|
|||||||
[trashBox, setTrashBox]
|
[trashBox, setTrashBox]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const { issue: issueStore, project: projectStore, issueFilter: issueFilterStore }: RootStore = useMobxStore();
|
||||||
|
|
||||||
|
useSWR(workspaceSlug && projectId ? `PROJECT_ISSUES` : null, async () => {
|
||||||
|
if (workspaceSlug && projectId) {
|
||||||
|
await issueFilterStore.fetchUserProjectFilters(workspaceSlug, projectId);
|
||||||
|
|
||||||
|
await projectStore.fetchProjectStates(workspaceSlug, projectId);
|
||||||
|
await projectStore.fetchProjectLabels(workspaceSlug, projectId);
|
||||||
|
await projectStore.fetchProjectMembers(workspaceSlug, projectId);
|
||||||
|
|
||||||
|
await issueStore.fetchIssues(workspaceSlug, projectId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DragDropContext onDragEnd={handleOnDragEnd}>
|
// <DragDropContext onDragEnd={handleOnDragEnd}>
|
||||||
<StrictModeDroppable droppableId="trashBox">
|
// <StrictModeDroppable droppableId="trashBox">
|
||||||
{(provided, snapshot) => (
|
// {(provided, snapshot) => (
|
||||||
<div
|
// <div
|
||||||
className={`${
|
// className={`${
|
||||||
trashBox ? "pointer-events-auto opacity-100" : "pointer-events-none opacity-0"
|
// trashBox ? "pointer-events-auto opacity-100" : "pointer-events-none opacity-0"
|
||||||
} fixed top-4 left-1/2 -translate-x-1/2 z-40 w-72 flex items-center justify-center gap-2 rounded border-2 border-red-500/20 bg-custom-background-100 px-3 py-5 text-xs font-medium italic text-red-500 ${
|
// } fixed top-4 left-1/2 -translate-x-1/2 z-40 w-72 flex items-center justify-center gap-2 rounded border-2 border-red-500/20 bg-custom-background-100 px-3 py-5 text-xs font-medium italic text-red-500 ${
|
||||||
snapshot.isDraggingOver ? "bg-red-500 blur-2xl opacity-70" : ""
|
// snapshot.isDraggingOver ? "bg-red-500 blur-2xl opacity-70" : ""
|
||||||
} transition duration-300`}
|
// } transition duration-300`}
|
||||||
ref={provided.innerRef}
|
// ref={provided.innerRef}
|
||||||
{...provided.droppableProps}
|
// {...provided.droppableProps}
|
||||||
>
|
// >
|
||||||
<TrashIcon className="h-4 w-4" />
|
// <TrashIcon className="h-4 w-4" />
|
||||||
Drop here to delete the issue.
|
// Drop here to delete the issue.
|
||||||
|
// </div>
|
||||||
|
// )}
|
||||||
|
// </StrictModeDroppable>
|
||||||
|
// {groupedIssues ? (
|
||||||
|
// !isEmpty ||
|
||||||
|
// displayFilters?.layout === "kanban" ||
|
||||||
|
// displayFilters?.layout === "calendar" ||
|
||||||
|
// displayFilters?.layout === "gantt_chart" ? (
|
||||||
|
// <>
|
||||||
|
// {displayFilters?.layout === "list" ? (
|
||||||
|
// <AllLists
|
||||||
|
// states={states}
|
||||||
|
// addIssueToGroup={addIssueToGroup}
|
||||||
|
// handleIssueAction={handleIssueAction}
|
||||||
|
// handleDraftIssueAction={handleDraftIssueAction}
|
||||||
|
// openIssuesListModal={cycleId || moduleId ? openIssuesListModal : null}
|
||||||
|
// removeIssue={removeIssue}
|
||||||
|
// myIssueProjectId={myIssueProjectId}
|
||||||
|
// handleMyIssueOpen={handleMyIssueOpen}
|
||||||
|
// disableUserActions={disableUserActions}
|
||||||
|
// disableAddIssueOption={disableAddIssueOption}
|
||||||
|
// user={user}
|
||||||
|
// userAuth={memberRole}
|
||||||
|
// viewProps={viewProps}
|
||||||
|
// />
|
||||||
|
// ) : displayFilters?.layout === "kanban" ? (
|
||||||
|
// <AllBoards
|
||||||
|
// addIssueToGroup={addIssueToGroup}
|
||||||
|
// disableUserActions={disableUserActions}
|
||||||
|
// disableAddIssueOption={disableAddIssueOption}
|
||||||
|
// dragDisabled={dragDisabled}
|
||||||
|
// handleIssueAction={handleIssueAction}
|
||||||
|
// handleDraftIssueAction={handleDraftIssueAction}
|
||||||
|
// handleTrashBox={handleTrashBox}
|
||||||
|
// openIssuesListModal={cycleId || moduleId ? openIssuesListModal : null}
|
||||||
|
// myIssueProjectId={myIssueProjectId}
|
||||||
|
// handleMyIssueOpen={handleMyIssueOpen}
|
||||||
|
// removeIssue={removeIssue}
|
||||||
|
// states={states}
|
||||||
|
// user={user}
|
||||||
|
// userAuth={memberRole}
|
||||||
|
// viewProps={viewProps}
|
||||||
|
// />
|
||||||
|
// ) : displayFilters?.layout === "calendar" ? (
|
||||||
|
// <CalendarView
|
||||||
|
// handleIssueAction={handleIssueAction}
|
||||||
|
// addIssueToDate={addIssueToDate}
|
||||||
|
// disableUserActions={disableUserActions}
|
||||||
|
// user={user}
|
||||||
|
// userAuth={memberRole}
|
||||||
|
// />
|
||||||
|
// ) : displayFilters?.layout === "spreadsheet" ? (
|
||||||
|
// <SpreadsheetView
|
||||||
|
// handleIssueAction={handleIssueAction}
|
||||||
|
// openIssuesListModal={cycleId || moduleId ? openIssuesListModal : null}
|
||||||
|
// disableUserActions={disableUserActions}
|
||||||
|
// user={user}
|
||||||
|
// userAuth={memberRole}
|
||||||
|
// />
|
||||||
|
// ) : (
|
||||||
|
// displayFilters?.layout === "gantt_chart" && <GanttChartView disableUserActions={disableUserActions} />
|
||||||
|
// )}
|
||||||
|
// </>
|
||||||
|
// ) : router.pathname.includes("archived-issues") ? (
|
||||||
|
// <EmptyState
|
||||||
|
// title="Archived Issues will be shown here"
|
||||||
|
// description="All the issues that have been in the completed or canceled groups for the configured period of time can be viewed here."
|
||||||
|
// image={emptyIssueArchive}
|
||||||
|
// primaryButton={{
|
||||||
|
// text: "Go to Automation Settings",
|
||||||
|
// onClick: () => {
|
||||||
|
// router.push(`/${workspaceSlug}/projects/${projectId}/settings/automations`);
|
||||||
|
// },
|
||||||
|
// }}
|
||||||
|
// />
|
||||||
|
// ) : (
|
||||||
|
// <EmptyState
|
||||||
|
// title={emptyState.title}
|
||||||
|
// description={emptyState.description}
|
||||||
|
// image={emptyIssue}
|
||||||
|
// primaryButton={
|
||||||
|
// emptyState.primaryButton
|
||||||
|
// ? {
|
||||||
|
// icon: emptyState.primaryButton.icon,
|
||||||
|
// text: emptyState.primaryButton.text,
|
||||||
|
// onClick: emptyState.primaryButton.onClick,
|
||||||
|
// }
|
||||||
|
// : undefined
|
||||||
|
// }
|
||||||
|
// secondaryButton={emptyState.secondaryButton}
|
||||||
|
// />
|
||||||
|
// )
|
||||||
|
// ) : (
|
||||||
|
// <div className="flex h-full w-full items-center justify-center">
|
||||||
|
// <Spinner />
|
||||||
|
// </div>
|
||||||
|
// )}
|
||||||
|
// </DragDropContext>
|
||||||
|
<div className="relative w-full h-full overflow-auto">
|
||||||
|
<KanBanLayout />
|
||||||
</div>
|
</div>
|
||||||
)}
|
|
||||||
</StrictModeDroppable>
|
|
||||||
{groupedIssues ? (
|
|
||||||
!isEmpty ||
|
|
||||||
displayFilters?.layout === "kanban" ||
|
|
||||||
displayFilters?.layout === "calendar" ||
|
|
||||||
displayFilters?.layout === "gantt_chart" ? (
|
|
||||||
<>
|
|
||||||
{displayFilters?.layout === "list" ? (
|
|
||||||
<AllLists
|
|
||||||
states={states}
|
|
||||||
addIssueToGroup={addIssueToGroup}
|
|
||||||
handleIssueAction={handleIssueAction}
|
|
||||||
handleDraftIssueAction={handleDraftIssueAction}
|
|
||||||
openIssuesListModal={cycleId || moduleId ? openIssuesListModal : null}
|
|
||||||
removeIssue={removeIssue}
|
|
||||||
myIssueProjectId={myIssueProjectId}
|
|
||||||
handleMyIssueOpen={handleMyIssueOpen}
|
|
||||||
disableUserActions={disableUserActions}
|
|
||||||
disableAddIssueOption={disableAddIssueOption}
|
|
||||||
user={user}
|
|
||||||
userAuth={memberRole}
|
|
||||||
viewProps={viewProps}
|
|
||||||
/>
|
|
||||||
) : displayFilters?.layout === "kanban" ? (
|
|
||||||
<AllBoards
|
|
||||||
addIssueToGroup={addIssueToGroup}
|
|
||||||
disableUserActions={disableUserActions}
|
|
||||||
disableAddIssueOption={disableAddIssueOption}
|
|
||||||
dragDisabled={dragDisabled}
|
|
||||||
handleIssueAction={handleIssueAction}
|
|
||||||
handleDraftIssueAction={handleDraftIssueAction}
|
|
||||||
handleTrashBox={handleTrashBox}
|
|
||||||
openIssuesListModal={cycleId || moduleId ? openIssuesListModal : null}
|
|
||||||
myIssueProjectId={myIssueProjectId}
|
|
||||||
handleMyIssueOpen={handleMyIssueOpen}
|
|
||||||
removeIssue={removeIssue}
|
|
||||||
states={states}
|
|
||||||
user={user}
|
|
||||||
userAuth={memberRole}
|
|
||||||
viewProps={viewProps}
|
|
||||||
/>
|
|
||||||
) : displayFilters?.layout === "calendar" ? (
|
|
||||||
<CalendarView
|
|
||||||
handleIssueAction={handleIssueAction}
|
|
||||||
addIssueToDate={addIssueToDate}
|
|
||||||
disableUserActions={disableUserActions}
|
|
||||||
user={user}
|
|
||||||
userAuth={memberRole}
|
|
||||||
/>
|
|
||||||
) : displayFilters?.layout === "spreadsheet" ? (
|
|
||||||
<SpreadsheetView
|
|
||||||
handleIssueAction={handleIssueAction}
|
|
||||||
openIssuesListModal={cycleId || moduleId ? openIssuesListModal : null}
|
|
||||||
disableUserActions={disableUserActions}
|
|
||||||
user={user}
|
|
||||||
userAuth={memberRole}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
displayFilters?.layout === "gantt_chart" && <GanttChartView disableUserActions={disableUserActions} />
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
) : router.pathname.includes("archived-issues") ? (
|
|
||||||
<EmptyState
|
|
||||||
title="Archived Issues will be shown here"
|
|
||||||
description="All the issues that have been in the completed or canceled groups for the configured period of time can be viewed here."
|
|
||||||
image={emptyIssueArchive}
|
|
||||||
primaryButton={{
|
|
||||||
text: "Go to Automation Settings",
|
|
||||||
onClick: () => {
|
|
||||||
router.push(`/${workspaceSlug}/projects/${projectId}/settings/automations`);
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<EmptyState
|
|
||||||
title={emptyState.title}
|
|
||||||
description={emptyState.description}
|
|
||||||
image={emptyIssue}
|
|
||||||
primaryButton={
|
|
||||||
emptyState.primaryButton
|
|
||||||
? {
|
|
||||||
icon: emptyState.primaryButton.icon,
|
|
||||||
text: emptyState.primaryButton.text,
|
|
||||||
onClick: emptyState.primaryButton.onClick,
|
|
||||||
}
|
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
secondaryButton={emptyState.secondaryButton}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
) : (
|
|
||||||
<div className="flex h-full w-full items-center justify-center">
|
|
||||||
<Spinner />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</DragDropContext>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -43,7 +43,7 @@ export const FilterSubGroupBy = observer(() => {
|
|||||||
{ISSUE_GROUP_BY_OPTIONS.map((subGroupBy) => {
|
{ISSUE_GROUP_BY_OPTIONS.map((subGroupBy) => {
|
||||||
if (
|
if (
|
||||||
issueFilterStore.userDisplayFilters.group_by !== null &&
|
issueFilterStore.userDisplayFilters.group_by !== null &&
|
||||||
issueFilterStore.userDisplayFilters.sub_group_by === issueFilterStore.userDisplayFilters.group_by
|
subGroupBy.key === issueFilterStore.userDisplayFilters.group_by
|
||||||
)
|
)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
|
1
web/components/issues/issue-layouts/calandar/index.ts
Normal file
1
web/components/issues/issue-layouts/calandar/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from "./root";
|
17
web/components/issues/issue-layouts/calandar/root.tsx
Normal file
17
web/components/issues/issue-layouts/calandar/root.tsx
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import React from "react";
|
||||||
|
|
||||||
|
export interface ICalendarLayout {
|
||||||
|
issues: any;
|
||||||
|
handleDragDrop: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const CalendarLayout: React.FC<ICalendarLayout> = ({}) => {
|
||||||
|
console.log("kanaban layout");
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div>header</div>
|
||||||
|
<div>content</div>
|
||||||
|
<div>footer</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
2
web/components/issues/issue-layouts/index.ts
Normal file
2
web/components/issues/issue-layouts/index.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export * from "./kanban";
|
||||||
|
export * from "./calandar";
|
50
web/components/issues/issue-layouts/kanban/block.tsx
Normal file
50
web/components/issues/issue-layouts/kanban/block.tsx
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
// react beautiful dnd
|
||||||
|
import { Draggable } from "@hello-pangea/dnd";
|
||||||
|
|
||||||
|
interface IssueBlockProps {
|
||||||
|
sub_group_id: string;
|
||||||
|
columnId: string;
|
||||||
|
issues: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const IssueBlock = ({ sub_group_id, columnId, issues }: IssueBlockProps) => {
|
||||||
|
console.log();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{issues && issues.length > 0 ? (
|
||||||
|
<>
|
||||||
|
{issues.map((issue: any, index: any) => (
|
||||||
|
<Draggable
|
||||||
|
draggableId={`${sub_group_id}-${columnId}-${issue.id}`}
|
||||||
|
index={index}
|
||||||
|
key={`issue-blocks-${sub_group_id}-${columnId}-${issue.id}`}
|
||||||
|
>
|
||||||
|
{(provided: any, snapshot: any) => (
|
||||||
|
<div
|
||||||
|
key={issue.id}
|
||||||
|
className="p-1.5 hover:cursor-default"
|
||||||
|
{...provided.draggableProps}
|
||||||
|
{...provided.dragHandleProps}
|
||||||
|
ref={provided.innerRef}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className={`min-h-[106px] text-sm rounded p-2 px-3 shadow-custom-shadow-2xs space-y-[4px] border transition-all bg-custom-background-100 hover:cursor-grab ${
|
||||||
|
snapshot.isDragging ? `border-custom-primary-100` : `border-transparent`
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<div className="text-xs line-clamp-1 text-custom-text-300">ONE-{issue.sequence_id}</div>
|
||||||
|
<div className="line-clamp-2 h-[40px] text-sm font-medium text-custom-text-100">{issue.name}</div>
|
||||||
|
<div className="h-[22px]">Footer</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</Draggable>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<div>No issues are available.</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
151
web/components/issues/issue-layouts/kanban/default.tsx
Normal file
151
web/components/issues/issue-layouts/kanban/default.tsx
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
import React from "react";
|
||||||
|
// react beautiful dnd
|
||||||
|
import { Droppable } from "@hello-pangea/dnd";
|
||||||
|
// components
|
||||||
|
import { KanBanGroupByHeaderRoot } from "./headers/group-by-root";
|
||||||
|
import { IssueBlock } from "./block";
|
||||||
|
// constants
|
||||||
|
import { ISSUE_STATE_GROUPS, ISSUE_PRIORITIES } from "constants/issue";
|
||||||
|
// mobx
|
||||||
|
import { observer } from "mobx-react-lite";
|
||||||
|
// mobx
|
||||||
|
import { useMobxStore } from "lib/mobx/store-provider";
|
||||||
|
import { RootStore } from "store/root";
|
||||||
|
|
||||||
|
export interface IKanBan {
|
||||||
|
issues?: any;
|
||||||
|
handleIssues?: () => void;
|
||||||
|
handleDragDrop?: (result: any) => void | undefined;
|
||||||
|
sub_group_id?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const KanBan: React.FC<IKanBan> = observer(({ issues, sub_group_id = "null" }) => {
|
||||||
|
const { project: projectStore, issueFilter: issueFilterStore }: RootStore = useMobxStore();
|
||||||
|
|
||||||
|
const group_by: string | null = issueFilterStore?.userDisplayFilters?.group_by || null;
|
||||||
|
const sub_group_by: string | null = issueFilterStore?.userDisplayFilters?.sub_group_by || null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="relative w-full h-full">
|
||||||
|
{group_by && group_by === "state" && (
|
||||||
|
<div className="relative w-full h-full flex">
|
||||||
|
{projectStore?.projectStates &&
|
||||||
|
projectStore?.projectStates.length > 0 &&
|
||||||
|
projectStore?.projectStates.map((state) => (
|
||||||
|
<div className="flex-shrink-0 flex flex-col w-[340px]">
|
||||||
|
{sub_group_by === null && (
|
||||||
|
<div className="flex-shrink-0 w-full bg-custom-background-90 py-1 sticky top-0 z-[2]">
|
||||||
|
<KanBanGroupByHeaderRoot column_id={state?.id} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className="w-full h-full">
|
||||||
|
<Droppable droppableId={`${sub_group_id}-${state?.id}`}>
|
||||||
|
{(provided: any, snapshot: any) => (
|
||||||
|
<div
|
||||||
|
className={`w-full h-full relative transition-all ${
|
||||||
|
snapshot.isDraggingOver ? `bg-custom-background-80` : ``
|
||||||
|
}`}
|
||||||
|
{...provided.droppableProps}
|
||||||
|
ref={provided.innerRef}
|
||||||
|
>
|
||||||
|
{issues && (
|
||||||
|
<IssueBlock sub_group_id={sub_group_id} columnId={state?.id} issues={issues[state?.id]} />
|
||||||
|
)}
|
||||||
|
{provided.placeholder}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</Droppable>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{group_by && group_by === "state_detail.group" && (
|
||||||
|
<div className="relative w-full h-full flex">
|
||||||
|
{ISSUE_STATE_GROUPS &&
|
||||||
|
ISSUE_STATE_GROUPS.length > 0 &&
|
||||||
|
ISSUE_STATE_GROUPS.map((stateGroup) => (
|
||||||
|
<div className="flex-shrink-0 flex flex-col w-[300px] h-full">
|
||||||
|
{sub_group_by === null && (
|
||||||
|
<div className="flex-shrink-0 w-full">
|
||||||
|
<KanBanGroupByHeaderRoot column_id={stateGroup?.key} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="w-full h-full">content</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{group_by && group_by === "priority" && (
|
||||||
|
<div className="relative w-full h-full flex">
|
||||||
|
{ISSUE_PRIORITIES &&
|
||||||
|
ISSUE_PRIORITIES.length > 0 &&
|
||||||
|
ISSUE_PRIORITIES.map((priority) => (
|
||||||
|
<div className="flex-shrink-0 flex flex-col w-[300px] h-full">
|
||||||
|
{sub_group_by === null && (
|
||||||
|
<div className="flex-shrink-0 w-full">
|
||||||
|
<KanBanGroupByHeaderRoot column_id={priority?.key} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="w-full h-full">content</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{group_by && group_by === "labels" && (
|
||||||
|
<div className="relative w-full h-full flex">
|
||||||
|
{projectStore?.projectLabels &&
|
||||||
|
projectStore?.projectLabels.length > 0 &&
|
||||||
|
projectStore?.projectLabels.map((label) => (
|
||||||
|
<div className="flex-shrink-0 flex flex-col w-[300px] h-full">
|
||||||
|
{sub_group_by === null && (
|
||||||
|
<div className="flex-shrink-0 w-full">
|
||||||
|
<KanBanGroupByHeaderRoot column_id={label?.id} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="w-full h-full">content</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{group_by && group_by === "assignees" && (
|
||||||
|
<div className="relative w-full h-full flex">
|
||||||
|
{projectStore?.projectMembers &&
|
||||||
|
projectStore?.projectMembers.length > 0 &&
|
||||||
|
projectStore?.projectMembers.map((member) => (
|
||||||
|
<div className="flex-shrink-0 flex flex-col w-[300px] h-full">
|
||||||
|
{sub_group_by === null && (
|
||||||
|
<div className="flex-shrink-0 w-full">
|
||||||
|
<KanBanGroupByHeaderRoot column_id={member?.id} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="w-full h-full">content</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{group_by && group_by === "created_by" && (
|
||||||
|
<div className="relative w-full h-full flex">
|
||||||
|
{projectStore?.projectMembers &&
|
||||||
|
projectStore?.projectMembers.length > 0 &&
|
||||||
|
projectStore?.projectMembers.map((member) => (
|
||||||
|
<div className="flex-shrink-0 flex flex-col w-[300px] h-full">
|
||||||
|
{sub_group_by === null && (
|
||||||
|
<div className="flex-shrink-0 w-full">
|
||||||
|
<KanBanGroupByHeaderRoot column_id={member?.id} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="w-full h-full">content</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
@ -0,0 +1,19 @@
|
|||||||
|
// components
|
||||||
|
import { HeaderCard } from "./card";
|
||||||
|
// mobx
|
||||||
|
import { observer } from "mobx-react-lite";
|
||||||
|
// store
|
||||||
|
import { useMobxStore } from "lib/mobx/store-provider";
|
||||||
|
import { RootStore } from "store/root";
|
||||||
|
|
||||||
|
export interface IAssigneesHeader {
|
||||||
|
column_id: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const AssigneesHeader: React.FC<IAssigneesHeader> = observer(({ column_id }) => {
|
||||||
|
const { project: projectStore }: RootStore = useMobxStore();
|
||||||
|
|
||||||
|
const assignee = (column_id && projectStore?.getProjectMemberById(column_id)) ?? null;
|
||||||
|
|
||||||
|
return <>{assignee && <HeaderCard title={assignee?.member?.display_name || ""} />}</>;
|
||||||
|
});
|
39
web/components/issues/issue-layouts/kanban/headers/card.tsx
Normal file
39
web/components/issues/issue-layouts/kanban/headers/card.tsx
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import React from "react";
|
||||||
|
// lucide icons
|
||||||
|
import { Plus, Minimize2, Maximize2, Circle } from "lucide-react";
|
||||||
|
|
||||||
|
interface IHeaderCard {
|
||||||
|
icon?: React.ReactNode;
|
||||||
|
title: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const HeaderCard = ({ icon, title }: IHeaderCard) => {
|
||||||
|
const position = false;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={`flex-shrink-0 relative flex gap-0.5 rounded-sm ${
|
||||||
|
position
|
||||||
|
? `flex-col items-center w-[44px] border border-custom-border-100 bg-custom-background-80 shadow-custom-shadow-sm`
|
||||||
|
: `flex-row items-center w-full`
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<div className="flex-shrink-0 w-[26px] h-[26px] rounded-sm overflow-hidden flex justify-center items-center hover:bg-custom-background-80 cursor-pointer transition-all">
|
||||||
|
{icon ? icon : <Circle width={14} strokeWidth={2} />}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={`capitalize flex items-center gap-1 ${position ? `flex-col` : `flex-row w-full`}`}>
|
||||||
|
<div className={`font-medium line-clamp-1 ${position ? `vertical-lr` : ``}`}>{title}</div>
|
||||||
|
<div className="text-xs">(0)</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex-shrink-0 w-[26px] h-[26px] rounded-sm overflow-hidden flex justify-center items-center hover:bg-custom-background-80 cursor-pointer transition-all">
|
||||||
|
{position ? <Maximize2 width={14} strokeWidth={2} /> : <Minimize2 width={14} strokeWidth={2} />}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex-shrink-0 w-[26px] h-[26px] rounded-sm overflow-hidden flex justify-center items-center hover:bg-custom-background-80 cursor-pointer transition-all">
|
||||||
|
<Plus width={14} strokeWidth={2} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1,19 @@
|
|||||||
|
// components
|
||||||
|
import { HeaderCard } from "./card";
|
||||||
|
// mobx
|
||||||
|
import { observer } from "mobx-react-lite";
|
||||||
|
// store
|
||||||
|
import { useMobxStore } from "lib/mobx/store-provider";
|
||||||
|
import { RootStore } from "store/root";
|
||||||
|
|
||||||
|
export interface ICreatedByHeader {
|
||||||
|
column_id: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const CreatedByHeader: React.FC<ICreatedByHeader> = observer(({ column_id }) => {
|
||||||
|
const { project: projectStore }: RootStore = useMobxStore();
|
||||||
|
|
||||||
|
const createdBy = (column_id && projectStore?.getProjectMemberById(column_id)) ?? null;
|
||||||
|
|
||||||
|
return <>{createdBy && <HeaderCard title={createdBy?.member?.display_name || ""} />}</>;
|
||||||
|
});
|
@ -0,0 +1,32 @@
|
|||||||
|
// components
|
||||||
|
import { StateHeader } from "./state";
|
||||||
|
import { StateGroupHeader } from "./state-group";
|
||||||
|
import { AssigneesHeader } from "./assignee";
|
||||||
|
import { PriorityHeader } from "./priority";
|
||||||
|
import { LabelHeader } from "./label";
|
||||||
|
import { CreatedByHeader } from "./created_by";
|
||||||
|
// mobx
|
||||||
|
import { observer } from "mobx-react-lite";
|
||||||
|
// mobx
|
||||||
|
import { useMobxStore } from "lib/mobx/store-provider";
|
||||||
|
import { RootStore } from "store/root";
|
||||||
|
|
||||||
|
export interface IKanBanGroupByHeaderRoot {
|
||||||
|
column_id: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const KanBanGroupByHeaderRoot: React.FC<IKanBanGroupByHeaderRoot> = observer(({ column_id }) => {
|
||||||
|
const { issueFilter: issueFilterStore }: RootStore = useMobxStore();
|
||||||
|
const group_by: string | null = issueFilterStore?.userDisplayFilters?.group_by || null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{group_by && group_by === "state" && <StateHeader column_id={column_id} />}
|
||||||
|
{group_by && group_by === "state_detail.group" && <StateGroupHeader column_id={column_id} />}
|
||||||
|
{group_by && group_by === "priority" && <PriorityHeader column_id={column_id} />}
|
||||||
|
{group_by && group_by === "labels" && <LabelHeader column_id={column_id} />}
|
||||||
|
{group_by && group_by === "assignees" && <AssigneesHeader column_id={column_id} />}
|
||||||
|
{group_by && group_by === "created_by" && <CreatedByHeader column_id={column_id} />}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
});
|
19
web/components/issues/issue-layouts/kanban/headers/label.tsx
Normal file
19
web/components/issues/issue-layouts/kanban/headers/label.tsx
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
// components
|
||||||
|
import { HeaderCard } from "./card";
|
||||||
|
// mobx
|
||||||
|
import { observer } from "mobx-react-lite";
|
||||||
|
// store
|
||||||
|
import { useMobxStore } from "lib/mobx/store-provider";
|
||||||
|
import { RootStore } from "store/root";
|
||||||
|
|
||||||
|
export interface ILabelHeader {
|
||||||
|
column_id: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const LabelHeader: React.FC<ILabelHeader> = observer(({ column_id }) => {
|
||||||
|
const { project: projectStore }: RootStore = useMobxStore();
|
||||||
|
|
||||||
|
const label = (column_id && projectStore?.getProjectLabelById(column_id)) ?? null;
|
||||||
|
|
||||||
|
return <>{label && <HeaderCard title={label?.name || ""} />}</>;
|
||||||
|
});
|
@ -0,0 +1,22 @@
|
|||||||
|
import React from "react";
|
||||||
|
// components
|
||||||
|
import { HeaderCard } from "./card";
|
||||||
|
// constants
|
||||||
|
import { issuePriorityByKey } from "constants/issue";
|
||||||
|
// mobx
|
||||||
|
import { observer } from "mobx-react-lite";
|
||||||
|
// mobx
|
||||||
|
import { useMobxStore } from "lib/mobx/store-provider";
|
||||||
|
import { RootStore } from "store/root";
|
||||||
|
|
||||||
|
export interface IPriorityHeader {
|
||||||
|
column_id: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const PriorityHeader: React.FC<IPriorityHeader> = observer(({ column_id }) => {
|
||||||
|
const {}: RootStore = useMobxStore();
|
||||||
|
|
||||||
|
const stateGroup = column_id && issuePriorityByKey(column_id);
|
||||||
|
|
||||||
|
return <>{stateGroup && <HeaderCard title={stateGroup?.title || ""} />}</>;
|
||||||
|
});
|
@ -0,0 +1,23 @@
|
|||||||
|
import React from "react";
|
||||||
|
// components
|
||||||
|
import { HeaderCard } from "./card";
|
||||||
|
// constants
|
||||||
|
import { issueStateGroupByKey } from "constants/issue";
|
||||||
|
// mobx
|
||||||
|
import { observer } from "mobx-react-lite";
|
||||||
|
// mobx
|
||||||
|
import { useMobxStore } from "lib/mobx/store-provider";
|
||||||
|
import { RootStore } from "store/root";
|
||||||
|
|
||||||
|
export interface IStateGroupHeader {
|
||||||
|
column_id: string;
|
||||||
|
swimlanes?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const StateGroupHeader: React.FC<IStateGroupHeader> = observer(({ column_id }) => {
|
||||||
|
const {}: RootStore = useMobxStore();
|
||||||
|
|
||||||
|
const stateGroup = column_id && issueStateGroupByKey(column_id);
|
||||||
|
|
||||||
|
return <>{stateGroup && <HeaderCard title={stateGroup?.title || ""} />}</>;
|
||||||
|
});
|
19
web/components/issues/issue-layouts/kanban/headers/state.tsx
Normal file
19
web/components/issues/issue-layouts/kanban/headers/state.tsx
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
// components
|
||||||
|
import { HeaderCard } from "./card";
|
||||||
|
// mobx
|
||||||
|
import { observer } from "mobx-react-lite";
|
||||||
|
// store
|
||||||
|
import { useMobxStore } from "lib/mobx/store-provider";
|
||||||
|
import { RootStore } from "store/root";
|
||||||
|
|
||||||
|
export interface IStateHeader {
|
||||||
|
column_id: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const StateHeader: React.FC<IStateHeader> = observer(({ column_id }) => {
|
||||||
|
const { project: projectStore }: RootStore = useMobxStore();
|
||||||
|
|
||||||
|
const state = (column_id && projectStore?.getProjectStateById(column_id)) ?? null;
|
||||||
|
|
||||||
|
return <>{state && <HeaderCard title={state?.name || ""} />}</>;
|
||||||
|
});
|
@ -0,0 +1,32 @@
|
|||||||
|
// components
|
||||||
|
import { StateHeader } from "./state";
|
||||||
|
import { StateGroupHeader } from "./state-group";
|
||||||
|
import { AssigneesHeader } from "./assignee";
|
||||||
|
import { PriorityHeader } from "./priority";
|
||||||
|
import { LabelHeader } from "./label";
|
||||||
|
import { CreatedByHeader } from "./created_by";
|
||||||
|
// mobx
|
||||||
|
import { observer } from "mobx-react-lite";
|
||||||
|
// mobx
|
||||||
|
import { useMobxStore } from "lib/mobx/store-provider";
|
||||||
|
import { RootStore } from "store/root";
|
||||||
|
|
||||||
|
export interface IKanBanSubGroupByHeaderRoot {
|
||||||
|
column_id: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const KanBanSubGroupByHeaderRoot: React.FC<IKanBanSubGroupByHeaderRoot> = observer(({ column_id }) => {
|
||||||
|
const { issueFilter: issueFilterStore }: RootStore = useMobxStore();
|
||||||
|
const sub_group_by: string | null = issueFilterStore?.userDisplayFilters?.sub_group_by || null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{sub_group_by && sub_group_by === "state" && <StateHeader column_id={column_id} />}
|
||||||
|
{sub_group_by && sub_group_by === "state_detail.group" && <StateGroupHeader column_id={column_id} />}
|
||||||
|
{sub_group_by && sub_group_by === "priority" && <PriorityHeader column_id={column_id} />}
|
||||||
|
{sub_group_by && sub_group_by === "labels" && <LabelHeader column_id={column_id} />}
|
||||||
|
{sub_group_by && sub_group_by === "assignees" && <AssigneesHeader column_id={column_id} />}
|
||||||
|
{sub_group_by && sub_group_by === "created_by" && <CreatedByHeader column_id={column_id} />}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
});
|
1
web/components/issues/issue-layouts/kanban/index.ts
Normal file
1
web/components/issues/issue-layouts/kanban/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from "./root";
|
48
web/components/issues/issue-layouts/kanban/root.tsx
Normal file
48
web/components/issues/issue-layouts/kanban/root.tsx
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
import React from "react";
|
||||||
|
// react beautiful dnd
|
||||||
|
import { DragDropContext } from "@hello-pangea/dnd";
|
||||||
|
// mobx
|
||||||
|
import { observer } from "mobx-react-lite";
|
||||||
|
// store
|
||||||
|
import { useMobxStore } from "lib/mobx/store-provider";
|
||||||
|
import { RootStore } from "store/root";
|
||||||
|
import { KanBanSwimLanes } from "./swimlanes";
|
||||||
|
import { KanBan } from "./default";
|
||||||
|
|
||||||
|
export interface IKanBanLayout {
|
||||||
|
issues?: any;
|
||||||
|
handleIssues?: () => void;
|
||||||
|
handleDragDrop?: (result: any) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const KanBanLayout: React.FC<IKanBanLayout> = observer(({}) => {
|
||||||
|
const { issue: issueStore, issueFilter: issueFilterStore }: RootStore = useMobxStore();
|
||||||
|
const currentKanBanView: "swimlanes" | "default" = issueFilterStore?.userDisplayFilters?.sub_group_by
|
||||||
|
? "swimlanes"
|
||||||
|
: "default";
|
||||||
|
|
||||||
|
const issues = issueStore?.getIssues;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
console.log("result", result);
|
||||||
|
// issueKanBanViewStore?.handleDragDrop(result.source, result.destination);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={`relative min-w-full w-max min-h-full h-max bg-custom-background-90`}>
|
||||||
|
<DragDropContext onDragEnd={onDragEnd}>
|
||||||
|
{currentKanBanView === "default" ? <KanBan issues={issues} /> : <KanBanSwimLanes issues={issues} />}
|
||||||
|
</DragDropContext>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
103
web/components/issues/issue-layouts/kanban/swimlanes.tsx
Normal file
103
web/components/issues/issue-layouts/kanban/swimlanes.tsx
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
import React from "react";
|
||||||
|
// components
|
||||||
|
import { KanBanGroupByHeaderRoot } from "./headers/group-by-root";
|
||||||
|
import { KanBanSubGroupByHeaderRoot } from "./headers/sub-group-by-root";
|
||||||
|
import { KanBan } from "./default";
|
||||||
|
// constants
|
||||||
|
import { ISSUE_STATE_GROUPS, ISSUE_PRIORITIES } from "constants/issue";
|
||||||
|
// mobx
|
||||||
|
import { observer } from "mobx-react-lite";
|
||||||
|
// mobx
|
||||||
|
import { useMobxStore } from "lib/mobx/store-provider";
|
||||||
|
import { RootStore } from "store/root";
|
||||||
|
|
||||||
|
export interface IKanBanSwimLanes {
|
||||||
|
issues?: any;
|
||||||
|
handleIssues?: () => void;
|
||||||
|
handleDragDrop?: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const SubGroupSwimlaneHeader = ({ list, _key }: any) => (
|
||||||
|
<div className="relative w-full min-h-full h-max flex items-center">
|
||||||
|
{list &&
|
||||||
|
list.length > 0 &&
|
||||||
|
list.map((_list: any) => (
|
||||||
|
<div className="flex-shrink-0 flex flex-col w-[340px]">
|
||||||
|
<KanBanGroupByHeaderRoot column_id={_list?.[_key]} />
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
const SubGroupSwimlane = ({ issues, list, _key }: any) => (
|
||||||
|
<div className="relative w-full min-h-full h-max">
|
||||||
|
{list &&
|
||||||
|
list.length > 0 &&
|
||||||
|
list.map((_list: any) => (
|
||||||
|
<div className="flex-shrink-0 flex flex-col">
|
||||||
|
<div className="sticky top-[30px] w-full z-[1] bg-custom-background-90 flex items-center py-1">
|
||||||
|
<div className="flex-shrink-0 sticky left-0 bg-custom-background-90 pr-2">
|
||||||
|
<KanBanSubGroupByHeaderRoot column_id={_list?.[_key]} />
|
||||||
|
</div>
|
||||||
|
<div className="w-full border-b border-custom-border-400 border-dashed" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="relative">
|
||||||
|
<KanBan issues={issues} sub_group_id={_list?.[_key]} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
export const KanBanSwimLanes: React.FC<IKanBanSwimLanes> = observer(({ issues }) => {
|
||||||
|
const { project: projectStore, issueFilter: issueFilterStore }: RootStore = useMobxStore();
|
||||||
|
|
||||||
|
const group_by: string | null = issueFilterStore?.userDisplayFilters?.group_by || null;
|
||||||
|
const sub_group_by: string | null = issueFilterStore?.userDisplayFilters?.sub_group_by || null;
|
||||||
|
|
||||||
|
console.log("sub_group_by", sub_group_by);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="relative">
|
||||||
|
<div className="sticky top-0 z-[2] bg-custom-background-90 h-[30px]">
|
||||||
|
{group_by && group_by === "state" && <SubGroupSwimlaneHeader list={projectStore?.projectStates} _key={"id"} />}
|
||||||
|
{group_by && group_by === "state_detail.group" && (
|
||||||
|
<SubGroupSwimlaneHeader list={ISSUE_STATE_GROUPS} _key={"key"} />
|
||||||
|
)}
|
||||||
|
{group_by && group_by === "priority" && <SubGroupSwimlaneHeader list={ISSUE_PRIORITIES} _key={"key"} />}
|
||||||
|
{group_by && group_by === "labels" && <SubGroupSwimlaneHeader list={projectStore?.projectLabels} _key={"id"} />}
|
||||||
|
{group_by && group_by === "assignees" && (
|
||||||
|
<SubGroupSwimlaneHeader list={projectStore?.projectMembers} _key={"id"} />
|
||||||
|
)}
|
||||||
|
{group_by && group_by === "created_by" && (
|
||||||
|
<SubGroupSwimlaneHeader list={projectStore?.projectMembers} _key={"id"} />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{sub_group_by && sub_group_by === "state" && (
|
||||||
|
<SubGroupSwimlane issues={issues} list={projectStore?.projectStates} _key={"id"} />
|
||||||
|
)}
|
||||||
|
|
||||||
|
{sub_group_by && sub_group_by === "state_detail.group" && (
|
||||||
|
<SubGroupSwimlane issues={issues} list={ISSUE_STATE_GROUPS} _key={"key"} />
|
||||||
|
)}
|
||||||
|
|
||||||
|
{sub_group_by && sub_group_by === "priority" && (
|
||||||
|
<SubGroupSwimlane issues={issues} list={ISSUE_PRIORITIES} _key={"key"} />
|
||||||
|
)}
|
||||||
|
|
||||||
|
{sub_group_by && sub_group_by === "labels" && (
|
||||||
|
<SubGroupSwimlane issues={issues} list={projectStore?.projectLabels} _key={"id"} />
|
||||||
|
)}
|
||||||
|
|
||||||
|
{sub_group_by && sub_group_by === "assignees" && (
|
||||||
|
<SubGroupSwimlane issues={issues} list={projectStore?.projectMembers} _key={"id"} />
|
||||||
|
)}
|
||||||
|
|
||||||
|
{sub_group_by && sub_group_by === "created_by" && (
|
||||||
|
<SubGroupSwimlane issues={issues} list={projectStore?.projectMembers} _key={"id"} />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
@ -23,6 +23,8 @@ export const ISSUE_PRIORITIES: {
|
|||||||
{ key: "none", title: "None" },
|
{ key: "none", title: "None" },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export const issuePriorityByKey = (key: string) => ISSUE_PRIORITIES.find((item) => item.key === key) || null;
|
||||||
|
|
||||||
export const ISSUE_STATE_GROUPS: {
|
export const ISSUE_STATE_GROUPS: {
|
||||||
key: TStateGroups;
|
key: TStateGroups;
|
||||||
title: string;
|
title: string;
|
||||||
@ -34,6 +36,8 @@ export const ISSUE_STATE_GROUPS: {
|
|||||||
{ key: "cancelled", title: "Cancelled" },
|
{ key: "cancelled", title: "Cancelled" },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export const issueStateGroupByKey = (key: string) => ISSUE_STATE_GROUPS.find((item) => item.key === key) || null;
|
||||||
|
|
||||||
export const ISSUE_START_DATE_OPTIONS = [
|
export const ISSUE_START_DATE_OPTIONS = [
|
||||||
{ key: "last_week", title: "Last Week" },
|
{ key: "last_week", title: "Last Week" },
|
||||||
{ key: "2_weeks_from_now", title: "2 weeks from now" },
|
{ key: "2_weeks_from_now", title: "2 weeks from now" },
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
"@emotion/react": "^11.11.1",
|
"@emotion/react": "^11.11.1",
|
||||||
"@emotion/styled": "^11.11.0",
|
"@emotion/styled": "^11.11.0",
|
||||||
"@headlessui/react": "^1.7.3",
|
"@headlessui/react": "^1.7.3",
|
||||||
|
"@hello-pangea/dnd": "^16.3.0",
|
||||||
"@heroicons/react": "^2.0.12",
|
"@heroicons/react": "^2.0.12",
|
||||||
"@jitsu/nextjs": "^3.1.5",
|
"@jitsu/nextjs": "^3.1.5",
|
||||||
"@mui/icons-material": "^5.14.1",
|
"@mui/icons-material": "^5.14.1",
|
||||||
|
@ -1,11 +1,42 @@
|
|||||||
import { observable, action, computed, makeObservable, runInAction } from "mobx";
|
import { observable, action, computed, makeObservable, runInAction } from "mobx";
|
||||||
import { IIssue } from "types";
|
// store
|
||||||
import { RootStore } from "./root";
|
import { RootStore } from "./root";
|
||||||
|
// types
|
||||||
|
import { IIssue } from "types";
|
||||||
|
// services
|
||||||
|
import { IssueService } from "services/issue.service";
|
||||||
|
|
||||||
|
export type IIssueType = "grouped" | "groupWithSubGroups" | "ungrouped";
|
||||||
|
export type IIssueGroupedStructure = { [group_id: string]: IIssue[] };
|
||||||
|
export type IIssueGroupWithSubGroupsStructure = {
|
||||||
|
[group_id: string]: {
|
||||||
|
[sub_group_id: string]: IIssue[];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
export type IIssueUnGroupedStructure = IIssue[];
|
||||||
|
|
||||||
export interface IIssueStore {
|
export interface IIssueStore {
|
||||||
loader: boolean;
|
loader: boolean;
|
||||||
error: any | null;
|
error: any | null;
|
||||||
|
// issues
|
||||||
|
issues: {
|
||||||
|
[project_id: string]: {
|
||||||
|
grouped: IIssueGroupedStructure;
|
||||||
|
groupWithSubGroups: IIssueGroupWithSubGroupsStructure;
|
||||||
|
ungrouped: IIssueUnGroupedStructure;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
// computed
|
||||||
|
getIssueType: IIssueType | null;
|
||||||
|
getIssues: IIssueGroupedStructure | IIssueGroupWithSubGroupsStructure | IIssueUnGroupedStructure | null;
|
||||||
|
// action
|
||||||
|
fetchIssues: (workspaceSlug: string, projectId: string) => Promise<any>;
|
||||||
|
updateIssueStructure: (group_id: string | null, sub_group_id: string | null, issue: IIssue) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
class IssueStore implements IIssueStore {
|
||||||
|
loader: boolean = false;
|
||||||
|
error: any | null = null;
|
||||||
issues: {
|
issues: {
|
||||||
[project_id: string]: {
|
[project_id: string]: {
|
||||||
grouped: {
|
grouped: {
|
||||||
@ -18,30 +49,9 @@ export interface IIssueStore {
|
|||||||
};
|
};
|
||||||
ungrouped: IIssue[];
|
ungrouped: IIssue[];
|
||||||
};
|
};
|
||||||
};
|
|
||||||
|
|
||||||
addIssueToIssuesStore: (projectId: string, issue: IIssue) => void;
|
|
||||||
updateIssueInIssuesStore: (projectId: string, issue: IIssue) => void;
|
|
||||||
deleteIssueFromIssuesStore: (projectId: string, issueId: string) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
class IssueStore implements IIssueStore {
|
|
||||||
loader: boolean = false;
|
|
||||||
error: any | null = null;
|
|
||||||
issues: {
|
|
||||||
[project_id: string]: {
|
|
||||||
grouped: {
|
|
||||||
[issueId: string]: IIssue[];
|
|
||||||
};
|
|
||||||
groupWithSubGroups: {
|
|
||||||
[group_id: string]: {
|
|
||||||
[sub_group_id: string]: IIssue[];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
ungrouped: IIssue[];
|
|
||||||
};
|
|
||||||
} = {};
|
} = {};
|
||||||
|
// service
|
||||||
|
issueService;
|
||||||
rootStore;
|
rootStore;
|
||||||
|
|
||||||
constructor(_rootStore: RootStore) {
|
constructor(_rootStore: RootStore) {
|
||||||
@ -50,55 +60,115 @@ class IssueStore implements IIssueStore {
|
|||||||
loader: observable.ref,
|
loader: observable.ref,
|
||||||
error: observable.ref,
|
error: observable.ref,
|
||||||
issues: observable.ref,
|
issues: observable.ref,
|
||||||
|
// computed
|
||||||
addIssueToIssuesStore: action,
|
getIssueType: computed,
|
||||||
updateIssueInIssuesStore: action,
|
getIssues: computed,
|
||||||
deleteIssueFromIssuesStore: action,
|
// actions
|
||||||
|
fetchIssues: action,
|
||||||
|
updateIssueStructure: action,
|
||||||
});
|
});
|
||||||
this.rootStore = _rootStore;
|
this.rootStore = _rootStore;
|
||||||
|
this.issueService = new IssueService();
|
||||||
}
|
}
|
||||||
|
|
||||||
addIssueToIssuesStore = (projectId: string, issue: IIssue) => {
|
get getIssueType() {
|
||||||
runInAction(() => {
|
const groupedLayouts = ["kanban", "list"];
|
||||||
this.rootStore.issue.issues = {
|
const ungroupedLayouts = ["calendar", "spreadsheet", "gantt_chart"];
|
||||||
...this.rootStore.issue.issues,
|
|
||||||
[projectId]: {
|
const issueLayout = this.rootStore?.issueFilter?.userDisplayFilters?.layout || null;
|
||||||
...this.rootStore.issue.issues[projectId],
|
const issueSubGroup = this.rootStore?.issueFilter?.userDisplayFilters?.sub_group_by || null;
|
||||||
ungrouped: [...this.rootStore.issue.issues[projectId].ungrouped, issue],
|
if (!issueLayout) return null;
|
||||||
|
|
||||||
|
const _issueState = groupedLayouts.includes(issueLayout)
|
||||||
|
? issueSubGroup
|
||||||
|
? "groupWithSubGroups"
|
||||||
|
: "grouped"
|
||||||
|
: ungroupedLayouts.includes(issueLayout)
|
||||||
|
? "ungrouped"
|
||||||
|
: null;
|
||||||
|
|
||||||
|
return _issueState || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
get getIssues() {
|
||||||
|
const projectId: string | null = this.rootStore?.project?.projectId;
|
||||||
|
const issueType = this.getIssueType;
|
||||||
|
if (!projectId || !issueType) return null;
|
||||||
|
|
||||||
|
return this.issues?.[projectId]?.[issueType] || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateIssueStructure = async (group_id: string | null, sub_group_id: string | null, issue: IIssue) => {
|
||||||
|
const projectId: string | null = issue?.project;
|
||||||
|
const issueType: IIssueType | null = this.getIssueType;
|
||||||
|
if (!projectId || !issueType) return null;
|
||||||
|
|
||||||
|
let issues: IIssueGroupedStructure | IIssueGroupWithSubGroupsStructure | IIssueUnGroupedStructure | null =
|
||||||
|
this.getIssues;
|
||||||
|
if (!issues) return null;
|
||||||
|
|
||||||
|
if (issueType === "grouped" && group_id) {
|
||||||
|
issues = issues as IIssueGroupedStructure;
|
||||||
|
issues = {
|
||||||
|
...issues,
|
||||||
|
[group_id]: issues[group_id].map((i: IIssue) => (i?.id === issue?.id ? issue : i)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (issueType === "groupWithSubGroups" && group_id && sub_group_id) {
|
||||||
|
issues = issues as IIssueGroupWithSubGroupsStructure;
|
||||||
|
issues = {
|
||||||
|
...issues,
|
||||||
|
[sub_group_id]: {
|
||||||
|
...issues[sub_group_id],
|
||||||
|
[group_id]: issues[sub_group_id][group_id].map((i: IIssue) => (i?.id === issue?.id ? issue : i)),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
if (issueType === "ungrouped") {
|
||||||
|
issues = issues as IIssueUnGroupedStructure;
|
||||||
|
issues = issues.map((i: IIssue) => (i?.id === issue?.id ? issue : i));
|
||||||
|
}
|
||||||
|
|
||||||
|
runInAction(() => {
|
||||||
|
this.issues = { ...this.issues, [projectId]: { ...this.issues[projectId], [issueType]: issues } };
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
updateIssueInIssuesStore = (projectId: string, issue: IIssue) => {
|
fetchIssues = async (workspaceSlug: string, projectId: string) => {
|
||||||
const newUngroupedIssues = this.rootStore.issue.issues[projectId].ungrouped.map((i) => ({
|
try {
|
||||||
...i,
|
this.loader = true;
|
||||||
...(i.id === issue.id ? issue : {}),
|
this.error = null;
|
||||||
}));
|
|
||||||
|
|
||||||
runInAction(() => {
|
this.rootStore.workspace.setWorkspaceSlug(workspaceSlug);
|
||||||
this.rootStore.issue.issues = {
|
this.rootStore.project.setProjectId(projectId);
|
||||||
...this.rootStore.issue.issues,
|
|
||||||
|
// TODO: replace this once the issue filter is completed
|
||||||
|
const params = { group_by: "state", order_by: "-created_at" };
|
||||||
|
const issueResponse = await this.issueService.getIssuesWithParams(workspaceSlug, projectId, params);
|
||||||
|
|
||||||
|
const issueType = this.getIssueType;
|
||||||
|
if (issueType != null) {
|
||||||
|
const _issues = {
|
||||||
|
...this.issues,
|
||||||
[projectId]: {
|
[projectId]: {
|
||||||
...this.rootStore.issue.issues[projectId],
|
...this.issues[projectId],
|
||||||
ungrouped: newUngroupedIssues,
|
[issueType]: issueResponse,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
deleteIssueFromIssuesStore = (projectId: string, issueId: string) => {
|
|
||||||
const newUngroupedIssues = this.rootStore.issue.issues[projectId].ungrouped.filter((i) => i.id !== issueId);
|
|
||||||
|
|
||||||
runInAction(() => {
|
runInAction(() => {
|
||||||
this.rootStore.issue.issues = {
|
this.issues = _issues;
|
||||||
...this.rootStore.issue.issues,
|
this.loader = false;
|
||||||
[projectId]: {
|
this.error = null;
|
||||||
...this.rootStore.issue.issues[projectId],
|
|
||||||
ungrouped: newUngroupedIssues,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return issueResponse;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error: Fetching error in issues", error);
|
||||||
|
this.loader = false;
|
||||||
|
this.error = error;
|
||||||
|
return error;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,8 +105,49 @@ class IssueFilterStore implements IIssueFilterStore {
|
|||||||
this.issueService = new IssueService();
|
this.issueService = new IssueService();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
computedFilter = (filters: any, filteredParams: any) => {
|
||||||
|
const computedFilters: any = {};
|
||||||
|
Object.keys(filters).map((key) => {
|
||||||
|
if (filters[key] != undefined && filteredParams.includes(key))
|
||||||
|
computedFilters[key] =
|
||||||
|
typeof filters[key] === "string" || typeof filters[key] === "boolean" ? filters[key] : filters[key].join(",");
|
||||||
|
});
|
||||||
|
|
||||||
|
return computedFilters;
|
||||||
|
};
|
||||||
|
|
||||||
get appliedFilters(): TIssueParams[] | null {
|
get appliedFilters(): TIssueParams[] | null {
|
||||||
return handleIssueQueryParamsByLayout(this.userDisplayFilters.layout);
|
if (
|
||||||
|
!this.userFilters ||
|
||||||
|
Object.keys(this.userFilters).length === 0 ||
|
||||||
|
!this.userDisplayFilters ||
|
||||||
|
Object.keys(this.userDisplayFilters).length === 0
|
||||||
|
)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
let filteredRouteParams: any = {
|
||||||
|
priority: this.userFilters?.priority || undefined,
|
||||||
|
state_group: this.userFilters?.state_group || undefined,
|
||||||
|
state: this.userFilters?.state || undefined,
|
||||||
|
assignees: this.userFilters?.assignees || undefined,
|
||||||
|
created_by: this.userFilters?.created_by || undefined,
|
||||||
|
labels: this.userFilters?.labels || undefined,
|
||||||
|
start_date: this.userFilters?.start_date || undefined,
|
||||||
|
target_date: this.userFilters?.target_date || undefined,
|
||||||
|
group_by: this.userDisplayFilters?.group_by || "state",
|
||||||
|
order_by: this.userDisplayFilters?.order_by || "-created_at",
|
||||||
|
sub_group_by: this.userDisplayFilters?.sub_group_by || undefined,
|
||||||
|
type: this.userDisplayFilters?.type || undefined,
|
||||||
|
sub_issue: this.userDisplayFilters?.sub_issue || true,
|
||||||
|
show_empty_groups: this.userDisplayFilters?.show_empty_groups || true,
|
||||||
|
calendar_date_range: this.userDisplayFilters?.calendar_date_range || undefined,
|
||||||
|
start_target_date: this.userDisplayFilters?.start_target_date || true,
|
||||||
|
};
|
||||||
|
|
||||||
|
const filteredParams = handleIssueQueryParamsByLayout(this.userDisplayFilters.layout);
|
||||||
|
if (filteredParams) filteredRouteParams = this.computedFilter(filteredRouteParams, filteredParams);
|
||||||
|
|
||||||
|
return filteredRouteParams;
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchUserProjectFilters = async (workspaceSlug: string, projectId: string) => {
|
fetchUserProjectFilters = async (workspaceSlug: string, projectId: string) => {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { action, computed, makeObservable } from "mobx";
|
import { action, computed, makeObservable } from "mobx";
|
||||||
// types
|
// types
|
||||||
import { RootStore } from "./root";
|
import { RootStore } from "./root";
|
||||||
|
import { IIssueType } from "./issue";
|
||||||
|
|
||||||
export interface IIssueKanBanViewStore {
|
export interface IIssueKanBanViewStore {
|
||||||
handleDragDrop: (source: any, destination: any) => void;
|
handleDragDrop: (source: any, destination: any) => void;
|
||||||
@ -26,12 +27,10 @@ class IssueKanBanViewStore implements IIssueKanBanViewStore {
|
|||||||
|
|
||||||
get canUserDragDrop() {
|
get canUserDragDrop() {
|
||||||
if (
|
if (
|
||||||
this.rootStore?.issueFilters?.issueView &&
|
this.rootStore?.issueFilter?.userDisplayFilters?.group_by &&
|
||||||
this.rootStore?.issueFilters?.userFilters?.display_filters?.group_by &&
|
this.rootStore?.issueFilter?.userDisplayFilters?.order_by &&
|
||||||
this.rootStore?.issueFilters?.userFilters?.display_filters?.order_by &&
|
["state", "priority"].includes(this.rootStore?.issueFilter?.userDisplayFilters?.group_by) &&
|
||||||
!["my_issues"].includes(this.rootStore?.issueFilters?.issueView) &&
|
this.rootStore?.issueFilter?.userDisplayFilters?.order_by === "sort_order"
|
||||||
["state", "priority"].includes(this.rootStore?.issueFilters?.userFilters?.display_filters?.group_by) &&
|
|
||||||
this.rootStore?.issueFilters?.userFilters?.display_filters?.order_by === "sort_order"
|
|
||||||
) {
|
) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -45,26 +44,18 @@ class IssueKanBanViewStore implements IIssueKanBanViewStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleDragDrop = async (source: any, destination: any) => {
|
handleDragDrop = async (source: any, destination: any) => {
|
||||||
const workspaceId = this.rootStore?.issueFilters?.workspaceId;
|
const workspaceSlug = this.rootStore?.workspace?.workspaceSlug;
|
||||||
const projectId = this.rootStore?.issueFilters?.projectId;
|
const projectId = this.rootStore?.project?.projectId;
|
||||||
const issueView = this.rootStore?.issueFilters?.issueView;
|
const issueType: IIssueType | null = this.rootStore?.issue?.getIssueType;
|
||||||
const issueLayout = this.rootStore?.issueFilters?.userFilters?.display_filters?.layout;
|
const issueLayout = this.rootStore?.issueFilter?.userDisplayFilters?.layout || null;
|
||||||
|
|
||||||
const sortOrderDefaultValue = 10000;
|
const sortOrderDefaultValue = 10000;
|
||||||
|
|
||||||
if (
|
if (workspaceSlug && projectId && issueType && issueLayout === "kanban" && this.rootStore.issue.getIssues) {
|
||||||
this.rootStore?.issueView?.getIssues &&
|
const currentIssues: any = this.rootStore.issue.getIssues;
|
||||||
workspaceId &&
|
|
||||||
projectId &&
|
|
||||||
issueView &&
|
|
||||||
issueLayout &&
|
|
||||||
issueView != "my_issues"
|
|
||||||
) {
|
|
||||||
const projectSortedIssues: any =
|
|
||||||
this.rootStore?.issueView.issues?.[workspaceId]?.project_issues?.[projectId]?.[issueView]?.[issueLayout];
|
|
||||||
|
|
||||||
let updateIssue: any = {
|
let updateIssue: any = {
|
||||||
workspaceId: workspaceId,
|
workspaceSlug: workspaceSlug,
|
||||||
projectId: projectId,
|
projectId: projectId,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -73,7 +64,7 @@ class IssueKanBanViewStore implements IIssueKanBanViewStore {
|
|||||||
// vertical
|
// vertical
|
||||||
if (source.droppableId === destination.droppableId) {
|
if (source.droppableId === destination.droppableId) {
|
||||||
const _columnId = source.droppableId;
|
const _columnId = source.droppableId;
|
||||||
const _issues = projectSortedIssues[_columnId];
|
const _issues = currentIssues[_columnId];
|
||||||
|
|
||||||
// update the sort order
|
// update the sort order
|
||||||
if (destination.index === 0) {
|
if (destination.index === 0) {
|
||||||
@ -92,7 +83,7 @@ class IssueKanBanViewStore implements IIssueKanBanViewStore {
|
|||||||
_issues.splice(destination.index, 0, { ...removed, sort_order: updateIssue.sort_order });
|
_issues.splice(destination.index, 0, { ...removed, sort_order: updateIssue.sort_order });
|
||||||
updateIssue = { ...updateIssue, issueId: removed?.id };
|
updateIssue = { ...updateIssue, issueId: removed?.id };
|
||||||
|
|
||||||
projectSortedIssues[_columnId] = _issues;
|
currentIssues[_columnId] = _issues;
|
||||||
}
|
}
|
||||||
|
|
||||||
// horizontal
|
// horizontal
|
||||||
@ -100,8 +91,8 @@ class IssueKanBanViewStore implements IIssueKanBanViewStore {
|
|||||||
const _sourceColumnId = source.droppableId;
|
const _sourceColumnId = source.droppableId;
|
||||||
const _destinationColumnId = destination.droppableId;
|
const _destinationColumnId = destination.droppableId;
|
||||||
|
|
||||||
const _sourceIssues = projectSortedIssues[_sourceColumnId];
|
const _sourceIssues = currentIssues[_sourceColumnId];
|
||||||
const _destinationIssues = projectSortedIssues[_destinationColumnId];
|
const _destinationIssues = currentIssues[_destinationColumnId];
|
||||||
|
|
||||||
if (_destinationIssues.length > 0) {
|
if (_destinationIssues.length > 0) {
|
||||||
if (destination.index === 0) {
|
if (destination.index === 0) {
|
||||||
@ -142,8 +133,8 @@ class IssueKanBanViewStore implements IIssueKanBanViewStore {
|
|||||||
});
|
});
|
||||||
updateIssue = { ...updateIssue, issueId: removed?.id };
|
updateIssue = { ...updateIssue, issueId: removed?.id };
|
||||||
|
|
||||||
projectSortedIssues[_sourceColumnId] = _sourceIssues;
|
currentIssues[_sourceColumnId] = _sourceIssues;
|
||||||
projectSortedIssues[_destinationColumnId] = _destinationIssues;
|
currentIssues[_destinationColumnId] = _destinationIssues;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -155,29 +146,23 @@ class IssueKanBanViewStore implements IIssueKanBanViewStore {
|
|||||||
if (this.canUserDragDropHorizontally && source.droppableId != destination.droppableId) {
|
if (this.canUserDragDropHorizontally && source.droppableId != destination.droppableId) {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.rootStore.issueView.issues = {
|
this.rootStore.issue.issues = {
|
||||||
...this.rootStore?.issueView.issues,
|
...this.rootStore?.issue.issues,
|
||||||
[workspaceId]: {
|
|
||||||
...this.rootStore?.issueView.issues?.[workspaceId],
|
|
||||||
project_issues: {
|
|
||||||
...this.rootStore?.issueView.issues?.[workspaceId]?.project_issues,
|
|
||||||
[projectId]: {
|
[projectId]: {
|
||||||
...this.rootStore?.issueView.issues?.[workspaceId]?.project_issues?.[projectId],
|
...this.rootStore?.issue.issues?.[projectId],
|
||||||
[issueView]: {
|
[issueType]: {
|
||||||
...this.rootStore?.issueView.issues?.[workspaceId]?.project_issues?.[projectId]?.[issueView],
|
...this.rootStore?.issue.issues?.[projectId]?.[issueType],
|
||||||
[issueLayout]: projectSortedIssues,
|
[issueType]: currentIssues,
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
this.rootStore.issueDetail?.updateIssueAsync(
|
// this.rootStore.issueDetail?.updateIssueAsync(
|
||||||
updateIssue.workspaceId,
|
// updateIssue.workspaceSlug,
|
||||||
updateIssue.projectId,
|
// updateIssue.projectId,
|
||||||
updateIssue.issueId,
|
// updateIssue.issueId,
|
||||||
updateIssue
|
// updateIssue
|
||||||
);
|
// );
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -58,8 +58,8 @@
|
|||||||
--color-border-300: 212, 212, 212; /* strong border- 1 */
|
--color-border-300: 212, 212, 212; /* strong border- 1 */
|
||||||
--color-border-400: 185, 185, 185; /* strong border- 2 */
|
--color-border-400: 185, 185, 185; /* strong border- 2 */
|
||||||
|
|
||||||
--color-shadow-2xs: 0px 0px 1px 0px rgba(23, 23, 23, 0.06),
|
--color-shadow-2xs: 0px 0px 1px 0px rgba(23, 23, 23, 0.06), 0px 1px 2px 0px rgba(23, 23, 23, 0.06),
|
||||||
0px 1px 2px 0px rgba(23, 23, 23, 0.06), 0px 1px 2px 0px rgba(23, 23, 23, 0.14);
|
0px 1px 2px 0px rgba(23, 23, 23, 0.14);
|
||||||
--color-shadow-xs: 0px 1px 2px 0px rgba(0, 0, 0, 0.16), 0px 2px 4px 0px rgba(16, 24, 40, 0.12),
|
--color-shadow-xs: 0px 1px 2px 0px rgba(0, 0, 0, 0.16), 0px 2px 4px 0px rgba(16, 24, 40, 0.12),
|
||||||
0px 1px 8px -1px rgba(16, 24, 40, 0.1);
|
0px 1px 8px -1px rgba(16, 24, 40, 0.1);
|
||||||
--color-shadow-sm: 0px 1px 4px 0px rgba(0, 0, 0, 0.01), 0px 4px 8px 0px rgba(0, 0, 0, 0.02),
|
--color-shadow-sm: 0px 1px 4px 0px rgba(0, 0, 0, 0.01), 0px 4px 8px 0px rgba(0, 0, 0, 0.02),
|
||||||
@ -72,8 +72,8 @@
|
|||||||
0px 1px 24px 0px rgba(16, 24, 40, 0.12);
|
0px 1px 24px 0px rgba(16, 24, 40, 0.12);
|
||||||
--color-shadow-xl: 0px 0px 18px 0px rgba(0, 0, 0, 0.16), 0px 0px 24px 0px rgba(16, 24, 40, 0.16),
|
--color-shadow-xl: 0px 0px 18px 0px rgba(0, 0, 0, 0.16), 0px 0px 24px 0px rgba(16, 24, 40, 0.16),
|
||||||
0px 0px 52px 0px rgba(16, 24, 40, 0.16);
|
0px 0px 52px 0px rgba(16, 24, 40, 0.16);
|
||||||
--color-shadow-2xl: 0px 8px 16px 0px rgba(0, 0, 0, 0.12),
|
--color-shadow-2xl: 0px 8px 16px 0px rgba(0, 0, 0, 0.12), 0px 12px 24px 0px rgba(16, 24, 40, 0.12),
|
||||||
0px 12px 24px 0px rgba(16, 24, 40, 0.12), 0px 1px 32px 0px rgba(16, 24, 40, 0.12);
|
0px 1px 32px 0px rgba(16, 24, 40, 0.12);
|
||||||
--color-shadow-3xl: 0px 12px 24px 0px rgba(0, 0, 0, 0.12), 0px 16px 32px 0px rgba(0, 0, 0, 0.12),
|
--color-shadow-3xl: 0px 12px 24px 0px rgba(0, 0, 0, 0.12), 0px 16px 32px 0px rgba(0, 0, 0, 0.12),
|
||||||
0px 1px 48px 0px rgba(16, 24, 40, 0.12);
|
0px 1px 48px 0px rgba(16, 24, 40, 0.12);
|
||||||
|
|
||||||
@ -359,3 +359,8 @@ body {
|
|||||||
.disable-scroll {
|
.disable-scroll {
|
||||||
overflow: hidden !important;
|
overflow: hidden !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.vertical-lr {
|
||||||
|
-webkit-writing-mode: vertical-lr;
|
||||||
|
-ms-writing-mode: vertical-lr;
|
||||||
|
}
|
||||||
|
60
yarn.lock
60
yarn.lock
@ -926,6 +926,13 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
regenerator-runtime "^0.14.0"
|
regenerator-runtime "^0.14.0"
|
||||||
|
|
||||||
|
"@babel/runtime@^7.12.1", "@babel/runtime@^7.22.5":
|
||||||
|
version "7.23.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.1.tgz#72741dc4d413338a91dcb044a86f3c0bc402646d"
|
||||||
|
integrity sha512-hC2v6p8ZSI/W0HUzh3V8C5g+NwSKzKPtJwSpTjwl0o297GP9+ZLQSkdvHz46CM3LqyoXxq+5G9komY+eSqSO0g==
|
||||||
|
dependencies:
|
||||||
|
regenerator-runtime "^0.14.0"
|
||||||
|
|
||||||
"@babel/template@^7.22.15", "@babel/template@^7.22.5":
|
"@babel/template@^7.22.15", "@babel/template@^7.22.5":
|
||||||
version "7.22.15"
|
version "7.22.15"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.15.tgz#09576efc3830f0430f4548ef971dde1350ef2f38"
|
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.15.tgz#09576efc3830f0430f4548ef971dde1350ef2f38"
|
||||||
@ -1248,6 +1255,19 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
client-only "^0.0.1"
|
client-only "^0.0.1"
|
||||||
|
|
||||||
|
"@hello-pangea/dnd@^16.3.0":
|
||||||
|
version "16.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@hello-pangea/dnd/-/dnd-16.3.0.tgz#3776212f812df4e8e69c42831ec8ab7ff3a087d6"
|
||||||
|
integrity sha512-RYQ/K8shtJoyNPvFWz0gfXIK7HF3P3mL9UZFGMuHB0ljRSXVgMjVFI/FxcZmakMzw6tO7NflWLriwTNBow/4vw==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.22.5"
|
||||||
|
css-box-model "^1.2.1"
|
||||||
|
memoize-one "^6.0.0"
|
||||||
|
raf-schd "^4.0.3"
|
||||||
|
react-redux "^8.1.1"
|
||||||
|
redux "^4.2.1"
|
||||||
|
use-memo-one "^1.1.3"
|
||||||
|
|
||||||
"@heroicons/react@^2.0.12":
|
"@heroicons/react@^2.0.12":
|
||||||
version "2.0.18"
|
version "2.0.18"
|
||||||
resolved "https://registry.yarnpkg.com/@heroicons/react/-/react-2.0.18.tgz#f80301907c243df03c7e9fd76c0286e95361f7c1"
|
resolved "https://registry.yarnpkg.com/@heroicons/react/-/react-2.0.18.tgz#f80301907c243df03c7e9fd76c0286e95361f7c1"
|
||||||
@ -2467,6 +2487,14 @@
|
|||||||
"@types/react" "*"
|
"@types/react" "*"
|
||||||
hoist-non-react-statics "^3.3.0"
|
hoist-non-react-statics "^3.3.0"
|
||||||
|
|
||||||
|
"@types/hoist-non-react-statics@^3.3.1":
|
||||||
|
version "3.3.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#dc1e9ded53375d37603c479cc12c693b0878aa2a"
|
||||||
|
integrity sha512-YIQtIg4PKr7ZyqNPZObpxfHsHEmuB8dXCxd6qVcGuQVDK2bpsF7bYNnBJ4Nn7giuACZg+WewExgrtAJ3XnA4Xw==
|
||||||
|
dependencies:
|
||||||
|
"@types/react" "*"
|
||||||
|
hoist-non-react-statics "^3.3.0"
|
||||||
|
|
||||||
"@types/js-cookie@^3.0.2", "@types/js-cookie@^3.0.3":
|
"@types/js-cookie@^3.0.2", "@types/js-cookie@^3.0.3":
|
||||||
version "3.0.3"
|
version "3.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/@types/js-cookie/-/js-cookie-3.0.3.tgz#d6bfbbdd0c187354ca555213d1962f6d0691ff4e"
|
resolved "https://registry.yarnpkg.com/@types/js-cookie/-/js-cookie-3.0.3.tgz#d6bfbbdd0c187354ca555213d1962f6d0691ff4e"
|
||||||
@ -2712,6 +2740,11 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.8.tgz#bb197b9639aa1a04cf464a617fe800cccd92ad5c"
|
resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.8.tgz#bb197b9639aa1a04cf464a617fe800cccd92ad5c"
|
||||||
integrity sha512-d0XxK3YTObnWVp6rZuev3c49+j4Lo8g4L1ZRm9z5L0xpoZycUPshHgczK5gsUMaZOstjVYYi09p5gYvUtfChYw==
|
integrity sha512-d0XxK3YTObnWVp6rZuev3c49+j4Lo8g4L1ZRm9z5L0xpoZycUPshHgczK5gsUMaZOstjVYYi09p5gYvUtfChYw==
|
||||||
|
|
||||||
|
"@types/use-sync-external-store@^0.0.3":
|
||||||
|
version "0.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz#b6725d5f4af24ace33b36fafd295136e75509f43"
|
||||||
|
integrity sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==
|
||||||
|
|
||||||
"@types/uuid@^8.3.4":
|
"@types/uuid@^8.3.4":
|
||||||
version "8.3.4"
|
version "8.3.4"
|
||||||
resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.4.tgz#bd86a43617df0594787d38b735f55c805becf1bc"
|
resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.4.tgz#bd86a43617df0594787d38b735f55c805becf1bc"
|
||||||
@ -3517,7 +3550,7 @@ crypto-random-string@^2.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5"
|
resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5"
|
||||||
integrity sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==
|
integrity sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==
|
||||||
|
|
||||||
css-box-model@^1.2.0:
|
css-box-model@^1.2.0, css-box-model@^1.2.1:
|
||||||
version "1.2.1"
|
version "1.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/css-box-model/-/css-box-model-1.2.1.tgz#59951d3b81fd6b2074a62d49444415b0d2b4d7c1"
|
resolved "https://registry.yarnpkg.com/css-box-model/-/css-box-model-1.2.1.tgz#59951d3b81fd6b2074a62d49444415b0d2b4d7c1"
|
||||||
integrity sha512-a7Vr4Q/kd/aw96bnJG332W9V9LkJO69JRcaCYDUqjp6/z0w6VcZjgAcTbgFxEPfBgdnAwlh3iwu+hLopa+flJw==
|
integrity sha512-a7Vr4Q/kd/aw96bnJG332W9V9LkJO69JRcaCYDUqjp6/z0w6VcZjgAcTbgFxEPfBgdnAwlh3iwu+hLopa+flJw==
|
||||||
@ -5898,6 +5931,11 @@ memoize-one@^5.1.1:
|
|||||||
resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.2.1.tgz#8337aa3c4335581839ec01c3d594090cebe8f00e"
|
resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.2.1.tgz#8337aa3c4335581839ec01c3d594090cebe8f00e"
|
||||||
integrity sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==
|
integrity sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==
|
||||||
|
|
||||||
|
memoize-one@^6.0.0:
|
||||||
|
version "6.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-6.0.0.tgz#b2591b871ed82948aee4727dc6abceeeac8c1045"
|
||||||
|
integrity sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==
|
||||||
|
|
||||||
merge-stream@^2.0.0:
|
merge-stream@^2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60"
|
resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60"
|
||||||
@ -6951,7 +6989,7 @@ queue-tick@^1.0.1:
|
|||||||
resolved "https://registry.yarnpkg.com/queue-tick/-/queue-tick-1.0.1.tgz#f6f07ac82c1fd60f82e098b417a80e52f1f4c142"
|
resolved "https://registry.yarnpkg.com/queue-tick/-/queue-tick-1.0.1.tgz#f6f07ac82c1fd60f82e098b417a80e52f1f4c142"
|
||||||
integrity sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==
|
integrity sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==
|
||||||
|
|
||||||
raf-schd@^4.0.2:
|
raf-schd@^4.0.2, raf-schd@^4.0.3:
|
||||||
version "4.0.3"
|
version "4.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/raf-schd/-/raf-schd-4.0.3.tgz#5d6c34ef46f8b2a0e880a8fcdb743efc5bfdbc1a"
|
resolved "https://registry.yarnpkg.com/raf-schd/-/raf-schd-4.0.3.tgz#5d6c34ef46f8b2a0e880a8fcdb743efc5bfdbc1a"
|
||||||
integrity sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ==
|
integrity sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ==
|
||||||
@ -7144,6 +7182,18 @@ react-redux@^7.2.0:
|
|||||||
prop-types "^15.7.2"
|
prop-types "^15.7.2"
|
||||||
react-is "^17.0.2"
|
react-is "^17.0.2"
|
||||||
|
|
||||||
|
react-redux@^8.1.1:
|
||||||
|
version "8.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-8.1.2.tgz#9076bbc6b60f746659ad6d51cb05de9c5e1e9188"
|
||||||
|
integrity sha512-xJKYI189VwfsFc4CJvHqHlDrzyFTY/3vZACbE+rr/zQ34Xx1wQfB4OTOSeOSNrF6BDVe8OOdxIrAnMGXA3ggfw==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.12.1"
|
||||||
|
"@types/hoist-non-react-statics" "^3.3.1"
|
||||||
|
"@types/use-sync-external-store" "^0.0.3"
|
||||||
|
hoist-non-react-statics "^3.3.2"
|
||||||
|
react-is "^18.0.0"
|
||||||
|
use-sync-external-store "^1.0.0"
|
||||||
|
|
||||||
react-remove-scroll-bar@^2.3.3:
|
react-remove-scroll-bar@^2.3.3:
|
||||||
version "2.3.4"
|
version "2.3.4"
|
||||||
resolved "https://registry.yarnpkg.com/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.4.tgz#53e272d7a5cb8242990c7f144c44d8bd8ab5afd9"
|
resolved "https://registry.yarnpkg.com/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.4.tgz#53e272d7a5cb8242990c7f144c44d8bd8ab5afd9"
|
||||||
@ -7226,7 +7276,7 @@ readdirp@~3.6.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
picomatch "^2.2.1"
|
picomatch "^2.2.1"
|
||||||
|
|
||||||
redux@^4.0.0, redux@^4.0.4:
|
redux@^4.0.0, redux@^4.0.4, redux@^4.2.1:
|
||||||
version "4.2.1"
|
version "4.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/redux/-/redux-4.2.1.tgz#c08f4306826c49b5e9dc901dee0452ea8fce6197"
|
resolved "https://registry.yarnpkg.com/redux/-/redux-4.2.1.tgz#c08f4306826c49b5e9dc901dee0452ea8fce6197"
|
||||||
integrity sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==
|
integrity sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==
|
||||||
@ -8418,7 +8468,7 @@ use-debounce@^9.0.4:
|
|||||||
resolved "https://registry.yarnpkg.com/use-debounce/-/use-debounce-9.0.4.tgz#51d25d856fbdfeb537553972ce3943b897f1ac85"
|
resolved "https://registry.yarnpkg.com/use-debounce/-/use-debounce-9.0.4.tgz#51d25d856fbdfeb537553972ce3943b897f1ac85"
|
||||||
integrity sha512-6X8H/mikbrt0XE8e+JXRtZ8yYVvKkdYRfmIhWZYsP8rcNs9hk3APV8Ua2mFkKRLcJKVdnX2/Vwrmg2GWKUQEaQ==
|
integrity sha512-6X8H/mikbrt0XE8e+JXRtZ8yYVvKkdYRfmIhWZYsP8rcNs9hk3APV8Ua2mFkKRLcJKVdnX2/Vwrmg2GWKUQEaQ==
|
||||||
|
|
||||||
use-memo-one@^1.1.1:
|
use-memo-one@^1.1.1, use-memo-one@^1.1.3:
|
||||||
version "1.1.3"
|
version "1.1.3"
|
||||||
resolved "https://registry.yarnpkg.com/use-memo-one/-/use-memo-one-1.1.3.tgz#2fd2e43a2169eabc7496960ace8c79efef975e99"
|
resolved "https://registry.yarnpkg.com/use-memo-one/-/use-memo-one-1.1.3.tgz#2fd2e43a2169eabc7496960ace8c79efef975e99"
|
||||||
integrity sha512-g66/K7ZQGYrI6dy8GLpVcMsBp4s17xNkYJVSMvTEevGy3nDxHOfE6z8BVE22+5G5x7t3+bhzrlTDB7ObrEE0cQ==
|
integrity sha512-g66/K7ZQGYrI6dy8GLpVcMsBp4s17xNkYJVSMvTEevGy3nDxHOfE6z8BVE22+5G5x7t3+bhzrlTDB7ObrEE0cQ==
|
||||||
@ -8431,7 +8481,7 @@ use-sidecar@^1.1.2:
|
|||||||
detect-node-es "^1.1.0"
|
detect-node-es "^1.1.0"
|
||||||
tslib "^2.0.0"
|
tslib "^2.0.0"
|
||||||
|
|
||||||
use-sync-external-store@1.2.0, use-sync-external-store@^1.2.0:
|
use-sync-external-store@1.2.0, use-sync-external-store@^1.0.0, use-sync-external-store@^1.2.0:
|
||||||
version "1.2.0"
|
version "1.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a"
|
resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a"
|
||||||
integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==
|
integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==
|
||||||
|
Loading…
Reference in New Issue
Block a user