init: stort init for global view store

This commit is contained in:
gurusainath 2024-01-30 14:26:59 +05:30
parent 61f92563a9
commit 8e590f6f60
14 changed files with 816 additions and 76 deletions

View File

@ -1,72 +0,0 @@
import { TIssue } from "./issues/base";
import type { IProjectLite } from "./projects";
export type TInboxIssueExtended = {
completed_at: string | null;
start_date: string | null;
target_date: string | null;
};
export interface IInboxIssue extends TIssue, TInboxIssueExtended {
issue_inbox: {
duplicate_to: string | null;
id: string;
snoozed_till: Date | null;
source: string;
status: -2 | -1 | 0 | 1 | 2;
}[];
}
export interface IInbox {
id: string;
project_detail: IProjectLite;
pending_issue_count: number;
created_at: Date;
updated_at: Date;
name: string;
description: string;
is_default: boolean;
created_by: string;
updated_by: string;
project: string;
view_props: { filters: IInboxFilterOptions };
workspace: string;
}
interface StatePending {
readonly status: -2;
}
interface StatusReject {
status: -1;
}
interface StatusSnoozed {
status: 0;
snoozed_till: Date;
}
interface StatusAccepted {
status: 1;
}
interface StatusDuplicate {
status: 2;
duplicate_to: string;
}
export type TInboxStatus =
| StatusReject
| StatusSnoozed
| StatusAccepted
| StatusDuplicate
| StatePending;
export interface IInboxFilterOptions {
priority?: string[] | null;
inbox_status?: number[] | null;
}
export interface IInboxQueryParams {
priority: string | null;
inbox_status: string | null;
}

View File

