Merge branch 'fix/issues-layout-mobx' of github.com:makeplane/plane into fix/issues-layout-mobx

This commit is contained in:
sriramveeraghanta 2023-09-27 16:00:46 +05:30
commit ccf6bd4e32
26 changed files with 1010 additions and 231 deletions

View File

@ -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>
); );
}; };

View File

@ -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;

View File

@ -0,0 +1 @@
export * from "./root";

View 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>
);
};

View File

@ -0,0 +1,2 @@
export * from "./kanban";
export * from "./calandar";

View 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>
)}
</>
);
};

View 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>
);
});

View 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 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 || ""} />}</>;
});

View 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>
);
};

View 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 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 || ""} />}</>;
});

View File

@ -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} />}
</>
);
});

View 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 || ""} />}</>;
});

View File

@ -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 || ""} />}</>;
});

View File

@ -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 || ""} />}</>;
});

View 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 || ""} />}</>;
});

View File

@ -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} />}
</>
);
});

View File

@ -0,0 +1 @@
export * from "./root";

View 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>
);
});

View 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>
);
});

View File

@ -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" },

View File

@ -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",

View File

@ -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;
}
}; };
} }

View File

@ -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) => {

View File

@ -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
); // );
} }
}; };
} }

View File

@ -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;
}

View File

@ -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==