chore: updated the store for issues and issue filters

This commit is contained in:
gurusainath 2023-09-08 12:42:09 +05:30
parent 7c5936e463
commit b9a6a00470
17 changed files with 1253 additions and 321 deletions

View File

@ -22,22 +22,23 @@ export const IssueContent = ({ columnId, issues }: IssueContentProps) => {
{(provided: any, snapshot: any) => (
<div
key={issue.id}
className="p-2"
className="p-1.5 hover:cursor-default"
{...provided.draggableProps}
{...provided.dragHandleProps}
ref={provided.innerRef}
>
<div
className={`text-sm rounded p-2 px-3 bg-white shadow space-y-1 border border-transparent`}
className={`min-h-[106px] text-sm rounded p-2 px-3 shadow-md space-y-[4px] border transition-all hover:cursor-grab ${
snapshot.isDragging
? `border-blue-500 bg-blue-50`
: `border-transparent bg-white`
}`}
>
<div className="text-xs line-clamp-1 text-gray-500 font-medium">
<div className="text-xs line-clamp-1 text-gray-500">
ONE-{issue.sequence_id}-{issue.sort_order}
</div>
<div className="line-clamp-2 h-[42px]">
{issue.name} {issue.name} {issue.name} {issue.name} {issue.name} {issue.name}{" "}
{issue.name} {issue.name}
</div>
<div>Footer</div>
<div className="line-clamp-2 h-[40px] text-sm font-medium">{issue.name}</div>
<div className="h-[22px]">Footer</div>
</div>
</div>
)}

View File

@ -1 +0,0 @@
export const IssueFooter = () => <div>Footer</div>;

View File

@ -1 +1,22 @@
export const IssueHeader = () => <div>Header</div>;
// lucide icons
import { Plus } from "lucide-react";
export const IssueHeader = () => (
<div className="relative flex flex-colw-full h-full gap-1">
{/* default layout */}
<div className=" border border-red-500 flex-shrink-0 w-[24px] h-[24px] flex justify-center items-center">
I
</div>
<div className=" border border-red-500 line-clamp-1 font-medium">Kanban Issue Heading</div>
<div className=" border border-red-500 flex-shrink-0 w-[24px] h-[24px] flex justify-center items-center">
0
</div>
<div className=" border border-red-500 ml-auto flex-shrink-0 w-[24px] h-[24px] flex justify-center items-center cursor-pointer rounded-sm hover:bg-gray-100 transition-all">
M
</div>
<div className=" border border-red-500 flex-shrink-0 w-[24px] h-[24px] flex justify-center items-center cursor-pointer rounded-sm hover:bg-gray-100 transition-all text-blue-800">
<Plus size={16} strokeWidth={2} />
</div>
</div>
);

View File

@ -4,66 +4,75 @@ import { DragDropContext, Droppable } from "react-beautiful-dnd";
// components
import { IssueHeader } from "./header";
import { IssueContent } from "./content";
import { IssueFooter } from "./footer";
// mobx
import { observer } from "mobx-react-lite";
// mobx store
import { useMobxStore } from "lib/mobx/store-provider";
import { RootStore } from "store/root";
export const IssueRoot = observer(() => {
export const IssueKanBanViewRoot = observer(() => {
const store: RootStore = useMobxStore();
const { kanban: issueViewStore } = store;
const { issueView: issueViewStore } = store;
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);
};
return (
<div className="relative w-screen h-screen">
<div className="relative w-full h-full">
{issueViewStore.loader && issueViewStore?.issues === null ? (
<div>Loading...</div>
) : (
<div className="relative w-full h-full p-2">
<>
{issueViewStore?.getIssues && Object.keys(issueViewStore?.getIssues).length > 0 ? (
<div className="relative overflow-hidden overflow-x-auto w-full h-full flex">
<DragDropContext onDragEnd={onDragEnd}>
<DragDropContext onDragEnd={onDragEnd}>
<div className="relative w-full h-full overflow-hidden !overflow-x-scroll flex">
{Object.keys(issueViewStore?.getIssues).map((_issueStateKey: any) => (
<div
key={`${_issueStateKey}`}
className="flex-shrink-0 w-[380px] h-full relative flex flex-col"
>
<div className="flex-shrink-0 w-full p-2 py-1">
<div className="flex-shrink-0 w-full p-2">
<IssueHeader />
</div>
<div className="w-full h-full overflow-auto">
<Droppable droppableId={`${_issueStateKey}`}>
{(provided: any, snapshot: any) => (
<>
<div {...provided.droppableProps} ref={provided.innerRef}>
<IssueContent
columnId={_issueStateKey}
issues={issueViewStore?.getIssues[_issueStateKey]}
/>
</div>
{provided.placeholder}
</>
)}
</Droppable>
</div>
<div className="flex-shrink-0 w-full p-2 py-1">
<IssueFooter />
</div>
<Droppable droppableId={`${_issueStateKey}`}>
{(provided: any, snapshot: any) => (
<div
className={`w-full h-full relative transition-all py-1.5 overflow-hidden overflow-y-auto ${
snapshot.isDraggingOver ? `bg-orange-50` : `bg-gray-50`
}`}
{...provided.droppableProps}
ref={provided.innerRef}
>
{issueViewStore?.getIssues && (
<IssueContent
columnId={_issueStateKey}
issues={issueViewStore?.getIssues[_issueStateKey]}
/>
)}
{provided.placeholder}
</div>
)}
</Droppable>
</div>
))}
</DragDropContext>
</div>
</div>
</DragDropContext>
) : (
<div>No Issues are available</div>
)}
</div>
</>
)}
</div>
);

View File

@ -58,7 +58,7 @@
"js-cookie": "^3.0.1",
"lodash.debounce": "^4.0.8",
"lowlight": "^2.9.0",
"lucide-react": "^0.263.1",
"lucide-react": "^0.274.0",
"mobx": "^6.10.0",
"mobx-react-lite": "^4.0.3",
"next": "12.3.2",

View File

@ -2,28 +2,175 @@ import React from "react";
// swr
import useSWR from "swr";
// components
import { IssueRoot } from "components/issue-layouts/kanban";
import { IssueKanBanViewRoot } from "components/issue-layouts/kanban";
// mobx store
import { useMobxStore } from "lib/mobx/store-provider";
import { RootStore } from "store/root";
const KanbanViewRoot = () => {
const KanBanViewRoot = () => {
const workspaceSlug: string = "plane-demo";
const projectSlug: string = "5b0e3f6e-c9f1-444d-be22-a8c2706bcf54";
const projectSlug: string = "08d59d96-9dfb-40e5-aa30-ecc66319450f";
const moduleSlug: string = "05613afc-29ea-4fd8-a025-a3cdfed209d1";
const cycleSlug: string = "1f66a767-00d1-422c-8f8f-6925282b7249";
const viewSlug: string = "1f66a767-00d1-422c-8f8f-6925282b7249";
const store: RootStore = useMobxStore();
const { kanban: issueViewStore } = store;
const { issueView: issueViewStore } = store;
useSWR(`PROJECT_ISSUES_KANBAN_VIEW`, () => {
if (workspaceSlug && projectSlug)
issueViewStore.getIssuesAsync(workspaceSlug, projectSlug, "kanban");
});
React.useEffect(() => {
const init = async () => {
// my issues under a workspace
console.log("started--->");
// await issueViewStore.getMyIssuesAsync(workspaceSlug, "my_issues", "list");
// await issueViewStore.getMyIssuesAsync(workspaceSlug, "my_issues", "kanban");
// await issueViewStore.getMyIssuesAsync(workspaceSlug, "my_issues", "calendar");
// await issueViewStore.getMyIssuesAsync(workspaceSlug, "my_issues", "spreadsheet");
// await issueViewStore.getMyIssuesAsync(workspaceSlug, "my_issues", "gantt");
// project issues under and workspace and project
// await issueViewStore.getProjectIssuesAsync(workspaceSlug, projectSlug, "issues", "list");
await issueViewStore.getProjectIssuesAsync(workspaceSlug, projectSlug, "issues", "kanban");
// await issueViewStore.getProjectIssuesAsync(workspaceSlug, projectSlug, "issues", "calendar");
// await issueViewStore.getProjectIssuesAsync(
// workspaceSlug,
// projectSlug,
// "issues",
// "spreadsheet"
// );
// await issueViewStore.getProjectIssuesAsync(workspaceSlug, projectSlug, "issues", "gantt");
// module issues under and workspace and project
// await issueViewStore.getIssuesForModulesAsync(
// workspaceSlug,
// projectSlug,
// moduleSlug,
// "modules",
// "list"
// );
// await issueViewStore.getIssuesForModulesAsync(
// workspaceSlug,
// projectSlug,
// moduleSlug,
// "modules",
// "kanban"
// );
// await issueViewStore.getIssuesForModulesAsync(
// workspaceSlug,
// projectSlug,
// moduleSlug,
// "modules",
// "calendar"
// );
// await issueViewStore.getIssuesForModulesAsync(
// workspaceSlug,
// projectSlug,
// moduleSlug,
// "modules",
// "spreadsheet"
// );
// await issueViewStore.getIssuesForModulesAsync(
// workspaceSlug,
// projectSlug,
// moduleSlug,
// "modules",
// "gantt"
// );
// cycle issues under and workspace and project
// await issueViewStore.getIssuesForCyclesAsync(
// workspaceSlug,
// projectSlug,
// cycleSlug,
// "cycles",
// "list"
// );
// await issueViewStore.getIssuesForCyclesAsync(
// workspaceSlug,
// projectSlug,
// cycleSlug,
// "cycles",
// "kanban"
// );
// await issueViewStore.getIssuesForCyclesAsync(
// workspaceSlug,
// projectSlug,
// cycleSlug,
// "cycles",
// "calendar"
// );
// await issueViewStore.getIssuesForCyclesAsync(
// workspaceSlug,
// projectSlug,
// cycleSlug,
// "cycles",
// "spreadsheet"
// );
// await issueViewStore.getIssuesForCyclesAsync(
// workspaceSlug,
// projectSlug,
// cycleSlug,
// "cycles",
// "gantt"
// );
// cycle issues under and workspace and project
// await issueViewStore.getIssuesForViewsAsync(
// workspaceSlug,
// projectSlug,
// viewSlug,
// "views",
// "list"
// );
// await issueViewStore.getIssuesForViewsAsync(
// workspaceSlug,
// projectSlug,
// viewSlug,
// "views",
// "kanban"
// );
// await issueViewStore.getIssuesForViewsAsync(
// workspaceSlug,
// projectSlug,
// viewSlug,
// "views",
// "calendar"
// );
// await issueViewStore.getIssuesForViewsAsync(
// workspaceSlug,
// projectSlug,
// viewSlug,
// "views",
// "spreadsheet"
// );
// await issueViewStore.getIssuesForViewsAsync(
// workspaceSlug,
// projectSlug,
// viewSlug,
// "views",
// "gantt"
// );
console.log("ended--->");
};
init();
}, []);
return (
<div>
<IssueRoot />
<div className="w-screen min-h-[600px] h-screen">
<div className="w-full h-full relative flex flex-col overflow-hidden">
<div
className="flex-shrink-0 h-[60px] border-b border-gray-200"
// style={{ writingMode: "vertical-lr" }}
>
Filter Header
</div>
<div className="w-full h-full relative overflow-hidden">
<IssueKanBanViewRoot />
</div>
</div>
</div>
);
};
export default KanbanViewRoot;
export default KanBanViewRoot;

View File

@ -10,7 +10,7 @@ const { NEXT_PUBLIC_API_BASE_URL } = process.env;
const trackEvent =
process.env.NEXT_PUBLIC_TRACK_EVENTS === "true" || process.env.NEXT_PUBLIC_TRACK_EVENTS === "1";
class ProjectCycleServices extends APIService {
export class ProjectCycleServices extends APIService {
constructor() {
super(NEXT_PUBLIC_API_BASE_URL || "http://localhost:8000");
}

View File

@ -10,7 +10,7 @@ const { NEXT_PUBLIC_API_BASE_URL } = process.env;
const trackEvent =
process.env.NEXT_PUBLIC_TRACK_EVENTS === "true" || process.env.NEXT_PUBLIC_TRACK_EVENTS === "1";
class ProjectIssuesServices extends APIService {
export class ProjectIssuesServices extends APIService {
constructor() {
super(NEXT_PUBLIC_API_BASE_URL || "http://localhost:8000");
}

View File

@ -17,7 +17,7 @@ const { NEXT_PUBLIC_API_BASE_URL } = process.env;
const trackEvent =
process.env.NEXT_PUBLIC_TRACK_EVENTS === "true" || process.env.NEXT_PUBLIC_TRACK_EVENTS === "1";
class UserService extends APIService {
export class UserService extends APIService {
constructor() {
super(NEXT_PUBLIC_API_BASE_URL || "http://localhost:8000");
}

View File

@ -0,0 +1,205 @@
import { observable, action, computed, makeObservable, runInAction } from "mobx";
// types
import { RootStore } from "../root";
// services
import { ProjectIssuesServices } from "services/issues.service";
// types
import { TIssueLayouts } from "./issue_filters";
export interface IIssueViewStore {
loader: boolean;
error: any | null;
issues: { [key: string]: { [key: string]: any } } | null;
getIssuesAsync: (
workspaceId: string,
projectId: string,
view: TIssueLayouts | null
) => null | Promise<any>;
}
class IssueViewStore implements IIssueViewStore {
loader: boolean = false;
error: any | null = null;
issues: { [key: string]: { [key: string]: any } } | null = null;
// root store
rootStore;
// service
issueService;
constructor(_rootStore: RootStore) {
makeObservable(this, {
// observable
loader: observable,
error: observable,
issues: observable.ref,
// action
getIssuesAsync: action,
// computed
});
this.rootStore = _rootStore;
this.issueService = new ProjectIssuesServices();
}
// computed
get getIssues() {
if (
this.rootStore.issueFilters.projectId &&
this.issues != null &&
this.rootStore.issueFilters.issueView != null
)
return this.issues[this.rootStore.issueFilters.projectId][
this.rootStore.issueFilters.issueView
];
else return null;
}
// handling kanBan drag and drop events
// fetching issues
getIssuesAsync = async (workspaceId: string, projectId: string) => {
try {
this.loader = true;
this.error = null;
const filteredParams = this.rootStore.issueFilters.getComputedFilters(
workspaceId,
projectId,
"kanBan",
"issues"
);
const issuesResponse = await this.issueService.getIssuesWithParams(
workspaceId,
projectId,
filteredParams
);
if (issuesResponse) {
runInAction(() => {
this.issues = { ...this.issues, [projectId]: { issues: { ...issuesResponse } } };
this.loader = false;
this.error = null;
});
}
return issuesResponse;
} catch (error) {
console.warn("error", error);
this.loader = false;
this.error = null;
return error;
}
};
// fetching issues for modules
getIssuesForModulesAsync = async (workspaceId: string, projectId: string, moduleId: string) => {
try {
this.loader = true;
this.error = null;
const filteredParams = this.rootStore.issueFilters.getComputedFilters(
workspaceId,
projectId,
"kanBan",
"modules"
);
const issuesResponse = await this.issueService.getIssuesWithParams(
workspaceId,
projectId,
filteredParams
);
if (issuesResponse) {
runInAction(() => {
this.issues = { ...this.issues, [projectId]: { views: { ...issuesResponse } } };
this.loader = false;
this.error = null;
});
}
return issuesResponse;
} catch (error) {
console.warn("error", error);
this.loader = false;
this.error = null;
return error;
}
};
// fetching issues for cycles
getIssuesForModulesCycles = async (workspaceId: string, projectId: string, moduleId: string) => {
try {
this.loader = true;
this.error = null;
const filteredParams = this.rootStore.issueFilters.getComputedFilters(
workspaceId,
projectId,
"kanBan",
"cycles"
);
const issuesResponse = await this.issueService.getIssuesWithParams(
workspaceId,
projectId,
filteredParams
);
if (issuesResponse) {
runInAction(() => {
this.issues = { ...this.issues, [projectId]: { views: { ...issuesResponse } } };
this.loader = false;
this.error = null;
});
}
return issuesResponse;
} catch (error) {
console.warn("error", error);
this.loader = false;
this.error = null;
return error;
}
};
// fetching issues for views
getIssuesForViews = async (workspaceId: string, projectId: string) => {
try {
this.loader = true;
this.error = null;
const filteredParams = this.rootStore.issueFilters.getComputedFilters(
workspaceId,
projectId,
"kanBan",
"views"
);
const issuesResponse = await this.issueService.getIssuesWithParams(
workspaceId,
projectId,
filteredParams
);
if (issuesResponse) {
runInAction(() => {
this.issues = { ...this.issues, [projectId]: { views: { ...issuesResponse } } };
this.loader = false;
this.error = null;
});
}
return issuesResponse;
} catch (error) {
console.warn("error", error);
this.loader = false;
this.error = null;
return error;
}
};
}
export default IssueViewStore;

View File

@ -0,0 +1,454 @@
import { observable, action, computed, makeObservable, runInAction } from "mobx";
// types
import { RootStore } from "../root";
// services
import { UserService } from "services/user.service";
import { ProjectIssuesServices } from "services/issues.service";
import { ProjectIssuesServices as ProjectModuleServices } from "services/modules.service";
import { ProjectCycleServices } from "services/cycles.service";
// types
import { TIssueLayouts, TIssueViews } from "./issue_filters";
export interface IIssues {
[key: string]: any;
}
export interface IIssuesLayout {
list: IIssues;
kanban: IIssues;
calendar: IIssues;
spreadsheet: IIssues;
gantt: IIssues;
}
export interface IIssueState {
[key: string]: {
my_issues: IIssuesLayout;
project_issues: {
[key: string]: {
issues: IIssuesLayout;
cycles: {
[key: string]: IIssuesLayout;
};
modules: {
[key: string]: IIssuesLayout;
};
views: {
[key: string]: IIssuesLayout;
};
};
};
};
}
export interface IIssueViewStore {
loader: boolean;
error: any | null;
issues: IIssueState;
// computed
getIssues: IIssues | null | undefined;
// actions
getMyIssuesAsync: (
workspaceId: string,
_view: TIssueViews,
_layout: TIssueLayouts
) => null | Promise<any>;
getProjectIssuesAsync: (
workspaceId: string,
projectId: string,
_view: TIssueViews,
_layout: TIssueLayouts
) => null | Promise<any>;
getIssuesForModulesAsync: (
workspaceId: string,
projectId: string,
moduleId: string,
_view: TIssueViews,
_layout: TIssueLayouts
) => null | Promise<any>;
getIssuesForCyclesAsync: (
workspaceId: string,
projectId: string,
cycleId: string,
_view: TIssueViews,
_layout: TIssueLayouts
) => null | Promise<any>;
getIssuesForViewsAsync: (
workspaceId: string,
projectId: string,
viewId: string,
_view: TIssueViews,
_layout: TIssueLayouts
) => null | Promise<any>;
}
class IssueViewStore implements IIssueViewStore {
loader: boolean = false;
error: any | null = null;
issues: IIssueState = {};
// root store
rootStore;
// service
issueService;
userService;
modulesService;
cyclesService;
constructor(_rootStore: RootStore) {
makeObservable(this, {
// observable
loader: observable,
error: observable,
issues: observable.ref,
// action
getMyIssuesAsync: action,
getProjectIssuesAsync: action,
getIssuesForModulesAsync: action,
getIssuesForCyclesAsync: action,
getIssuesForViewsAsync: action,
// computed
getIssues: computed,
});
this.rootStore = _rootStore;
this.issueService = new ProjectIssuesServices();
this.userService = new UserService();
this.modulesService = new ProjectModuleServices();
this.cyclesService = new ProjectCycleServices();
}
// computed
get getIssues() {
if (this.issues != null) {
const currentView: TIssueViews | null = this.rootStore.issueFilters.issueView;
const currentLayout: TIssueLayouts | null = this.rootStore.issueFilters.issueLayout;
const currentWorkspaceId: string | null = this.rootStore.issueFilters.workspaceId;
const currentProjectId: string | null = this.rootStore.issueFilters.projectId;
const currentModuleId: string | null = this.rootStore.issueFilters.moduleId;
const currentCycleId: string | null = this.rootStore.issueFilters.cycleId;
const currentViewId: string | null = this.rootStore.issueFilters.viewId;
if (!currentView || !currentLayout || !currentWorkspaceId) return null;
if (currentView === "my_issues")
return this.issues?.[currentWorkspaceId]?.my_issues?.[currentLayout];
else if (currentView === "issues" && currentProjectId)
return this.issues?.[currentWorkspaceId]?.project_issues?.[currentProjectId]?.issues?.[
currentLayout
];
else if (currentView === "modules" && currentProjectId && currentModuleId)
return this.issues?.[currentWorkspaceId]?.project_issues?.[currentProjectId]?.modules?.[
currentModuleId
]?.[currentLayout];
else if (currentView === "cycles" && currentProjectId && currentCycleId)
return this.issues?.[currentWorkspaceId]?.project_issues?.[currentProjectId]?.cycles?.[
currentCycleId
]?.[currentLayout];
else if (currentView === "views" && currentProjectId && currentViewId)
return this.issues?.[currentWorkspaceId]?.project_issues?.[currentProjectId]?.views?.[
currentViewId
]?.[currentLayout];
}
}
// fetching my issues
getMyIssuesAsync = async (workspaceId: string, _view: TIssueViews, _layout: TIssueLayouts) => {
try {
this.loader = true;
this.error = null;
const filteredParams = this.rootStore.issueFilters.getComputedFilters(
workspaceId,
null,
null,
null,
null,
_view,
_layout
);
const issuesResponse = await this.userService.userIssues(workspaceId, filteredParams);
if (issuesResponse) {
const _issueResponse: any = {
...this.issues,
[workspaceId]: {
...this?.issues[workspaceId],
my_issues: {
...this?.issues[workspaceId]?.my_issues,
[_layout as string]: issuesResponse,
},
},
};
runInAction(() => {
this.issues = _issueResponse;
this.loader = false;
this.error = null;
});
}
return issuesResponse;
} catch (error) {
console.warn("error", error);
this.loader = false;
this.error = null;
return error;
}
};
// fetching project issues
getProjectIssuesAsync = async (
workspaceId: string,
projectId: string,
_view: TIssueViews,
_layout: TIssueLayouts
) => {
try {
this.loader = true;
this.error = null;
const filteredParams = this.rootStore.issueFilters.getComputedFilters(
workspaceId,
projectId,
null,
null,
null,
_view,
_layout
);
const issuesResponse = await this.issueService.getIssuesWithParams(
workspaceId,
projectId,
filteredParams
);
if (issuesResponse) {
const _issueResponse: any = {
...this.issues,
[workspaceId]: {
...this?.issues?.[workspaceId],
project_issues: {
...this?.issues?.[workspaceId]?.project_issues,
[projectId]: {
...this?.issues?.[workspaceId]?.project_issues?.[projectId],
issues: {
...this?.issues[workspaceId]?.project_issues?.[projectId]?.issues,
[_layout as string]: issuesResponse,
},
},
},
},
};
runInAction(() => {
this.issues = _issueResponse;
this.loader = false;
this.error = null;
});
}
return issuesResponse;
} catch (error) {
console.warn("error", error);
this.loader = false;
this.error = null;
return error;
}
};
// fetching project issues for modules
getIssuesForModulesAsync = async (
workspaceId: string,
projectId: string,
moduleId: string,
_view: TIssueViews,
_layout: TIssueLayouts
) => {
try {
this.loader = true;
this.error = null;
const filteredParams = this.rootStore.issueFilters.getComputedFilters(
workspaceId,
projectId,
moduleId,
null,
null,
_view,
_layout
);
const issuesResponse = await this.modulesService.getModuleIssuesWithParams(
workspaceId,
projectId,
moduleId,
filteredParams
);
if (issuesResponse) {
const _issueResponse: any = {
...this.issues,
[workspaceId]: {
...this?.issues?.[workspaceId],
project_issues: {
...this?.issues?.[workspaceId]?.project_issues,
[projectId]: {
...this?.issues?.[workspaceId]?.project_issues?.[projectId],
modules: {
...this?.issues[workspaceId]?.project_issues?.[projectId]?.modules,
[moduleId]: {
...this?.issues[workspaceId]?.project_issues?.[projectId]?.modules?.[moduleId],
[_layout as string]: issuesResponse,
},
},
},
},
},
};
runInAction(() => {
this.issues = _issueResponse;
this.loader = false;
this.error = null;
});
}
return issuesResponse;
} catch (error) {
console.warn("error", error);
this.loader = false;
this.error = null;
return error;
}
};
// fetching project issues for cycles
getIssuesForCyclesAsync = async (
workspaceId: string,
projectId: string,
cycleId: string,
_view: TIssueViews,
_layout: TIssueLayouts
) => {
try {
this.loader = true;
this.error = null;
const filteredParams = this.rootStore.issueFilters.getComputedFilters(
workspaceId,
projectId,
null,
cycleId,
null,
_view,
_layout
);
const issuesResponse = await this.cyclesService.getCycleIssuesWithParams(
workspaceId,
projectId,
cycleId,
filteredParams
);
if (issuesResponse) {
const _issueResponse: any = {
...this.issues,
[workspaceId]: {
...this?.issues?.[workspaceId],
project_issues: {
...this?.issues?.[workspaceId]?.project_issues,
[projectId]: {
...this?.issues?.[workspaceId]?.project_issues?.[projectId],
cycles: {
...this?.issues[workspaceId]?.project_issues?.[projectId]?.cycles,
[cycleId]: {
...this?.issues[workspaceId]?.project_issues?.[projectId]?.cycles?.[cycleId],
[_layout as string]: issuesResponse,
},
},
},
},
},
};
runInAction(() => {
this.issues = _issueResponse;
this.loader = false;
this.error = null;
});
}
return issuesResponse;
} catch (error) {
console.warn("error", error);
this.loader = false;
this.error = null;
return error;
}
};
// fetching project issues for views
getIssuesForViewsAsync = async (
workspaceId: string,
projectId: string,
viewId: string,
_view: TIssueViews,
_layout: TIssueLayouts
) => {
try {
this.loader = true;
this.error = null;
const filteredParams = this.rootStore.issueFilters.getComputedFilters(
workspaceId,
projectId,
null,
null,
viewId,
_view,
_layout
);
const issuesResponse = await this.issueService.getIssuesWithParams(
workspaceId,
projectId,
filteredParams
);
if (issuesResponse) {
const _issueResponse: any = {
...this.issues,
[workspaceId]: {
...this?.issues?.[workspaceId],
project_issues: {
...this?.issues?.[workspaceId]?.project_issues,
[projectId]: {
...this?.issues?.[workspaceId]?.project_issues?.[projectId],
views: {
...this?.issues[workspaceId]?.project_issues?.[projectId]?.views,
[viewId]: {
...this?.issues[workspaceId]?.project_issues?.[projectId]?.views?.[viewId],
[_layout as string]: issuesResponse,
},
},
},
},
},
};
runInAction(() => {
this.issues = _issueResponse;
this.loader = false;
this.error = null;
});
}
return issuesResponse;
} catch (error) {
console.warn("error", error);
this.loader = false;
this.error = null;
return error;
}
};
}
export default IssueViewStore;

View File

@ -1,154 +0,0 @@
import { observable, action, computed, makeObservable, runInAction } from "mobx";
// types
import { RootStore } from "../root";
// services
import {} from "services/issues.service";
export type TIssueViews = "list" | "kanban" | "calendar" | "spreadsheet" | "gantt";
export interface IIssueFilterStore {
loader: boolean;
error: any | null;
// current issue view
workspaceId: string | null;
projectId: string | null;
issueView: string | null;
// filters
priority?: null;
state?: null;
assignees?: null;
createdBy?: null;
labels?: null;
startDate?: null;
dueDate?: null;
userSelectedParams?: {
assignees: undefined | string;
created_by: undefined | string;
group_by: undefined | string;
labels: undefined | string;
order_by: undefined | string;
priority: undefined | string;
start_date: undefined | string;
state: undefined | string;
sub_issue: boolean;
target_date: undefined | string;
type: undefined | string;
};
// display properties
displayProperties?: {
assignee: boolean;
attachment_count: boolean;
created_on: boolean;
due_date: boolean;
estimate: boolean;
key: boolean;
labels: boolean;
link: boolean;
priority: boolean;
start_date: boolean;
state: boolean;
sub_issue_count: boolean;
updated_on: boolean;
};
// extra's
showEmptyGroups?: boolean;
// actions
getProjectIssueFilterProperties: () => any | Promise<any>;
getProjectIssueDisplayProperties: () => any | Promise<any>;
getProjectMembers: () => any | Promise<any>;
getProjectStates: () => any | Promise<any>;
getProjectLabels: () => any | Promise<any>;
}
class IssueFilterStore implements IIssueFilterStore {
loader: boolean = false;
error: any | null = null;
workspaceId: string | null = null;
projectId: string | null = null;
issueView: string | null = null;
// root store
rootStore;
// service
projectPublishService = null;
constructor(_rootStore: RootStore) {
makeObservable(this, {
// observable
loader: observable,
error: observable,
workspaceId: observable,
projectId: observable,
// action
getProjectIssueFilterProperties: action,
getProjectIssueDisplayProperties: action,
getProjectMembers: action,
getProjectStates: action,
getProjectLabels: action,
// computed
});
this.rootStore = _rootStore;
this.projectPublishService = null;
}
// computed functions starts
getComputedFilters = (
_workspaceId: string,
_projectId: string,
_view: TIssueViews | null = "kanban"
) => {
this.workspaceId = _workspaceId;
this.projectId = _projectId;
this.issueView = _view;
let filteredRouteParams = {
assignees: undefined, // ['user_id', 'user_id']
state: undefined, // ['state_id', 'state_id']
priority: undefined, // ['low', 'high', 'medium', 'urgent', 'null']
type: undefined, // 'active' (started, un_started) || 'backlog' || 'null' (all_the_issues)
labels: undefined, // ['label_id', 'label_id']
created_by: undefined, // ['user_id', 'user_id']
start_date: undefined, // ['yyyy-mm-dd:after/before', 'yyyy-mm-dd:after/before']
target_date: undefined, // [yyyy-mm-dd:after, yyyy-mm-dd:before]
order_by: "-created_at", // TIssueOrderByOptions
group_by: "state", // TIssueGroupByOptions
sub_issue: true, // true for all other views except spreadsheet
};
if (_view === "list") filteredRouteParams = { ...filteredRouteParams };
if (_view === "kanban") filteredRouteParams = { ...filteredRouteParams };
if (_view === "calendar") filteredRouteParams = { ...filteredRouteParams };
if (_view === "spreadsheet") filteredRouteParams = { ...filteredRouteParams };
if (_view === "gantt") filteredRouteParams = { ...filteredRouteParams };
return filteredRouteParams;
};
// computed functions ends
// fetching current user project issue filter and display settings
getProjectIssueFilterProperties = () => {};
// fetching display properties
getProjectIssueDisplayProperties = () => {};
// fetching project members
getProjectMembers = () => {};
// fetching project state <-> groups
getProjectStates = () => {};
// fetching project labels
getProjectLabels = () => {};
}
export default IssueFilterStore;

View File

@ -1,25 +1,25 @@
export const filtersPriority: { key: string; title: string }[] = [
{ key: "", title: "Urgent" },
{ key: "", title: "High" },
{ key: "", title: "Medium" },
{ key: "", title: "Low" },
{ key: "", title: "None" },
{ key: "urgent", title: "Urgent" },
{ key: "high", title: "High" },
{ key: "medium", title: "Medium" },
{ key: "low", title: "Low" },
{ key: "null", title: "None" },
];
export const filtersStartDate = [
{ key: "", title: "Last Week" },
{ key: "", title: "2 weeks from now" },
{ key: "", title: "1 month from now" },
{ key: "", title: "2 months from now" },
{ key: "", title: "Custom" },
{ key: "last_week", title: "Last Week" },
{ key: "2_weeks_from_now", title: "2 weeks from now" },
{ key: "1_month_from_now", title: "1 month from now" },
{ key: "2_months_from_now", title: "2 months from now" },
{ key: "custom", title: "Custom" },
];
export const filtersDueDate = [
{ key: "", title: "Last Week" },
{ key: "", title: "2 weeks from now" },
{ key: "", title: "1 month from now" },
{ key: "", title: "2 months from now" },
{ key: "", title: "Custom" },
{ key: "last_week", title: "Last Week" },
{ key: "2_weeks_from_now", title: "2 weeks from now" },
{ key: "1_month_from_now", title: "1 month from now" },
{ key: "2_months_from_now", title: "2 months from now" },
{ key: "custom", title: "Custom" },
];
export const displayPropertyGroupBy = [

View File

@ -0,0 +1,330 @@
import { observable, action, computed, makeObservable, runInAction } from "mobx";
// types
import { RootStore } from "../root";
// services
import {} from "services/issues.service";
// default data
import {
filtersPriority,
filtersStartDate,
filtersDueDate,
displayPropertyGroupBy,
displayPropertyOrderBy,
displayPropertyIssueType,
displayProperties,
} from "./issue_data";
export type TIssueViews = "my_issues" | "issues" | "modules" | "views" | "cycles";
export type TIssueLayouts = "list" | "kanban" | "calendar" | "spreadsheet" | "gantt";
export interface IIssueFilterStore {
loader: boolean;
error: any | null;
// current workspace and project id
workspaceId: string | null;
projectId: string | null;
moduleId: string | null;
cycleId: string | null;
viewId: string | null;
// current issue layout:TIssueLayouts and view:TIssueViews
issueView: TIssueViews | null;
issueLayout: TIssueLayouts | null;
// filters
// static filters data
priority: { key: string; title: string }[];
startDate: { key: string; title: string }[];
dueDate: { key: string; title: string }[];
issueType: { key: string; title: string }[];
// static display filters data
groupBy: { key: string; title: string }[];
orderBy: { key: string; title: string }[];
// dynamic filters data
state: { [key: string]: { key: string; title: string }[] } | null;
members: { [key: string]: { key: string; title: string }[] } | null; // members are used for both assignees and crated_by
labels: { [key: string]: { key: string; title: string }[] } | null;
userSelectedFilters: {
priority: string[] | null;
state: string[] | null;
assignees: string[] | null;
created_by: string[] | null;
labels: string[] | null;
start_date: string[] | null;
target_date: string[] | null;
type: string;
};
userSelectedDisplayFilters?: {
group_by: undefined | string;
order_by: undefined | string;
sub_issue: boolean;
showEmptyGroups: boolean;
};
// static display properties data
displayProperties?: { key: string; title: string }[] | null;
userSelectedDisplayProperties?: {
assignee: boolean;
attachment_count: boolean;
created_on: boolean;
due_date: boolean;
estimate: boolean;
key: boolean;
labels: boolean;
link: boolean;
priority: boolean;
start_date: boolean;
state: boolean;
sub_issue_count: boolean;
updated_on: boolean;
};
// actions
getProjectIssueFilterProperties: () => any | Promise<any>;
getProjectIssueDisplayProperties: () => any | Promise<any>;
getProjectMembers: () => any | Promise<any>;
getProjectStates: () => any | Promise<any>;
getProjectLabels: () => any | Promise<any>;
}
class IssueFilterStore implements IIssueFilterStore {
loader: boolean = false;
error: any | null = null;
workspaceId: string | null = null;
projectId: string | null = null;
moduleId: string | null = null;
cycleId: string | null = null;
viewId: string | null = null;
issueLayout: TIssueLayouts | null = null;
issueView: TIssueViews | null = null;
priority: { key: string; title: string }[] = filtersPriority;
startDate: { key: string; title: string }[] = filtersStartDate;
dueDate: { key: string; title: string }[] = filtersDueDate;
issueType: { key: string; title: string }[] = displayPropertyIssueType;
// static display filters data
groupBy: { key: string; title: string }[] = displayPropertyGroupBy;
orderBy: { key: string; title: string }[] = displayPropertyOrderBy;
state: { [key: string]: { key: string; title: string }[] } | null = null;
members: { [key: string]: { key: string; title: string }[] } | null = null;
labels: { [key: string]: { key: string; title: string }[] } | null = null;
userSelectedFilters: {
priority: string[] | null;
state: string[] | null;
assignees: string[] | null;
created_by: string[] | null;
labels: string[] | null;
start_date: string[] | null;
target_date: string[] | null;
type: string;
} = {
priority: null,
state: null,
assignees: null,
created_by: null,
labels: null,
start_date: null,
target_date: null,
type: "all",
};
// root store
rootStore;
// service
projectPublishService = null;
constructor(_rootStore: RootStore) {
makeObservable(this, {
// observable
loader: observable,
error: observable,
workspaceId: observable,
projectId: observable,
moduleId: observable,
cycleId: observable,
viewId: observable,
issueLayout: observable,
issueView: observable,
state: observable.ref,
members: observable.ref,
labels: observable.ref,
userSelectedFilters: observable.ref,
// action
setWorkspaceId: action,
setProjectId: action,
setModuleId: action,
setCycleId: action,
setViewId: action,
setIssueLayout: action,
setIssueView: action,
getProjectIssueFilterProperties: action,
getProjectIssueDisplayProperties: action,
getProjectMembers: action,
getProjectStates: action,
getProjectLabels: action,
// computed
});
this.rootStore = _rootStore;
this.projectPublishService = null;
}
setWorkspaceId = (_workspaceId: string | null) => (this.workspaceId = _workspaceId);
setProjectId = (_projectId: string | null) => (this.projectId = _projectId);
setModuleId = (_moduleId: string | null) => (this.moduleId = _moduleId);
setCycleId = (_cycleId: string | null) => (this.cycleId = _cycleId);
setViewId = (_viewId: string | null) => (this.viewId = _viewId);
setIssueLayout = (_layout: TIssueLayouts | null) => (this.issueLayout = _layout);
setIssueView = (_view: TIssueViews | null) => (this.issueView = _view);
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;
};
// computed functions starts
getComputedFilters = (
_workspaceId: string | null,
_projectId: string | null,
_moduleId: string | null,
_cycleId: string | null,
_viewId: string | null,
_issueView: TIssueViews,
_issueLayout: TIssueLayouts
) => {
this.setWorkspaceId(_workspaceId);
this.setProjectId(_projectId);
this.setModuleId(_moduleId);
this.setCycleId(_cycleId);
this.setViewId(_viewId);
this.setIssueView(_issueView);
this.setIssueLayout(_issueLayout);
let filteredRouteParams: any = {
priority: this.userSelectedFilters?.priority || undefined,
state: this.userSelectedFilters?.state || undefined,
assignees: this.userSelectedFilters?.assignees || undefined, // ['user_id', 'user_id']
created_by: this.userSelectedFilters?.created_by || undefined, // ['user_id', 'user_id']
labels: this.userSelectedFilters?.labels || undefined, // ['label_id', 'label_id']
start_date: this.userSelectedFilters?.start_date || undefined, // ['yyyy-mm-dd:after/before', 'yyyy-mm-dd:after/before']
target_date: this.userSelectedFilters?.target_date || undefined, // [yyyy-mm-dd:after, yyyy-mm-dd:before]
type: this.userSelectedFilters?.type || undefined, // 'active' (started, un_started) || 'backlog' || 'null' (all_the_issues)
group_by: "state", // TIssueGroupByOptions
order_by: "-created_at", // TIssueOrderByOptions
sub_issue: true, // true for all other views except spreadsheet
};
let filteredParams: any = {};
if (this.issueLayout === "list")
filteredParams = [
"priority",
"state",
"assignees",
"created_by",
"labels",
"start_date",
"target_date",
"type",
"group_by",
"order_by",
"sub_issue",
];
if (this.issueLayout === "kanban")
filteredParams = [
"priority",
"state",
"assignees",
"created_by",
"labels",
"start_date",
"target_date",
"type",
"group_by",
"order_by",
"sub_issue",
];
if (this.issueLayout === "calendar")
filteredParams = [
"priority",
"state",
"assignees",
"created_by",
"labels",
"start_date",
"target_date",
"type",
];
if (this.issueLayout === "spreadsheet")
filteredParams = [
"priority",
"state",
"assignees",
"created_by",
"labels",
"start_date",
"target_date",
"type",
];
if (this.issueLayout === "gantt")
filteredParams = [
"priority",
"state",
"assignees",
"created_by",
"labels",
"start_date",
"target_date",
"type",
"order_by",
"sub_issue_id",
];
filteredRouteParams = this.computedFilter(filteredRouteParams, filteredParams);
return filteredRouteParams;
};
// computed functions ends
// fetching current user project issue filter and display settings
getProjectIssueFilterProperties = () => {};
// fetching display properties
getProjectIssueDisplayProperties = () => {};
// fetching project members
getProjectMembers = () => {};
// fetching project state <-> groups
getProjectStates = () => {};
// fetching project labels
getProjectLabels = () => {};
}
export default IssueFilterStore;

View File

@ -1,92 +0,0 @@
import { observable, action, computed, makeObservable, runInAction } from "mobx";
// types
import { RootStore } from "../root";
// services
import { ProjectIssuesServices } from "services/issues.service";
// types
import { TIssueViews } from "./filters";
export interface IKanbanStore {
loader: boolean;
error: any | null;
issues: { [key: string]: any } | null;
getIssuesAsync: (
workspaceId: string,
projectId: string,
view: TIssueViews | null
) => null | Promise<any>;
}
class KanbanStore implements IKanbanStore {
loader: boolean = false;
error: any | null = null;
issues: { [key: string]: any } | null = null;
// root store
rootStore;
// service
issueService;
constructor(_rootStore: RootStore) {
makeObservable(this, {
// observable
loader: observable,
error: observable,
issues: observable.ref,
// action
getIssuesAsync: action,
// computed
});
this.rootStore = _rootStore;
this.issueService = new ProjectIssuesServices();
}
// computed
get getIssues() {
if (this.rootStore.issueFilters.projectId && this.issues != null)
return this.issues[this.rootStore.issueFilters.projectId];
else return null;
}
// fetching issues
getIssuesAsync = async (workspaceId: string, projectId: string, view: TIssueViews | null) => {
try {
this.loader = true;
this.error = null;
const filteredParams = this.rootStore.issueFilters.getComputedFilters(
workspaceId,
projectId,
view
);
const issuesResponse = await this.issueService.getIssuesWithParams(
workspaceId,
projectId,
filteredParams
);
if (issuesResponse) {
runInAction(() => {
this.issues = { ...this.issues, [projectId]: { ...issuesResponse } };
this.loader = false;
this.error = null;
});
}
return issuesResponse;
} catch (error) {
console.warn("error", error);
this.loader = false;
this.error = null;
return error;
}
};
// handle issue drag and drop
}
export default KanbanStore;

View File

@ -7,8 +7,8 @@ import ProjectStore, { IProjectStore } from "./project";
import ProjectPublishStore, { IProjectPublishStore } from "./project-publish";
import IssuesStore from "./issues";
// issues views and filters
import IssueFilterStore from "./issue-views/filters";
import KanbanStore from "./issue-views/kanban";
import IssueFilterStore from "./issue-views/issue_filters";
import IssueViewStore from "./issue-views/Issues";
enableStaticRendering(typeof window === "undefined");
@ -19,7 +19,7 @@ export class RootStore {
projectPublish: IProjectPublishStore;
issues: IssuesStore;
issueFilters: IssueFilterStore;
kanban: KanbanStore;
issueView: IssueViewStore;
constructor() {
this.user = new UserStore(this);
@ -28,6 +28,6 @@ export class RootStore {
this.projectPublish = new ProjectPublishStore(this);
this.issues = new IssuesStore(this);
this.issueFilters = new IssueFilterStore(this);
this.kanban = new KanbanStore(this);
this.issueView = new IssueViewStore(this);
}
}

View File

@ -5600,6 +5600,11 @@ lucide-react@^0.263.1:
resolved "https://registry.yarnpkg.com/lucide-react/-/lucide-react-0.263.1.tgz#a456ee0d171aa373929bd3ee20d6f9fb4429c301"
integrity sha512-keqxAx97PlaEN89PXZ6ki1N8nRjGWtDa4021GFYLNj0RgruM5odbpl8GHTExj0hhPq3sF6Up0gnxt6TSHu+ovw==
lucide-react@^0.274.0:
version "0.274.0"
resolved "https://registry.yarnpkg.com/lucide-react/-/lucide-react-0.274.0.tgz#d3b54dcb972b12f1292061448d61d422ef2e269d"
integrity sha512-qiWcojRXEwDiSimMX1+arnxha+ROJzZjJaVvCC0rsG6a9pUPjZePXSq7em4ZKMp0NDm1hyzPNkM7UaWC3LU2AA==
magic-string@^0.25.0, magic-string@^0.25.7:
version "0.25.9"
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.9.tgz#de7f9faf91ef8a1c91d02c2e5314c8277dbcdd1c"
@ -6623,7 +6628,14 @@ prosemirror-menu@^1.2.1:
prosemirror-history "^1.0.0"
prosemirror-state "^1.0.0"
prosemirror-model@^1.0.0, prosemirror-model@^1.16.0, prosemirror-model@^1.18.1, prosemirror-model@^1.19.0, prosemirror-model@^1.8.1:
prosemirror-model@^1.0.0, prosemirror-model@^1.16.0, prosemirror-model@^1.18.1, prosemirror-model@^1.8.1:
version "1.18.1"
resolved "https://registry.yarnpkg.com/prosemirror-model/-/prosemirror-model-1.18.1.tgz#1d5d6b6de7b983ee67a479dc607165fdef3935bd"
integrity sha512-IxSVBKAEMjD7s3n8cgtwMlxAXZrC7Mlag7zYsAKDndAqnDScvSmp/UdnRTV/B33lTCVU3CCm7dyAn/rVVD0mcw==
dependencies:
orderedmap "^2.0.0"
prosemirror-model@^1.19.0:
version "1.19.3"
resolved "https://registry.yarnpkg.com/prosemirror-model/-/prosemirror-model-1.19.3.tgz#f0d55285487fefd962d0ac695f716f4ec6705006"
integrity sha512-tgSnwN7BS7/UM0sSARcW+IQryx2vODKX4MI7xpqY2X+iaepJdKBPc7I4aACIsDV/LTaTjt12Z56MhDr9LsyuZQ==