@ -13,11 +13,7 @@ export * from "./pages";
export * from "./ai";
export * from "./estimate";
export * from "./importer";
// FIXME: Remove this after development and the refactor/mobx-store-issue branch is stable
export * from "./inbox";
export * from "./inbox/root";
export * from "./analytics";
export * from "./calendar";
export * from "./notifications";
@ -31,6 +27,7 @@ export * from "./auth";
export * from "./api_token";
export * from "./instance";
export * from "./app";
export * from "./view";
export type NestedKeyOf<ObjectType extends object> = {
[Key in keyof ObjectType & (string | number)]: ObjectType[Key] extends object

81
packages/types/src/view.d.ts vendored Normal file
View File

@ -0,0 +1,81 @@
declare enum EGlobalViewAccess {
"public" = 0,
"private" = 1,
"shared" = 2,
}
export type TViewAccess =
| EGlobalViewAccess.public
| EGlobalViewAccess.private
| EGlobalViewAccess.shared;
export type TViewLayouts =
| "list"
| "kanban"
| "calendar"
| "spreadsheet"
| "gantt";
export type TViewFilters = {
project: string[];
priority: string[];
state: string[];
state_group: string[];
assignees: string[];
mentions: string[];
created_by: string[];
label: string[];
start_date: string[];
target_date: string[];
};
export type TViewDisplayFilters = {
group_by: string;
sub_group_by: string;
order_by: string;
issue_type: string;
layout: TViewLayouts;
};
export type TViewDisplayProperties = {
assignee: boolean;
start_date: boolean;
due_date: boolean;
labels: boolean;
key: boolean;
priority: boolean;
state: boolean;
sub_issue_count: boolean;
link: boolean;
attachment_count: boolean;
estimate: boolean;
created_on: boolean;
updated_on: boolean;
};
export type TViewProps = {
filters: TViewFilters;
display_filters: TViewDisplayFilters;
display_properties: TViewDisplayProperties;
};
export type TView = {
id: string;
workspace: string;
project: string | undefined;
name: string;
description: string | undefined;
query: string;
filters: undefined;
display_filters: undefined;
display_properties: undefined;
access: TViewAccess;
owned_by: string;
sort_order: number;
is_locked: boolean;
is_pinned: boolean;
created_by: string;
updated_by: string;
created_at: Date;
updated_at: Date;
};

View File

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

View File

@ -0,0 +1,13 @@
import { FC } from "react";
type TGlobalViewsRootProps = {
workspaceSlug: string;
projectId: string;
viewId: string;
};
export const GlobalViewsRoot: FC<TGlobalViewsRootProps> = (props) => {
const { viewId } = props;
return <div>GlobalViewsRoot {viewId}</div>;
};

View File

@ -0,0 +1,9 @@
type TUseViewsProps = {};
export const useViews = (issueId: string | undefined): TUseViewsProps => {
console.log("issueId", issueId);
return {
issueId,
};
};

View File

@ -0,0 +1,113 @@
import { APIService } from "services/api.service";
// types
import { TView } from "@plane/types";
import { TViewService } from "./types";
// helpers
import { API_BASE_URL } from "helpers/common.helper";
export class ProjectViewService extends APIService implements TViewService {
constructor() {
super(API_BASE_URL);
}
async fetch(workspaceSlug: string, projectId: string | undefined = undefined): Promise<TView[] | undefined> {
if (!projectId) return undefined;
return this.get(`/api/workspaces/${workspaceSlug}/projects/${projectId}/views/`)
.then((response) => response?.data)
.catch((error) => {
throw error?.response;
});
}
async fetchById(
workspaceSlug: string,
viewId: string,
projectId: string | undefined = undefined
): Promise<TView | undefined> {
if (!projectId) return undefined;
return this.get(`/api/workspaces/${workspaceSlug}/projects/${projectId}/views/${viewId}/`)
.then((response) => response?.data)
.catch((error) => {
throw error?.response;
});
}
async create(
workspaceSlug: string,
data: Partial<TView>,
projectId: string | undefined = undefined
): Promise<TView | undefined> {
if (!projectId) return undefined;
return this.post(`/api/workspaces/${workspaceSlug}/projects/${projectId}/views/`, data)
.then((response) => response?.data)
.catch((error) => {
throw error?.response;
});
}
async update(
workspaceSlug: string,
viewId: string,
data: Partial<TView>,
projectId: string | undefined = undefined
): Promise<TView | undefined> {
if (!projectId) return undefined;
return this.patch(`/api/workspaces/${workspaceSlug}/projects/${projectId}/views/${viewId}/`, data)
.then((response) => response?.data)
.catch((error) => {
throw error?.response;
});
}
async remove(
workspaceSlug: string,
viewId: string,
projectId: string | undefined = undefined
): Promise<void | undefined> {
if (!projectId) return undefined;
return this.delete(`/api/workspaces/${workspaceSlug}/projects/${projectId}/views/${viewId}/`)
.then((response) => response?.data)
.catch((error) => {
throw error?.response;
});
}
async lock(
workspaceSlug: string,
viewId: string,
projectId: string | undefined = undefined
): Promise<TView | undefined> {
if (!projectId) return undefined;
return this.post(`/api/workspaces/${workspaceSlug}/projects/${projectId}/views/${viewId}/lock/`)
.then((response) => response?.data)
.catch((error) => {
throw error?.response;
});
}
async unlock(
workspaceSlug: string,
viewId: string,
projectId: string | undefined = undefined
): Promise<TView | undefined> {
if (!projectId) return undefined;
return this.post(`/api/workspaces/${workspaceSlug}/projects/${projectId}/views/${viewId}/unlock/`)
.then((response) => response?.data)
.catch((error) => {
throw error?.response;
});
}
async duplicate(
workspaceSlug: string,
viewId: string,
projectId: string | undefined = undefined
): Promise<TView | undefined> {
if (!projectId) return undefined;
return this.post(`/api/workspaces/${workspaceSlug}/projects/${projectId}/views/${viewId}/duplicate/`)
.then((response) => response?.data)
.catch((error) => {
throw error?.response;
});
}
}

View File

@ -0,0 +1,113 @@
import { APIService } from "services/api.service";
// types
import { TView } from "@plane/types";
import { TViewService } from "./types";
// helpers
import { API_BASE_URL } from "helpers/common.helper";
export class ProjectViewMeService extends APIService implements TViewService {
constructor() {
super(API_BASE_URL);
}
async fetch(workspaceSlug: string, projectId: string | undefined = undefined): Promise<TView[] | undefined> {
if (!projectId) return undefined;
return this.get(`/api/users/me/workspaces/${workspaceSlug}/projects/${projectId}/views/`)
.then((response) => response?.data)
.catch((error) => {
throw error?.response;
});
}
async fetchById(
workspaceSlug: string,
viewId: string,
projectId: string | undefined = undefined
): Promise<TView | undefined> {
if (!projectId) return undefined;
return this.get(`/api/users/me/workspaces/${workspaceSlug}/projects/${projectId}/views/${viewId}/`)
.then((response) => response?.data)
.catch((error) => {
throw error?.response;
});
}
async create(
workspaceSlug: string,
data: Partial<TView>,
projectId: string | undefined = undefined
): Promise<TView | undefined> {
if (!projectId) return undefined;
return this.post(`/api/users/me/workspaces/${workspaceSlug}/projects/${projectId}/views/`, data)
.then((response) => response?.data)
.catch((error) => {
throw error?.response;
});
}
async update(
workspaceSlug: string,
viewId: string,
data: Partial<TView>,
projectId: string | undefined = undefined
): Promise<TView | undefined> {
if (!projectId) return undefined;
return this.patch(`/api/users/me/workspaces/${workspaceSlug}/projects/${projectId}/views/${viewId}/`, data)
.then((response) => response?.data)
.catch((error) => {
throw error?.response;
});
}
async remove(
workspaceSlug: string,
viewId: string,
projectId: string | undefined = undefined
): Promise<void | undefined> {
if (!projectId) return undefined;
return this.delete(`/api/users/me/workspaces/${workspaceSlug}/projects/${projectId}/views/${viewId}/`)
.then((response) => response?.data)
.catch((error) => {
throw error?.response;
});
}
async lock(
workspaceSlug: string,
viewId: string,
projectId: string | undefined = undefined
): Promise<TView | undefined> {
if (!projectId) return undefined;
return this.post(`/api/users/me/workspaces/${workspaceSlug}/projects/${projectId}/views/${viewId}/lock/`)
.then((response) => response?.data)
.catch((error) => {
throw error?.response;
});
}
async unlock(
workspaceSlug: string,
viewId: string,
projectId: string | undefined = undefined
): Promise<TView | undefined> {
if (!projectId) return undefined;
return this.post(`/api/users/me/workspaces/${workspaceSlug}/projects/${projectId}/views/${viewId}/unlock/`)
.then((response) => response?.data)
.catch((error) => {
throw error?.response;
});
}
async duplicate(
workspaceSlug: string,
viewId: string,
projectId: string | undefined = undefined
): Promise<TView | undefined> {
if (!projectId) return undefined;
return this.post(`/api/users/me/workspaces/${workspaceSlug}/projects/${projectId}/views/${viewId}/duplicate/`)
.then((response) => response?.data)
.catch((error) => {
throw error?.response;
});
}
}

17
web/services/view/types.d.ts vendored Normal file
View File

@ -0,0 +1,17 @@
import { TView } from "@plane/types";
export type TViewService = {
fetch: (workspaceSlug: string, projectId?: string) => Promise<TView[] | undefined>;
fetchById: (workspaceSlug: string, viewId: string, projectId?: string) => Promise<TView | undefined>;
create: (workspaceSlug: string, data: Partial<TView>, projectId?: string) => Promise<TView | undefined>;
update: (
workspaceSlug: string,
viewId: string,
data: Partial<TView>,
projectId?: string
) => Promise<TView | undefined>;
remove: (workspaceSlug: string, viewId: string, projectId?: string) => Promise<void> | undefined;
lock: (workspaceSlug: string, viewId: string, projectId?: string) => Promise<TView | undefined>;
unlock: (workspaceSlug: string, viewId: string, projectId?: string) => Promise<TView | undefined>;
duplicate: (workspaceSlug: string, viewId: string, projectId?: string) => Promise<TView | undefined>;
};

View File

@ -0,0 +1,76 @@
import { APIService } from "services/api.service";
// types
import { TView } from "@plane/types";
import { TViewService } from "./types";
// helpers
import { API_BASE_URL } from "helpers/common.helper";
export class WorkspaceViewService extends APIService implements TViewService {
constructor() {
super(API_BASE_URL);
}
async fetch(workspaceSlug: string): Promise<TView[]> {
return this.get(`/api/workspaces/${workspaceSlug}/views/`)
.then((response) => response?.data)
.catch((error) => {
throw error?.response;
});
}
async fetchById(workspaceSlug: string, viewId: string): Promise<TView> {
return this.get(`/api/workspaces/${workspaceSlug}/views/${viewId}/`)
.then((response) => response?.data)
.catch((error) => {
throw error?.response;
});
}
async create(workspaceSlug: string, data: Partial<TView>): Promise<TView> {
return this.post(`/api/workspaces/${workspaceSlug}/views/`, data)
.then((response) => response?.data)
.catch((error) => {
throw error?.response;
});
}
async update(workspaceSlug: string, viewId: string, data: Partial<TView>): Promise<TView> {
return this.patch(`/api/workspaces/${workspaceSlug}/views/${viewId}/`, data)
.then((response) => response?.data)
.catch((error) => {
throw error?.response;
});
}
async remove(workspaceSlug: string, viewId: string): Promise<void> {
return this.delete(`/api/workspaces/${workspaceSlug}/views/${viewId}/`)
.then((response) => response?.data)
.catch((error) => {
throw error?.response;
});
}
async lock(workspaceSlug: string, viewId: string): Promise<TView> {
return this.post(`/api/workspaces/${workspaceSlug}/views/${viewId}/lock/`)
.then((response) => response?.data)
.catch((error) => {
throw error?.response;
});
}
async unlock(workspaceSlug: string, viewId: string): Promise<TView> {
return this.post(`/api/workspaces/${workspaceSlug}/views/${viewId}/unlock/`)
.then((response) => response?.data)
.catch((error) => {
throw error?.response;
});
}
async duplicate(workspaceSlug: string, viewId: string): Promise<TView> {
return this.post(`/api/workspaces/${workspaceSlug}/views/${viewId}/duplicate/`)
.then((response) => response?.data)
.catch((error) => {
throw error?.response;
});
}
}

View File

@ -0,0 +1,76 @@
import { APIService } from "services/api.service";
// types
import { TView } from "@plane/types";
import { TViewService } from "./types";
// helpers
import { API_BASE_URL } from "helpers/common.helper";
export class WorkspaceMeViewService extends APIService implements TViewService {
constructor() {
super(API_BASE_URL);
}
async fetch(workspaceSlug: string): Promise<TView[]> {
return this.get(`/api/users/me/workspaces/${workspaceSlug}/views/`)
.then((response) => response?.data)
.catch((error) => {
throw error?.response;
});
}
async fetchById(workspaceSlug: string, viewId: string): Promise<TView> {
return this.get(`/api/users/me/workspaces/${workspaceSlug}/views/${viewId}/`)
.then((response) => response?.data)
.catch((error) => {
throw error?.response;
});
}
async create(workspaceSlug: string, data: Partial<TView>): Promise<TView> {
return this.post(`/api/users/me/workspaces/${workspaceSlug}/views/`, data)
.then((response) => response?.data)
.catch((error) => {
throw error?.response;
});
}
async update(workspaceSlug: string, viewId: string, data: Partial<TView>): Promise<TView> {
return this.patch(`/api/users/me/workspaces/${workspaceSlug}/views/${viewId}/`, data)
.then((response) => response?.data)
.catch((error) => {
throw error?.response;
});
}
async remove(workspaceSlug: string, viewId: string): Promise<void> {
return this.delete(`/api/users/me/workspaces/${workspaceSlug}/views/${viewId}/`)
.then((response) => response?.data)
.catch((error) => {
throw error?.response;
});
}
async lock(workspaceSlug: string, viewId: string): Promise<TView> {
return this.post(`/api/users/me/workspaces/${workspaceSlug}/views/${viewId}/lock/`)
.then((response) => response?.data)
.catch((error) => {
throw error?.response;
});
}
async unlock(workspaceSlug: string, viewId: string): Promise<TView> {
return this.post(`/api/users/me/workspaces/${workspaceSlug}/views/${viewId}/unlock/`)
.then((response) => response?.data)
.catch((error) => {
throw error?.response;
});
}
async duplicate(workspaceSlug: string, viewId: string): Promise<TView> {
return this.post(`/api/users/me/workspaces/${workspaceSlug}/views/${viewId}/duplicate/`)
.then((response) => response?.data)
.catch((error) => {
throw error?.response;
});
}
}

View File

@ -0,0 +1,23 @@
// services
import { WorkspaceViewService } from "services/view/workspace.service";
import { WorkspaceMeViewService } from "services/view/workspace_me.service";
import { ProjectViewService } from "services/view/project.service";
import { ProjectViewMeService } from "services/view/project_me.service";
// stores
import { ViewRoot } from "./view-root.store";
// types
import { RootStore } from "store/root.store";
export class ViewRootStore {
workspaceViewStore: ViewRoot;
workspaceViewMeStore: ViewRoot;
projectViewStore: ViewRoot;
projectViewMeStore: ViewRoot;
constructor(private store: RootStore) {
this.workspaceViewStore = new ViewRoot(this.store, new WorkspaceViewService());
this.workspaceViewMeStore = new ViewRoot(this.store, new WorkspaceMeViewService());
this.projectViewStore = new ViewRoot(this.store, new ProjectViewService());
this.projectViewMeStore = new ViewRoot(this.store, new ProjectViewMeService());
}
}

View File

@ -0,0 +1,109 @@
// types
import { action, computed, makeObservable, observable } from "mobx";
// stores
import { RootStore } from "store/root.store";
import { Views } from "./view.store";
// types
import { TViewService } from "services/view/types";
import { TView } from "@plane/types";
import { set } from "lodash";
export type TLoader = "" | undefined;
type TViewRoot = {
// observables
viewMap: Record<string, Views>;
// computed
viewIds: string[];
// actions
fetch: () => Promise<void>;
create: (view: Partial<TView>) => Promise<void>;
delete: (viewId: string) => Promise<void>;
duplicate: (viewId: string) => Promise<void>;
};
export class ViewRoot implements TViewRoot {
viewMap: Record<string, Views> = {};
constructor(private store: RootStore, private service: TViewService) {
makeObservable(this, {
// observables
viewMap: observable,
// computed
viewIds: computed,
// actions
fetch: action,
create: action,
delete: action,
duplicate: action,
});
}
// computed
get viewIds() {
return Object.keys(this.viewMap);
}
get views() {
return Object.values(this.viewMap);
}
// actions
fetch = async () => {
try {
const { workspaceSlug, projectId } = this.store.app.router;
if (!workspaceSlug || !projectId) return;
const views = await this.service.fetch(workspaceSlug, projectId);
if (!views) return;
views.forEach((view) => {
set(this.viewMap, [view.id], new Views(this.store, view, this.service));
});
} catch (error) {
console.log(error);
}
};
create = async (_view: Partial<TView>) => {
try {
const { workspaceSlug, projectId } = this.store.app.router;
if (!workspaceSlug || !projectId) return;
const view = await this.service.create(workspaceSlug, _view, projectId);
if (!view) return;
set(this.viewMap, [view.id], new Views(this.store, view, this.service));
} catch (error) {
console.log(error);
}
};
delete = async (viewId: string) => {
try {
const { workspaceSlug, projectId } = this.store.app.router;
if (!workspaceSlug || !projectId) return;
await this.service.remove(workspaceSlug, viewId, projectId);
delete this.viewMap[viewId];
} catch (error) {
console.log(error);
}
};
duplicate = async (viewId: string) => {
try {
const { workspaceSlug, projectId } = this.store.app.router;
if (!workspaceSlug || !projectId) return;
const view = await this.service.duplicate(workspaceSlug, viewId, projectId);
if (!view) return;
set(this.viewMap, [view.id], new Views(this.store, view, this.service));
} catch (error) {
console.log(error);
}
};
}

View File

@ -0,0 +1,184 @@
import { action, computed, makeObservable } from "mobx";
// store
import { RootStore } from "store/root.store";
// types
import { TViewService } from "services/view/types";
import { TView, TViewFilters, TViewDisplayFilters, TViewDisplayProperties, TViewAccess } from "@plane/types";
export type TViews = TView & {
// computed
user: undefined;
// actions
updateName: (name: string) => Promise<void>;
updateDescription: (description: string) => Promise<void>;
updateFilters: (filters: TViewFilters) => Promise<void>;
updateDisplayFilters: (display_filters: TViewDisplayFilters) => Promise<void>;
updateDisplayProperties: (display_properties: TViewDisplayProperties) => Promise<void>;
lockView: () => Promise<void>;
unlockView: () => Promise<void>;
};
export class Views implements TViews {
id: string;
workspace: string;
project: string | undefined;
name: string;
description: string | undefined;
query: string;
filters: undefined;
display_filters: undefined;
display_properties: undefined;
access: TViewAccess;
owned_by: string;
sort_order: number;
is_locked: boolean;
is_pinned: boolean;
created_by: string;
updated_by: string;
created_at: Date;
updated_at: Date;
constructor(private store: RootStore, _view: TView, private service: TViewService) {
this.id = _view.id;
this.workspace = _view.workspace;
this.project = _view.project;
this.name = _view.name;
this.description = _view.description;
this.query = _view.query;
this.filters = _view.filters;
this.display_filters = _view.display_filters;
this.display_properties = _view.display_properties;
this.access = _view.access;
this.owned_by = _view.owned_by;
this.sort_order = _view.sort_order;
this.is_locked = _view.is_locked;
this.is_pinned = _view.is_pinned;
this.created_by = _view.created_by;
this.updated_by = _view.updated_by;
this.created_at = _view.created_at;
this.updated_at = _view.updated_at;
makeObservable(this, {
// computed
user: computed,
// actions
updateName: action,
updateDescription: action,
updateFilters: action,
updateDisplayFilters: action,
updateDisplayProperties: action,
lockView: action,
unlockView: action,
});
}
// computed
get user() {
return undefined;
}
// actions
updateName = async (name: string) => {
try {
const { workspaceSlug, projectId } = this.store.app.router;
if (!workspaceSlug || !projectId) return;
const view = await this.service.update(workspaceSlug, this.id, { name: name }, projectId);
if (!view) return;
this.name = view.name;
} catch (error) {
console.log(error);
}
};
updateDescription = async (description: string) => {
try {
const { workspaceSlug, projectId } = this.store.app.router;
if (!workspaceSlug || !projectId) return;
const view = await this.service.update(workspaceSlug, this.id, { description: description }, projectId);
if (!view) return;
this.description = view.description;
} catch (error) {
console.log(error);
}
};
updateFilters = async (filters: any) => {
try {
const { workspaceSlug, projectId } = this.store.app.router;
if (!workspaceSlug || !projectId) return;
const view = await this.service.update(workspaceSlug, this.id, { filters: filters }, projectId);
if (!view) return;
this.filters = view.filters;
} catch (error) {
console.log(error);
}
};
updateDisplayFilters = async (display_filters: any) => {
try {
const { workspaceSlug, projectId } = this.store.app.router;
if (!workspaceSlug || !projectId) return;
const view = await this.service.update(workspaceSlug, this.id, { display_filters: display_filters }, projectId);
if (!view) return;
this.display_filters = view.display_filters;
} catch (error) {
console.log(error);
}
};
updateDisplayProperties = async (display_properties: any) => {
try {
const { workspaceSlug, projectId } = this.store.app.router;
if (!workspaceSlug || !projectId) return;
const view = await this.service.update(
workspaceSlug,
this.id,
{ display_properties: display_properties },
projectId
);
if (!view) return;
this.display_properties = view.display_properties;
} catch (error) {
console.log(error);
}
};
lockView = async () => {
try {
const { workspaceSlug, projectId } = this.store.app.router;
if (!workspaceSlug || !projectId) return;
const view = await this.service.lock(workspaceSlug, this.id, projectId);
if (!view) return;
this.is_locked = view.is_locked;
} catch (error) {
console.log(error);
}
};
unlockView = async () => {
try {
const { workspaceSlug, projectId } = this.store.app.router;
if (!workspaceSlug || !projectId) return;
const view = await this.service.unlock(workspaceSlug, this.id, projectId);
if (!view) return;
this.is_locked = view.is_locked;
} catch (error) {
console.log(error);
}
};
}