diff --git a/web/components/issue-layouts/kanban/content.tsx b/web/components/issue-layouts/kanban/content.tsx
new file mode 100644
index 000000000..15e34becd
--- /dev/null
+++ b/web/components/issue-layouts/kanban/content.tsx
@@ -0,0 +1,52 @@
+// react beautiful dnd
+import { Draggable } from "react-beautiful-dnd";
+
+interface IssueContentProps {
+ columnId: string;
+ issues: any;
+}
+
+export const IssueContent = ({ columnId, issues }: IssueContentProps) => {
+ console.log();
+
+ return (
+ <>
+ {issues && issues.length > 0 ? (
+ <>
+ {issues.map((issue: any, index: any) => (
+
+ {(provided: any, snapshot: any) => (
+
+
+
+ ONE-{issue.sequence_id}-{issue.sort_order}
+
+
+ {issue.name} {issue.name} {issue.name} {issue.name} {issue.name} {issue.name}{" "}
+ {issue.name} {issue.name}
+
+
Footer
+
+
+ )}
+
+ ))}
+ >
+ ) : (
+
No issues are available.
+ )}
+ >
+ );
+};
diff --git a/web/components/issue-layouts/kanban/footer.tsx b/web/components/issue-layouts/kanban/footer.tsx
new file mode 100644
index 000000000..21b09c11f
--- /dev/null
+++ b/web/components/issue-layouts/kanban/footer.tsx
@@ -0,0 +1 @@
+export const IssueFooter = () => Footer
;
diff --git a/web/components/issue-layouts/kanban/header.tsx b/web/components/issue-layouts/kanban/header.tsx
new file mode 100644
index 000000000..4852fabd9
--- /dev/null
+++ b/web/components/issue-layouts/kanban/header.tsx
@@ -0,0 +1 @@
+export const IssueHeader = () => Header
;
diff --git a/web/components/issue-layouts/kanban/index.tsx b/web/components/issue-layouts/kanban/index.tsx
new file mode 100644
index 000000000..eaa6e4468
--- /dev/null
+++ b/web/components/issue-layouts/kanban/index.tsx
@@ -0,0 +1,70 @@
+import React from "react";
+// react beautiful dnd
+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(() => {
+ const store: RootStore = useMobxStore();
+ const { kanban: issueViewStore } = store;
+
+ const onDragEnd = (result: any) => {
+ console.log("result", result);
+ };
+
+ return (
+
+ {issueViewStore.loader && issueViewStore?.issues === null ? (
+
Loading...
+ ) : (
+
+ {issueViewStore?.getIssues && Object.keys(issueViewStore?.getIssues).length > 0 ? (
+
+
+ {Object.keys(issueViewStore?.getIssues).map((_issueStateKey: any) => (
+
+
+
+
+
+
+
+ {(provided: any, snapshot: any) => (
+ <>
+
+
+
+ {provided.placeholder}
+ >
+ )}
+
+
+
+
+
+
+
+ ))}
+
+
+ ) : (
+
No Issues are available
+ )}
+
+ )}
+
+ );
+});
diff --git a/web/components/kanban-layout/index.tsx b/web/components/kanban-layout/index.tsx
deleted file mode 100644
index cd910124d..000000000
--- a/web/components/kanban-layout/index.tsx
+++ /dev/null
@@ -1,11 +0,0 @@
-import React from "react";
-
-export const KanbanInitLayout = () => {
- console.log("");
-
- return (
-
- );
-};
diff --git a/web/pages/kanban.tsx b/web/pages/kanban.tsx
new file mode 100644
index 000000000..a25d75824
--- /dev/null
+++ b/web/pages/kanban.tsx
@@ -0,0 +1,29 @@
+import React from "react";
+// swr
+import useSWR from "swr";
+// components
+import { IssueRoot } from "components/issue-layouts/kanban";
+// mobx store
+import { useMobxStore } from "lib/mobx/store-provider";
+import { RootStore } from "store/root";
+
+const KanbanViewRoot = () => {
+ const workspaceSlug: string = "plane-demo";
+ const projectSlug: string = "5b0e3f6e-c9f1-444d-be22-a8c2706bcf54";
+
+ const store: RootStore = useMobxStore();
+ const { kanban: issueViewStore } = store;
+
+ useSWR(`PROJECT_ISSUES_KANBAN_VIEW`, () => {
+ if (workspaceSlug && projectSlug)
+ issueViewStore.getIssuesAsync(workspaceSlug, projectSlug, "kanban");
+ });
+
+ return (
+
+
+
+ );
+};
+
+export default KanbanViewRoot;
diff --git a/web/services/issues.service.ts b/web/services/issues.service.ts
index b8875e6c5..c6e122a3b 100644
--- a/web/services/issues.service.ts
+++ b/web/services/issues.service.ts
@@ -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 ProjectIssuesServices extends APIService {
+export class ProjectIssuesServices extends APIService {
constructor() {
super(NEXT_PUBLIC_API_BASE_URL || "http://localhost:8000");
}
diff --git a/web/store/issue-views/data.ts b/web/store/issue-views/data.ts
new file mode 100644
index 000000000..559084e08
--- /dev/null
+++ b/web/store/issue-views/data.ts
@@ -0,0 +1,60 @@
+export const filtersPriority: { key: string; title: string }[] = [
+ { key: "", title: "Urgent" },
+ { key: "", title: "High" },
+ { key: "", title: "Medium" },
+ { key: "", title: "Low" },
+ { key: "", 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" },
+];
+
+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" },
+];
+
+export const displayPropertyGroupBy = [
+ { key: "state", title: "States" },
+ { key: "state_detail.group", title: "State Groups" },
+ { key: "priority", title: "Priority" },
+ { key: "labels", title: "Labels" },
+ { key: "assignees", title: "Assignees" },
+ { key: "created_by", title: "Created By" },
+];
+
+export const displayPropertyOrderBy = [
+ { key: "sort_order", title: "Manual" },
+ { key: "created_at", title: "Last Created" },
+ { key: "updated_at", title: "Last Updated" },
+ { key: "start_date", title: "Start Date" },
+ { key: "priority", title: "Priority" },
+];
+
+export const displayPropertyIssueType = [
+ { key: "all", title: "All" },
+ { key: "active", title: "Active Issues" },
+ { key: "backlog", title: "Backlog Issues" },
+];
+
+export const displayProperties = [
+ { key: "assignee", title: "Assignee" },
+ { key: "start_date", title: "Start Date" },
+ { key: "due_date", title: "Due Date" },
+ { key: "key", title: "Id" },
+ { key: "labels", title: "Labels" },
+ { key: "priority", title: "Priority" },
+ { key: "state", title: "State" },
+ { key: "sub_issue_count", title: "Sub Issue Count" },
+ { key: "attachment_count", title: "Attachment Count" },
+ { key: "link", title: "Link" },
+ { key: "estimate", title: "Estimate" },
+];
diff --git a/web/store/issue-views/filters.ts b/web/store/issue-views/filters.ts
new file mode 100644
index 000000000..2f79b9d3d
--- /dev/null
+++ b/web/store/issue-views/filters.ts
@@ -0,0 +1,154 @@
+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;
+ getProjectIssueDisplayProperties: () => any | Promise;
+ getProjectMembers: () => any | Promise;
+ getProjectStates: () => any | Promise;
+ getProjectLabels: () => any | Promise;
+}
+
+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;
diff --git a/web/store/issue-views/kanban.ts b/web/store/issue-views/kanban.ts
new file mode 100644
index 000000000..34a9e5b14
--- /dev/null
+++ b/web/store/issue-views/kanban.ts
@@ -0,0 +1,92 @@
+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;
+}
+
+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;
diff --git a/web/store/kanban.ts b/web/store/kanban.ts
deleted file mode 100644
index 81730032f..000000000
--- a/web/store/kanban.ts
+++ /dev/null
@@ -1,82 +0,0 @@
-import { observable, action, computed, makeObservable, runInAction } from "mobx";
-// types
-import { RootStore } from "./root";
-// services
-
-export interface IKanbanStore {
- loader: boolean;
- error: any | null;
-
- // current issue view
- issueView?: "kanban";
-
- // 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;
-
- issues?: null;
-}
-
-class KanbanStore implements IKanbanStore {
- loader: boolean = false;
- error: any | null = null;
-
- // root store
- rootStore;
- // service
- projectPublishService = null;
-
- constructor(_rootStore: RootStore) {
- makeObservable(this, {
- // observable
- loader: observable,
- error: observable,
-
- // action
- // computed
- });
-
- this.rootStore = _rootStore;
- this.projectPublishService = null;
- }
-}
-
-export default KanbanStore;
diff --git a/web/store/root.ts b/web/store/root.ts
index 2e81922d2..eba9aff95 100644
--- a/web/store/root.ts
+++ b/web/store/root.ts
@@ -5,8 +5,10 @@ import UserStore from "./user";
import ThemeStore from "./theme";
import ProjectStore, { IProjectStore } from "./project";
import ProjectPublishStore, { IProjectPublishStore } from "./project-publish";
-import KanbanStore from "./kanban";
import IssuesStore from "./issues";
+// issues views and filters
+import IssueFilterStore from "./issue-views/filters";
+import KanbanStore from "./issue-views/kanban";
enableStaticRendering(typeof window === "undefined");
@@ -16,6 +18,7 @@ export class RootStore {
project: IProjectStore;
projectPublish: IProjectPublishStore;
issues: IssuesStore;
+ issueFilters: IssueFilterStore;
kanban: KanbanStore;
constructor() {
@@ -24,6 +27,7 @@ export class RootStore {
this.project = new ProjectStore(this);
this.projectPublish = new ProjectPublishStore(this);
this.issues = new IssuesStore(this);
+ this.issueFilters = new IssueFilterStore(this);
this.kanban = new KanbanStore(this);
}
}