[WEB-999] chore: updated UI improvements and workflow updates in the project inbox (#4180)
* chore: snoozed filter in the issue inbox filter * chore: navigating to the next or previous issue when we accept, decline, or duplicate the issue in inbox * chore: Implemented state, label, assignee and target_date in the inbox issue description and Implemented issue edit confirmation once we click accept the inbox issue * chore: removed logs * chore: inbox issue create response * chore: update inbox issue response * chore: updated inbox issue accept workflow and added issue properties in inbox issue create modal * chore: resolved build errors and upgraded lucide react * chore: updated inbox issue store hook * chore: code cleanup and removed validation for inbox description * fix: renamed the variable isLoading to loader in project-inbox store * fix: updated set function for issue property update --------- Co-authored-by: NarayanBavisetti <narayan3119@gmail.com>
This commit is contained in:
parent
a44a032683
commit
20b0edeaa6
22 changed files with 965 additions and 127 deletions
|
|
@ -1,3 +1,4 @@
|
|||
import clone from "lodash/clone";
|
||||
import set from "lodash/set";
|
||||
import { makeObservable, observable, runInAction, action } from "mobx";
|
||||
import { TIssue, TInboxIssue, TInboxIssueStatus, TInboxDuplicateIssueDetails } from "@plane/types";
|
||||
|
|
@ -5,6 +6,7 @@ import { TIssue, TInboxIssue, TInboxIssueStatus, TInboxDuplicateIssueDetails } f
|
|||
import { EInboxIssueStatus } from "@/helpers/inbox.helper";
|
||||
// services
|
||||
import { InboxIssueService } from "@/services/inbox";
|
||||
import { IssueService } from "@/services/issue";
|
||||
// root store
|
||||
import { RootStore } from "@/store/root.store";
|
||||
|
||||
|
|
@ -22,6 +24,8 @@ export interface IInboxIssueStore {
|
|||
updateInboxIssueDuplicateTo: (issueId: string) => Promise<void>; // connecting the inbox issue to the project existing issue
|
||||
updateInboxIssueSnoozeTill: (date: Date) => Promise<void>; // snooze the issue
|
||||
updateIssue: (issue: Partial<TIssue>) => Promise<void>; // updating the issue
|
||||
updateProjectIssue: (issue: Partial<TIssue>) => Promise<void>; // updating the issue
|
||||
fetchIssueActivity: () => Promise<void>; // fetching the issue activity
|
||||
}
|
||||
|
||||
export class InboxIssueStore implements IInboxIssueStore {
|
||||
|
|
@ -38,6 +42,7 @@ export class InboxIssueStore implements IInboxIssueStore {
|
|||
projectId: string;
|
||||
// services
|
||||
inboxIssueService;
|
||||
issueService;
|
||||
|
||||
constructor(workspaceSlug: string, projectId: string, data: TInboxIssue, private store: RootStore) {
|
||||
this.id = data.id;
|
||||
|
|
@ -51,6 +56,7 @@ export class InboxIssueStore implements IInboxIssueStore {
|
|||
this.projectId = projectId;
|
||||
// services
|
||||
this.inboxIssueService = new InboxIssueService();
|
||||
this.issueService = new IssueService();
|
||||
// observable variables should be defined after the initialization of the values
|
||||
makeObservable(this, {
|
||||
id: observable,
|
||||
|
|
@ -65,6 +71,8 @@ export class InboxIssueStore implements IInboxIssueStore {
|
|||
updateInboxIssueDuplicateTo: action,
|
||||
updateInboxIssueSnoozeTill: action,
|
||||
updateIssue: action,
|
||||
updateProjectIssue: action,
|
||||
fetchIssueActivity: action,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -72,12 +80,13 @@ export class InboxIssueStore implements IInboxIssueStore {
|
|||
const previousData: Partial<TInboxIssue> = {
|
||||
status: this.status,
|
||||
};
|
||||
|
||||
try {
|
||||
if (!this.issue.id) return;
|
||||
set(this, "status", status);
|
||||
await this.inboxIssueService.update(this.workspaceSlug, this.projectId, this.issue.id, {
|
||||
const inboxIssue = await this.inboxIssueService.update(this.workspaceSlug, this.projectId, this.issue.id, {
|
||||
status: status,
|
||||
});
|
||||
runInAction(() => set(this, "status", inboxIssue?.status));
|
||||
} catch {
|
||||
runInAction(() => set(this, "status", previousData.status));
|
||||
}
|
||||
|
|
@ -85,20 +94,19 @@ export class InboxIssueStore implements IInboxIssueStore {
|
|||
|
||||
updateInboxIssueDuplicateTo = async (issueId: string) => {
|
||||
const inboxStatus = EInboxIssueStatus.DUPLICATE;
|
||||
|
||||
const previousData: Partial<TInboxIssue> = {
|
||||
status: this.status,
|
||||
duplicate_to: this.duplicate_to,
|
||||
};
|
||||
|
||||
try {
|
||||
if (!this.issue.id) return;
|
||||
set(this, "status", inboxStatus);
|
||||
set(this, "duplicate_to", issueId);
|
||||
const issueResponse = await this.inboxIssueService.update(this.workspaceSlug, this.projectId, this.issue.id, {
|
||||
status: inboxStatus,
|
||||
duplicate_to: issueId,
|
||||
});
|
||||
runInAction(() => {
|
||||
this.status = issueResponse.status;
|
||||
this.duplicate_to = issueResponse.duplicate_to;
|
||||
this.duplicate_issue_detail = issueResponse.duplicate_issue_detail;
|
||||
});
|
||||
|
|
@ -112,19 +120,21 @@ export class InboxIssueStore implements IInboxIssueStore {
|
|||
|
||||
updateInboxIssueSnoozeTill = async (date: Date) => {
|
||||
const inboxStatus = EInboxIssueStatus.SNOOZED;
|
||||
|
||||
const previousData: Partial<TInboxIssue> = {
|
||||
status: this.status,
|
||||
snoozed_till: this.snoozed_till,
|
||||
};
|
||||
|
||||
try {
|
||||
if (!this.issue.id) return;
|
||||
set(this, "status", inboxStatus);
|
||||
set(this, "snoozed_till", date);
|
||||
await this.inboxIssueService.update(this.workspaceSlug, this.projectId, this.issue.id, {
|
||||
const issueResponse = await this.inboxIssueService.update(this.workspaceSlug, this.projectId, this.issue.id, {
|
||||
status: inboxStatus,
|
||||
snoozed_till: new Date(date),
|
||||
});
|
||||
runInAction(() => {
|
||||
this.status = issueResponse?.status;
|
||||
this.snoozed_till = issueResponse?.snoozed_till ? new Date(issueResponse.snoozed_till) : undefined;
|
||||
});
|
||||
} catch {
|
||||
runInAction(() => {
|
||||
set(this, "status", previousData.status);
|
||||
|
|
@ -134,21 +144,49 @@ export class InboxIssueStore implements IInboxIssueStore {
|
|||
};
|
||||
|
||||
updateIssue = async (issue: Partial<TIssue>) => {
|
||||
const inboxIssue = this.issue;
|
||||
const inboxIssue = clone(this.issue);
|
||||
try {
|
||||
if (!this.issue.id) return;
|
||||
Object.keys(issue).forEach((key) => {
|
||||
const issueKey = key as keyof TIssue;
|
||||
set(inboxIssue, issueKey, issue[issueKey]);
|
||||
set(this.issue, issueKey, issue[issueKey]);
|
||||
});
|
||||
await this.inboxIssueService.updateIssue(this.workspaceSlug, this.projectId, this.issue.id, issue);
|
||||
// fetching activity
|
||||
await this.store.issue.issueDetail.fetchActivities(this.workspaceSlug, this.projectId, this.issue.id);
|
||||
this.fetchIssueActivity();
|
||||
} catch {
|
||||
Object.keys(issue).forEach((key) => {
|
||||
const issueKey = key as keyof TIssue;
|
||||
set(inboxIssue, issueKey, inboxIssue[issueKey]);
|
||||
set(this.issue, issueKey, inboxIssue[issueKey]);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
updateProjectIssue = async (issue: Partial<TIssue>) => {
|
||||
const inboxIssue = clone(this.issue);
|
||||
try {
|
||||
if (!this.issue.id) return;
|
||||
Object.keys(issue).forEach((key) => {
|
||||
const issueKey = key as keyof TIssue;
|
||||
set(this.issue, issueKey, issue[issueKey]);
|
||||
});
|
||||
await this.issueService.patchIssue(this.workspaceSlug, this.projectId, this.issue.id, issue);
|
||||
// fetching activity
|
||||
this.fetchIssueActivity();
|
||||
} catch {
|
||||
Object.keys(issue).forEach((key) => {
|
||||
const issueKey = key as keyof TIssue;
|
||||
set(this.issue, issueKey, inboxIssue[issueKey]);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
fetchIssueActivity = async () => {
|
||||
try {
|
||||
if (!this.issue.id) return;
|
||||
await this.store.issue.issueDetail.fetchActivities(this.workspaceSlug, this.projectId, this.issue.id);
|
||||
} catch {
|
||||
console.error("Failed to fetch issue activity");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ type TLoader =
|
|||
|
||||
export interface IProjectInboxStore {
|
||||
currentTab: TInboxIssueCurrentTab;
|
||||
isLoading: TLoader;
|
||||
loader: TLoader;
|
||||
error: { message: string; status: "init-error" | "pagination-error" } | undefined;
|
||||
currentInboxProjectId: string;
|
||||
inboxFilters: Partial<TInboxIssueFilter>;
|
||||
|
|
@ -42,7 +42,7 @@ export interface IProjectInboxStore {
|
|||
getAppliedFiltersCount: number;
|
||||
inboxIssuesArray: IInboxIssueStore[];
|
||||
// helper actions
|
||||
getIssueInboxByIssueId: (issueId: string) => IInboxIssueStore | undefined;
|
||||
getIssueInboxByIssueId: (issueId: string) => IInboxIssueStore;
|
||||
inboxIssueSorting: (issues: IInboxIssueStore[]) => IInboxIssueStore[];
|
||||
inboxIssueQueryParams: (
|
||||
inboxFilters: Partial<TInboxIssueFilter>,
|
||||
|
|
@ -70,7 +70,7 @@ export class ProjectInboxStore implements IProjectInboxStore {
|
|||
PER_PAGE_COUNT = 10;
|
||||
// observables
|
||||
currentTab: TInboxIssueCurrentTab = EInboxIssueCurrentTab.OPEN;
|
||||
isLoading: TLoader = "init-loading";
|
||||
loader: TLoader = "init-loading";
|
||||
error: { message: string; status: "init-error" | "pagination-error" } | undefined = undefined;
|
||||
currentInboxProjectId: string = "";
|
||||
inboxFilters: Partial<TInboxIssueFilter> = {
|
||||
|
|
@ -88,7 +88,7 @@ export class ProjectInboxStore implements IProjectInboxStore {
|
|||
constructor(private store: RootStore) {
|
||||
makeObservable(this, {
|
||||
currentTab: observable.ref,
|
||||
isLoading: observable.ref,
|
||||
loader: observable.ref,
|
||||
error: observable,
|
||||
currentInboxProjectId: observable.ref,
|
||||
inboxFilters: observable,
|
||||
|
|
@ -123,17 +123,17 @@ export class ProjectInboxStore implements IProjectInboxStore {
|
|||
}
|
||||
|
||||
get inboxIssuesArray() {
|
||||
let appliedFilters =
|
||||
this.currentTab === EInboxIssueCurrentTab.OPEN
|
||||
? [EInboxIssueStatus.PENDING, EInboxIssueStatus.SNOOZED]
|
||||
: [EInboxIssueStatus.ACCEPTED, EInboxIssueStatus.DECLINED, EInboxIssueStatus.DUPLICATE];
|
||||
appliedFilters = appliedFilters.filter((filter) => this.inboxFilters?.status?.includes(filter));
|
||||
return this.inboxIssueSorting(
|
||||
Object.values(this.inboxIssues || {}).filter((inbox) =>
|
||||
(this.currentTab === EInboxIssueCurrentTab.OPEN
|
||||
? [EInboxIssueStatus.PENDING, EInboxIssueStatus.SNOOZED]
|
||||
: [EInboxIssueStatus.ACCEPTED, EInboxIssueStatus.DECLINED, EInboxIssueStatus.DUPLICATE]
|
||||
).includes(inbox.status)
|
||||
)
|
||||
Object.values(this.inboxIssues || {}).filter((inbox) => appliedFilters.includes(inbox.status))
|
||||
);
|
||||
}
|
||||
|
||||
getIssueInboxByIssueId = computedFn((issueId: string) => this.inboxIssues?.[issueId] || undefined);
|
||||
getIssueInboxByIssueId = computedFn((issueId: string) => this.inboxIssues?.[issueId]);
|
||||
|
||||
// helpers
|
||||
inboxIssueSorting = (issues: IInboxIssueStore[]) => {
|
||||
|
|
@ -252,9 +252,9 @@ export class ProjectInboxStore implements IProjectInboxStore {
|
|||
set(this, ["inboxIssues"], {});
|
||||
set(this, ["inboxIssuePaginationInfo"], undefined);
|
||||
}
|
||||
if (Object.keys(this.inboxIssues).length === 0) this.isLoading = "init-loading";
|
||||
else this.isLoading = "mutation-loading";
|
||||
if (loadingType) this.isLoading = loadingType;
|
||||
if (Object.keys(this.inboxIssues).length === 0) this.loader = "init-loading";
|
||||
else this.loader = "mutation-loading";
|
||||
if (loadingType) this.loader = loadingType;
|
||||
|
||||
const queryParams = this.inboxIssueQueryParams(
|
||||
this.inboxFilters,
|
||||
|
|
@ -265,7 +265,7 @@ export class ProjectInboxStore implements IProjectInboxStore {
|
|||
const { results, ...paginationInfo } = await this.inboxIssueService.list(workspaceSlug, projectId, queryParams);
|
||||
|
||||
runInAction(() => {
|
||||
this.isLoading = undefined;
|
||||
this.loader = undefined;
|
||||
set(this, "inboxIssuePaginationInfo", paginationInfo);
|
||||
if (results && results.length > 0)
|
||||
results.forEach((value: TInboxIssue) => {
|
||||
|
|
@ -279,7 +279,7 @@ export class ProjectInboxStore implements IProjectInboxStore {
|
|||
});
|
||||
} catch (error) {
|
||||
console.error("Error fetching the inbox issues", error);
|
||||
this.isLoading = undefined;
|
||||
this.loader = undefined;
|
||||
this.error = {
|
||||
message: "Error fetching the inbox issues please try again later.",
|
||||
status: "init-error",
|
||||
|
|
@ -301,7 +301,7 @@ export class ProjectInboxStore implements IProjectInboxStore {
|
|||
(this.inboxIssuePaginationInfo?.total_results &&
|
||||
this.inboxIssuesArray.length < this.inboxIssuePaginationInfo?.total_results))
|
||||
) {
|
||||
this.isLoading = "pagination-loading";
|
||||
this.loader = "pagination-loading";
|
||||
|
||||
const queryParams = this.inboxIssueQueryParams(
|
||||
this.inboxFilters,
|
||||
|
|
@ -312,7 +312,7 @@ export class ProjectInboxStore implements IProjectInboxStore {
|
|||
const { results, ...paginationInfo } = await this.inboxIssueService.list(workspaceSlug, projectId, queryParams);
|
||||
|
||||
runInAction(() => {
|
||||
this.isLoading = undefined;
|
||||
this.loader = undefined;
|
||||
set(this, "inboxIssuePaginationInfo", paginationInfo);
|
||||
if (results && results.length > 0)
|
||||
results.forEach((value: TInboxIssue) => {
|
||||
|
|
@ -327,7 +327,7 @@ export class ProjectInboxStore implements IProjectInboxStore {
|
|||
} else set(this, ["inboxIssuePaginationInfo", "next_page_results"], false);
|
||||
} catch (error) {
|
||||
console.error("Error fetching the inbox issues", error);
|
||||
this.isLoading = undefined;
|
||||
this.loader = undefined;
|
||||
this.error = {
|
||||
message: "Error fetching the paginated inbox issues please try again later.",
|
||||
status: "pagination-error",
|
||||
|
|
@ -348,7 +348,7 @@ export class ProjectInboxStore implements IProjectInboxStore {
|
|||
inboxIssueId: string
|
||||
): Promise<TInboxIssue> => {
|
||||
try {
|
||||
this.isLoading = "issue-loading";
|
||||
this.loader = "issue-loading";
|
||||
const inboxIssue = await this.inboxIssueService.retrieve(workspaceSlug, projectId, inboxIssueId);
|
||||
const issueId = inboxIssue?.issue?.id || undefined;
|
||||
|
||||
|
|
@ -362,12 +362,12 @@ export class ProjectInboxStore implements IProjectInboxStore {
|
|||
await this.store.issue.issueDetail.fetchActivities(workspaceSlug, projectId, issueId);
|
||||
// fetching comments
|
||||
await this.store.issue.issueDetail.fetchComments(workspaceSlug, projectId, issueId);
|
||||
this.isLoading = undefined;
|
||||
this.loader = undefined;
|
||||
}
|
||||
return inboxIssue;
|
||||
} catch (error) {
|
||||
console.error("Error fetching the inbox issue with inbox issue id");
|
||||
this.isLoading = undefined;
|
||||
this.loader = undefined;
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue