chore: inbox issue restructure the components and store (#3456)

* chore: inbox-issues store and type updates

* chore: issue inbox payload change for GET and POST

* chore: issue inbox payload change for PATCH

* chore: inbox-issue new hooks and store updates

* chore: update inbox issue template.

* chore: UI root

* chore: sidebar issues render

* chore: inbox issue details page layout.

* chore: inbox issue filters

* chore: inbox issue status card.

* chore: add loader.

* chore: active inbox issue styles.

* chore: inbox filters

* chore: inbox applied filters UI

* chore: inbox issue approval header

* chore: inbox issue approval header operations

* chore: issue reaction and activity fetch in issue_inbox store

* chore: posthog enabled

---------

Co-authored-by: NarayanBavisetti <narayan3119@gmail.com>
Co-authored-by: Prateek Shourya <prateekshourya29@gmail.com>
This commit is contained in:
guru_sainath 2024-01-24 20:33:54 +05:30 committed by GitHub
parent 911211cf3d
commit b66f07845a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
52 changed files with 1998 additions and 1457 deletions

View file

@ -1,244 +1,282 @@
import { observable, action, makeObservable, runInAction, autorun, computed } from "mobx";
import { observable, action, makeObservable, runInAction } from "mobx";
import { computedFn } from "mobx-utils";
import { set } from "lodash";
import set from "lodash/set";
import update from "lodash/update";
import concat from "lodash/concat";
import uniq from "lodash/uniq";
import pull from "lodash/pull";
// services
import { InboxService } from "services/inbox.service";
import { InboxIssueService } from "services/inbox/inbox-issue.service";
// types
import { RootStore } from "store/root.store";
import { IInboxIssue, TIssue, TInboxStatus } from "@plane/types";
import type {
TInboxIssueDetailIdMap,
TInboxIssueDetailMap,
TInboxIssueDetail,
TInboxIssueExtendedDetail,
TInboxDetailedStatus,
TIssue,
} from "@plane/types";
// constants
import { INBOX_ISSUE_SOURCE } from "constants/inbox";
export interface IInboxIssuesStore {
type TInboxIssueLoader = "fetch" | undefined;
export interface IInboxIssue {
// observables
issueMap: Record<string, Record<string, IInboxIssue>>; // {inboxId: {issueId: IInboxIssue}}
// computed
currentInboxIssueIds: string[] | null;
// computed actions
getIssueById: (inboxId: string, issueId: string) => IInboxIssue | null;
// fetch actions
fetchIssues: (workspaceSlug: string, projectId: string, inboxId: string) => Promise<IInboxIssue[]>;
fetchIssueDetails: (
loader: TInboxIssueLoader;
inboxIssues: TInboxIssueDetailIdMap;
inboxIssueMap: TInboxIssueDetailMap;
// helper methods
getInboxIssuesByInboxId: (inboxId: string) => string[] | undefined;
getInboxIssueByIssueId: (inboxId: string, issueId: string) => TInboxIssueDetail | undefined;
// actions
fetchInboxIssues: (workspaceSlug: string, projectId: string, inboxId: string) => Promise<TInboxIssueExtendedDetail[]>;
fetchInboxIssueById: (
workspaceSlug: string,
projectId: string,
inboxId: string,
issueId: string
) => Promise<IInboxIssue>;
// CRUD actions
createIssue: (
inboxIssueId: string
) => Promise<TInboxIssueExtendedDetail[]>;
createInboxIssue: (
workspaceSlug: string,
projectId: string,
inboxId: string,
data: Partial<TIssue>
) => Promise<IInboxIssue>;
updateIssue: (
data: Partial<TInboxIssueExtendedDetail>
) => Promise<TInboxIssueExtendedDetail>;
updateInboxIssue: (
workspaceSlug: string,
projectId: string,
inboxId: string,
issueId: string,
data: Partial<IInboxIssue>
) => Promise<void>;
updateIssueStatus: (
inboxIssueId: string,
data: Partial<TInboxIssueExtendedDetail>
) => Promise<TInboxIssueExtendedDetail>;
removeInboxIssue: (workspaceSlug: string, projectId: string, inboxId: string, issueId: string) => Promise<void>;
updateInboxIssueStatus: (
workspaceSlug: string,
projectId: string,
inboxId: string,
issueId: string,
data: TInboxStatus
) => Promise<void>;
deleteIssue: (workspaceSlug: string, projectId: string, inboxId: string, issueId: string) => Promise<void>;
inboxIssueId: string,
data: TInboxDetailedStatus
) => Promise<TInboxIssueExtendedDetail>;
}
export class InboxIssuesStore implements IInboxIssuesStore {
export class InboxIssue implements IInboxIssue {
// observables
issueMap: { [inboxId: string]: Record<string, IInboxIssue> } = {};
loader: TInboxIssueLoader = "fetch";
inboxIssues: TInboxIssueDetailIdMap = {};
inboxIssueMap: TInboxIssueDetailMap = {};
// root store
rootStore;
// services
inboxService;
inboxIssueService;
constructor(_rootStore: RootStore) {
makeObservable(this, {
// observables
issueMap: observable,
// computed
currentInboxIssueIds: computed,
// fetch actions
fetchIssues: action,
fetchIssueDetails: action,
// CRUD actions
createIssue: action,
updateIssue: action,
updateIssueStatus: action,
deleteIssue: action,
loader: observable.ref,
inboxIssues: observable,
inboxIssueMap: observable,
// actions
fetchInboxIssues: action,
fetchInboxIssueById: action,
createInboxIssue: action,
updateInboxIssue: action,
removeInboxIssue: action,
updateInboxIssueStatus: action,
});
// root store
this.rootStore = _rootStore;
// services
this.inboxService = new InboxService();
autorun(() => {
const routerStore = this.rootStore.app.router;
const workspaceSlug = routerStore?.workspaceSlug;
const projectId = routerStore?.projectId;
const inboxId = routerStore?.inboxId;
if (workspaceSlug && projectId && inboxId && this.rootStore.inboxRoot.inboxFilters.inboxFilters[inboxId])
this.fetchIssues(workspaceSlug, projectId, inboxId);
});
this.inboxIssueService = new InboxIssueService();
}
/**
* Returns the issue IDs belong to a specific inbox issues list
*/
get currentInboxIssueIds() {
const inboxId = this.rootStore.app.router.inboxId;
if (!inboxId) return null;
return Object.keys(this.issueMap?.[inboxId] ?? {}) ?? null;
}
// helper methods
getInboxIssuesByInboxId = computedFn((inboxId: string) => {
if (!inboxId) return undefined;
return this.inboxIssues?.[inboxId] ?? undefined;
});
/**
* Returns the issue details belongs to a specific inbox issue
*/
getIssueById = computedFn(
(inboxId: string, issueId: string): IInboxIssue | null => this.issueMap?.[inboxId]?.[issueId] ?? null
);
getInboxIssueByIssueId = computedFn((inboxId: string, issueId: string) => {
if (!inboxId) return undefined;
return this.inboxIssueMap?.[inboxId]?.[issueId] ?? undefined;
});
/**
* Fetches issues of a specific inbox and adds it to the store
* @param workspaceSlug
* @param projectId
* @param inboxId
* @returns Promise<IInbox[]>
*/
fetchIssues = async (workspaceSlug: string, projectId: string, inboxId: string) => {
const queryParams = this.rootStore.inboxRoot.inboxFilters.appliedFilters ?? undefined;
return await this.inboxService
.getInboxIssues(workspaceSlug, projectId, inboxId, queryParams)
.then((issuesResponse) => {
runInAction(() => {
issuesResponse.forEach((issue) => {
set(this.issueMap, [inboxId, issue.issue_inbox?.[0].id], issue);
});
});
return issuesResponse;
});
};
// actions
fetchInboxIssues = async (workspaceSlug: string, projectId: string, inboxId: string) => {
try {
this.loader = "fetch";
const queryParams = this.rootStore.inbox.inboxFilter.inboxAppliedFilters ?? {};
/**
* Fetches issue details of a specific inbox issue and updates it to the store
* @param workspaceSlug
* @param projectId
* @param inboxId
* @param issueId
* returns Promise<IInboxIssue>
*/
fetchIssueDetails = async (workspaceSlug: string, projectId: string, inboxId: string, issueId: string) => {
return await this.inboxService
.getInboxIssueById(workspaceSlug, projectId, inboxId, issueId)
.then((issueResponse) => {
runInAction(() => {
set(this.issueMap, [inboxId, issueId], issueResponse);
});
return issueResponse;
});
};
const response = await this.inboxIssueService.fetchInboxIssues(workspaceSlug, projectId, inboxId, queryParams);
/**
* Creates a new issue for a specific inbox and add it to the store
* @param workspaceSlug
* @param projectId
* @param inboxId
* @param data
* @returns Promise<IInboxIssue>
*/
createIssue = async (workspaceSlug: string, projectId: string, inboxId: string, data: Partial<TIssue>) => {
const payload = {
issue: {
name: data.name,
// description: data.description,
description_html: data.description_html,
priority: data.priority,
},
source: INBOX_ISSUE_SOURCE,
};
return await this.inboxService.createInboxIssue(workspaceSlug, projectId, inboxId, payload).then((response) => {
runInAction(() => {
set(this.issueMap, [inboxId, response.issue_inbox?.[0].id], response);
response.forEach((_inboxIssue) => {
const { ["issue_inbox"]: issueInboxDetail, ...issue } = _inboxIssue;
this.rootStore.inbox.rootStore.issue.issues.addIssue([issue]);
const { ["id"]: omittedId, ...inboxIssue } = issueInboxDetail[0];
set(this.inboxIssueMap, [inboxId, _inboxIssue.id], inboxIssue);
});
});
const _inboxIssueIds = response.map((inboxIssue) => inboxIssue.id);
runInAction(() => {
set(this.inboxIssues, inboxId, _inboxIssueIds);
this.loader = undefined;
});
return response;
});
} catch (error) {
this.loader = undefined;
throw error;
}
};
/**
* Updates an issue for a specific inbox and update it in the store
* @param workspaceSlug
* @param projectId
* @param inboxId
* @param issueId
* @param data
* @returns Promise<IInboxIssue>
*/
updateIssue = async (
fetchInboxIssueById = async (workspaceSlug: string, projectId: string, inboxId: string, inboxIssueId: string) => {
try {
const response = await this.inboxIssueService.fetchInboxIssueById(
workspaceSlug,
projectId,
inboxId,
inboxIssueId
);
runInAction(() => {
const { ["issue_inbox"]: issueInboxDetail, ...issue } = response;
this.rootStore.inbox.rootStore.issue.issues.addIssue([issue]);
const { ["id"]: omittedId, ...inboxIssue } = issueInboxDetail[0];
set(this.inboxIssueMap, [inboxId, response.id], inboxIssue);
});
runInAction(() => {
update(this.inboxIssues, inboxId, (inboxIssueIds: string[] = []) => {
if (inboxIssueIds.includes(response.id)) return inboxIssueIds;
return uniq(concat(inboxIssueIds, response.id));
});
});
// fetching issue activity
await this.rootStore.issue.issueDetail.fetchActivities(workspaceSlug, projectId, inboxIssueId);
// fetching issue reaction
await this.rootStore.issue.issueDetail.fetchReactions(workspaceSlug, projectId, inboxIssueId);
return response as any;
} catch (error) {
throw error;
}
};
createInboxIssue = async (workspaceSlug: string, projectId: string, inboxId: string, data: Partial<TIssue>) => {
try {
const response = await this.inboxIssueService.createInboxIssue(workspaceSlug, projectId, inboxId, {
source: "in-app",
issue: data,
});
runInAction(() => {
const { ["issue_inbox"]: issueInboxDetail, ...issue } = response;
this.rootStore.inbox.rootStore.issue.issues.addIssue([issue]);
const { ["id"]: omittedId, ...inboxIssue } = issueInboxDetail[0];
set(this.inboxIssueMap, [inboxId, response.id], inboxIssue);
});
runInAction(() => {
update(this.inboxIssues, inboxId, (inboxIssueIds: string[] = []) => {
if (inboxIssueIds.includes(response.id)) return inboxIssueIds;
return uniq(concat(inboxIssueIds, response.id));
});
});
await this.rootStore.issue.issueDetail.fetchActivities(workspaceSlug, projectId, response.id);
return response;
} catch (error) {
throw error;
}
};
updateInboxIssue = async (
workspaceSlug: string,
projectId: string,
inboxId: string,
issueId: string,
data: Partial<IInboxIssue>
inboxIssueId: string,
data: Partial<TIssue>
) => {
const issueDetails = this.rootStore.inboxRoot.inboxIssues.getIssueById(inboxId, issueId);
return await this.inboxService
.patchInboxIssue(workspaceSlug, projectId, inboxId, issueId, { issue: data })
.then((issueResponse) => {
runInAction(() => {
set(this.issueMap, [inboxId, issueId], {
...issueDetails,
...issueResponse,
});
});
return issueResponse;
try {
const response = await this.inboxIssueService.updateInboxIssue(workspaceSlug, projectId, inboxId, inboxIssueId, {
issue: data,
});
runInAction(() => {
const { ["issue_inbox"]: issueInboxDetail, ...issue } = response;
this.rootStore.inbox.rootStore.issue.issues.addIssue([issue]);
const { ["id"]: omittedId, ...inboxIssue } = issueInboxDetail[0];
set(this.inboxIssueMap, [inboxId, response.id], inboxIssue);
});
runInAction(() => {
update(this.inboxIssues, inboxId, (inboxIssueIds: string[] = []) => {
if (inboxIssueIds.includes(response.id)) return inboxIssueIds;
return uniq(concat(inboxIssueIds, response.id));
});
});
await this.rootStore.issue.issueDetail.fetchActivities(workspaceSlug, projectId, inboxIssueId);
return response as any;
} catch (error) {
throw error;
}
};
/**
* Updates an issue status for a specific inbox issue and update it in the store
* @param workspaceSlug
* @param projectId
* @param inboxId
* @param issueId
* @param data
* @returns Promise<IInboxIssue>
*/
updateIssueStatus = async (
removeInboxIssue = async (workspaceSlug: string, projectId: string, inboxId: string, inboxIssueId: string) => {
try {
const response = await this.inboxIssueService.removeInboxIssue(workspaceSlug, projectId, inboxId, inboxIssueId);
runInAction(() => {
pull(this.inboxIssues[inboxId], inboxIssueId);
delete this.inboxIssueMap[inboxId][inboxIssueId];
});
await this.rootStore.issue.issueDetail.fetchActivities(workspaceSlug, projectId, inboxIssueId);
return response as any;
} catch (error) {
throw error;
}
};
updateInboxIssueStatus = async (
workspaceSlug: string,
projectId: string,
inboxId: string,
issueId: string,
data: TInboxStatus
inboxIssueId: string,
data: TInboxDetailedStatus
) => {
const issueDetails = this.rootStore.inboxRoot.inboxIssues.getIssueById(inboxId, issueId);
await this.inboxService.markInboxStatus(workspaceSlug, projectId, inboxId, issueId, data).then((response) => {
try {
const response = await this.inboxIssueService.updateInboxIssueStatus(
workspaceSlug,
projectId,
inboxId,
inboxIssueId,
data
);
runInAction(() => {
set(this.issueMap, [inboxId, issueId, "issue_inbox", 0], {
...issueDetails?.issue_inbox?.[0],
...response?.issue_inbox?.[0],
const { ["issue_inbox"]: issueInboxDetail, ...issue } = response;
this.rootStore.inbox.rootStore.issue.issues.addIssue([issue]);
const { ["id"]: omittedId, ...inboxIssue } = issueInboxDetail[0];
set(this.inboxIssueMap, [inboxId, response.id], inboxIssue);
});
runInAction(() => {
update(this.inboxIssues, inboxId, (inboxIssueIds: string[] = []) => {
if (inboxIssueIds.includes(response.id)) return inboxIssueIds;
return uniq(concat(inboxIssueIds, response.id));
});
});
return response;
});
};
/**
* Deletes an issue for a specific inbox and removes it from the store
* @param workspaceSlug
* @param projectId
* @param inboxId
* @param issueId
* @returns Promise<void>
*/
deleteIssue = async (workspaceSlug: string, projectId: string, inboxId: string, issueId: string) => {
await this.inboxService.deleteInboxIssue(workspaceSlug, projectId, inboxId, issueId).then((_) => {
runInAction(() => {
delete this.issueMap?.[inboxId]?.[issueId];
});
});
await this.rootStore.issue.issueDetail.fetchActivities(workspaceSlug, projectId, inboxIssueId);
return response as any;
} catch (error) {
throw error;
}
};
}