From 635a144b11010438d939982b6ebd78e109c9c3fc Mon Sep 17 00:00:00 2001 From: dakshesh14 Date: Thu, 2 Nov 2023 11:53:41 +0530 Subject: [PATCH 1/6] dev: archived issue store --- web/store/archived-issues/issue.store.ts | 52 +++--- .../archived-issues/issue_filters.store.ts | 149 +++++++++++++++++- 2 files changed, 168 insertions(+), 33 deletions(-) diff --git a/web/store/archived-issues/issue.store.ts b/web/store/archived-issues/issue.store.ts index 11c29f25c94..bc38f31ba5f 100644 --- a/web/store/archived-issues/issue.store.ts +++ b/web/store/archived-issues/issue.store.ts @@ -1,10 +1,10 @@ -import { observable, action, computed, makeObservable, runInAction } from "mobx"; +import { observable, action, computed, makeObservable, runInAction, autorun } from "mobx"; // store import { RootStore } from "../root"; // types import { IIssue } from "types"; // services -import { IssueService } from "services/issue"; +import { IssueArchiveService } from "services/issue"; import { sortArrayByDate, sortArrayByPriority } from "constants/kanban-helpers"; import { IIssueGroupWithSubGroupsStructure, @@ -24,9 +24,14 @@ export interface IArchivedIssueStore { ungrouped: IIssueUnGroupedStructure; }; }; + + // services + archivedIssueService: IssueArchiveService; + // computed getIssueType: IIssueType | null; getIssues: IIssueGroupedStructure | IIssueGroupWithSubGroupsStructure | IIssueUnGroupedStructure | null; + // action fetchIssues: (workspaceSlug: string, projectId: string) => Promise; updateIssueStructure: (group_id: string | null, sub_group_id: string | null, issue: IIssue) => void; @@ -49,7 +54,7 @@ export class ArchivedIssueStore implements IArchivedIssueStore { }; } = {}; // service - issueService; + archivedIssueService; rootStore; constructor(_rootStore: RootStore) { @@ -66,26 +71,25 @@ export class ArchivedIssueStore implements IArchivedIssueStore { updateIssueStructure: action, }); this.rootStore = _rootStore; - this.issueService = new IssueService(); + this.archivedIssueService = new IssueArchiveService(); + + autorun(() => { + const workspaceSlug = this.rootStore.workspace.workspaceSlug; + const projectId = this.rootStore.project.projectId; + + if ( + workspaceSlug && + projectId && + this.rootStore.archivedIssueFilters.userDisplayFilters && + this.rootStore.archivedIssueFilters.userFilters + ) + this.fetchIssues(workspaceSlug, projectId); + }); } get getIssueType() { - const groupedLayouts = ["kanban", "list", "calendar"]; - const ungroupedLayouts = ["spreadsheet", "gantt_chart"]; - - const issueLayout = this.rootStore?.issueFilter?.userDisplayFilters?.layout || null; - const issueSubGroup = this.rootStore?.issueFilter?.userDisplayFilters?.sub_group_by || null; - if (!issueLayout) return null; - - const _issueState = groupedLayouts.includes(issueLayout) - ? issueSubGroup - ? "groupWithSubGroups" - : "grouped" - : ungroupedLayouts.includes(issueLayout) - ? "ungrouped" - : null; - - return _issueState || null; + const issueSubGroup = this.rootStore.archivedIssueFilters.userDisplayFilters?.sub_group_by || null; + return issueSubGroup ? "groupWithSubGroups" : "grouped"; } get getIssues() { @@ -122,10 +126,6 @@ export class ArchivedIssueStore implements IArchivedIssueStore { }, }; } - if (issueType === "ungrouped") { - issues = issues as IIssueUnGroupedStructure; - issues = issues.map((i: IIssue) => (i?.id === issue?.id ? { ...i, ...issue } : i)); - } const orderBy = this.rootStore?.issueFilter?.userDisplayFilters?.order_by || ""; if (orderBy === "-created_at") { @@ -154,8 +154,8 @@ export class ArchivedIssueStore implements IArchivedIssueStore { this.rootStore.workspace.setWorkspaceSlug(workspaceSlug); this.rootStore.project.setProjectId(projectId); - const params = this.rootStore?.issueFilter?.appliedFilters; - const issueResponse = await this.issueService.getIssuesWithParams(workspaceSlug, projectId, params); + const params = this.rootStore.archivedIssueFilters.appliedFilters; + const issueResponse = await this.archivedIssueService.getArchivedIssues(workspaceSlug, projectId, params); const issueType = this.getIssueType; if (issueType != null) { diff --git a/web/store/archived-issues/issue_filters.store.ts b/web/store/archived-issues/issue_filters.store.ts index 04a0da9bd6b..edb1549a630 100644 --- a/web/store/archived-issues/issue_filters.store.ts +++ b/web/store/archived-issues/issue_filters.store.ts @@ -1,20 +1,53 @@ -import { observable, computed, makeObservable } from "mobx"; +import { observable, computed, makeObservable, action, runInAction } from "mobx"; // helpers import { handleIssueQueryParamsByLayout } from "helpers/issue.helper"; +// services +import { IssueService } from "services/issue"; +import { ProjectService } from "services/project"; // types import { RootStore } from "../root"; -import { IIssueDisplayFilterOptions, IIssueDisplayProperties, IIssueFilterOptions, TIssueParams } from "types"; +import { + IIssueDisplayFilterOptions, + IIssueDisplayProperties, + IIssueFilterOptions, + TIssueParams, + IProjectViewProps, +} from "types"; export interface IArchivedIssueFilterStore { + loader: boolean; + error: any | null; + + // observables userDisplayProperties: IIssueDisplayProperties; userDisplayFilters: IIssueDisplayFilterOptions; userFilters: IIssueFilterOptions; + // services + projectService: ProjectService; + issueService: IssueService; + // computed appliedFilters: TIssueParams[] | null; + + // actions + fetchUserProjectFilters: (workspaceSlug: string, projectId: string) => Promise; + updateUserFilters: ( + workspaceSlug: string, + projectId: string, + properties: Partial + ) => Promise; + updateDisplayProperties: ( + workspaceSlug: string, + projectId: string, + properties: Partial + ) => Promise; } export class ArchivedIssueFilterStore implements IArchivedIssueFilterStore { + loader: boolean = false; + error: any | null = null; + // observables userFilters: IIssueFilterOptions = { priority: null, @@ -52,6 +85,10 @@ export class ArchivedIssueFilterStore implements IArchivedIssueFilterStore { // root store rootStore; + // services + projectService: ProjectService; + issueService: IssueService; + constructor(_rootStore: RootStore) { makeObservable(this, { // observables @@ -61,9 +98,19 @@ export class ArchivedIssueFilterStore implements IArchivedIssueFilterStore { // computed appliedFilters: computed, + + // actions + fetchUserProjectFilters: action, + updateUserFilters: action, + updateDisplayProperties: action, + computedFilter: action, }); this.rootStore = _rootStore; + + // services + this.issueService = new IssueService(); + this.projectService = new ProjectService(); } computedFilter = (filters: any, filteredParams: any) => { @@ -89,7 +136,7 @@ export class ArchivedIssueFilterStore implements IArchivedIssueFilterStore { labels: this.userFilters?.labels || undefined, start_date: this.userFilters?.start_date || undefined, target_date: this.userFilters?.target_date || undefined, - group_by: this.userDisplayFilters?.group_by || "state", + group_by: this.userDisplayFilters?.group_by, order_by: this.userDisplayFilters?.order_by || "-created_at", sub_group_by: this.userDisplayFilters?.sub_group_by || undefined, type: this.userDisplayFilters?.type || undefined, @@ -98,12 +145,100 @@ export class ArchivedIssueFilterStore implements IArchivedIssueFilterStore { start_target_date: this.userDisplayFilters?.start_target_date || true, }; - const filteredParams = handleIssueQueryParamsByLayout(this.userDisplayFilters.layout, "issues"); + const filteredParams = handleIssueQueryParamsByLayout("list", "issues"); if (filteredParams) filteredRouteParams = this.computedFilter(filteredRouteParams, filteredParams); - if (this.userDisplayFilters.layout === "calendar") filteredRouteParams.group_by = "target_date"; - if (this.userDisplayFilters.layout === "gantt_chart") filteredRouteParams.start_target_date = true; - return filteredRouteParams; } + + updateUserFilters = async (workspaceSlug: string, projectId: string, properties: Partial) => { + const newViewProps = { + display_filters: { + ...this.userDisplayFilters, + ...properties.display_filters, + }, + filters: { + ...this.userFilters, + ...properties.filters, + }, + }; + + // set sub_group_by to null if group_by is set to null + if (newViewProps.display_filters.group_by === null) newViewProps.display_filters.sub_group_by = null; + + // set group_by to state if layout is switched to kanban and group_by is null + if (newViewProps.display_filters.layout === "kanban" && newViewProps.display_filters.group_by === null) + newViewProps.display_filters.group_by = "state"; + + try { + runInAction(() => { + this.userFilters = newViewProps.filters; + this.userDisplayFilters = { + ...newViewProps.display_filters, + layout: "list", + }; + }); + + this.projectService.setProjectView(workspaceSlug, projectId, { + view_props: newViewProps, + }); + } catch (error) { + this.fetchUserProjectFilters(workspaceSlug, projectId); + + runInAction(() => { + this.error = error; + }); + + console.log("Failed to update user filters in issue filter store", error); + } + }; + + updateDisplayProperties = async ( + workspaceSlug: string, + projectId: string, + properties: Partial + ) => { + const newProperties: IIssueDisplayProperties = { + ...this.userDisplayProperties, + ...properties, + }; + + try { + runInAction(() => { + this.userDisplayProperties = newProperties; + }); + + await this.issueService.updateIssueDisplayProperties(workspaceSlug, projectId, newProperties); + } catch (error) { + this.fetchUserProjectFilters(workspaceSlug, projectId); + + runInAction(() => { + this.error = error; + }); + + console.log("Failed to update user display properties in issue filter store", error); + } + }; + + fetchUserProjectFilters = async (workspaceSlug: string, projectId: string) => { + try { + const memberResponse = await this.projectService.projectMemberMe(workspaceSlug, projectId); + const issueProperties = await this.issueService.getIssueDisplayProperties(workspaceSlug, projectId); + + runInAction(() => { + this.userFilters = memberResponse?.view_props?.filters; + this.userDisplayFilters = { + ...(memberResponse?.view_props?.display_filters ?? {}), + layout: "list", + }; + this.userDisplayProperties = issueProperties?.properties; + }); + } catch (error) { + runInAction(() => { + this.error = error; + }); + + console.log("Failed to fetch user filters in issue filter store", error); + } + }; } From 55b672ce1a4c1386f500352aa76e013c2d67bbdb Mon Sep 17 00:00:00 2001 From: dakshesh14 Date: Thu, 2 Nov 2023 11:54:11 +0530 Subject: [PATCH 2/6] dev: archived issue layouts and store binding --- .../headers/project-archived-issues.tsx | 110 ++++++++++++++++-- .../applied-filters/roots/archived-issue.tsx | 81 +++++++++++++ .../filters/applied-filters/roots/index.ts | 1 + .../list/roots/archived-issue-root.tsx | 71 +++++++++++ .../issues/issue-layouts/list/roots/index.ts | 1 + .../roots/archived-issue-layout-root.tsx | 31 +++++ .../issues/issue-layouts/roots/index.ts | 1 + .../[projectId]/archived-issues/index.tsx | 3 +- 8 files changed, 288 insertions(+), 11 deletions(-) create mode 100644 web/components/issues/issue-layouts/filters/applied-filters/roots/archived-issue.tsx create mode 100644 web/components/issues/issue-layouts/list/roots/archived-issue-root.tsx create mode 100644 web/components/issues/issue-layouts/roots/archived-issue-layout-root.tsx diff --git a/web/components/headers/project-archived-issues.tsx b/web/components/headers/project-archived-issues.tsx index a40ab9e6001..6b02abc05a3 100644 --- a/web/components/headers/project-archived-issues.tsx +++ b/web/components/headers/project-archived-issues.tsx @@ -4,37 +4,127 @@ import { useRouter } from "next/router"; import { observer } from "mobx-react-lite"; // hooks import { useMobxStore } from "lib/mobx/store-provider"; -// ui -import { Breadcrumbs, BreadcrumbItem } from "@plane/ui"; +// constants +import { ISSUE_DISPLAY_FILTERS_BY_LAYOUT } from "constants/issue"; // helper import { truncateText } from "helpers/string.helper"; +// ui +import { Breadcrumbs, BreadcrumbItem } from "@plane/ui"; +// icons +import { ArrowLeft } from "lucide-react"; +// components +import { DisplayFiltersSelection, FilterSelection, FiltersDropdown } from "components/issues"; +// types +import type { IIssueDisplayFilterOptions, IIssueDisplayProperties, IIssueFilterOptions } from "types"; export const ProjectArchivedIssuesHeader: FC = observer(() => { const router = useRouter(); const { workspaceSlug, projectId } = router.query; - const { project: projectStore } = useMobxStore(); + const { project: projectStore, archivedIssueFilters: archivedIssueFiltersStore } = useMobxStore(); const projectDetails = workspaceSlug && projectId ? projectStore.getProjectById(workspaceSlug.toString(), projectId.toString()) : undefined; + // for archived issues list layout is the only option + const activeLayout = "list"; + + const handleFiltersUpdate = (key: keyof IIssueFilterOptions, value: string | string[]) => { + if (!workspaceSlug || !projectId) return; + + const newValues = archivedIssueFiltersStore.userFilters?.[key] ?? []; + + if (Array.isArray(value)) { + value.forEach((val) => { + if (!newValues.includes(val)) newValues.push(val); + }); + } else { + if (archivedIssueFiltersStore.userFilters?.[key]?.includes(value)) newValues.splice(newValues.indexOf(value), 1); + else newValues.push(value); + } + + archivedIssueFiltersStore.updateUserFilters(workspaceSlug.toString(), projectId.toString(), { + filters: { + [key]: newValues, + }, + }); + }; + + const handleDisplayFiltersUpdate = (updatedDisplayFilter: Partial) => { + if (!workspaceSlug || !projectId) return; + + console.log(updatedDisplayFilter); + + archivedIssueFiltersStore.updateUserFilters(workspaceSlug.toString(), projectId.toString(), { + display_filters: { + ...archivedIssueFiltersStore.userDisplayFilters, + ...updatedDisplayFilter, + }, + }); + }; + + const handleDisplayPropertiesUpdate = (property: Partial) => { + if (!workspaceSlug || !projectId) return; + + archivedIssueFiltersStore.updateDisplayProperties(workspaceSlug.toString(), projectId.toString(), property); + }; + return (
+
+ +
router.back()}> - - -

Projects

-
- - - + + +

Projects

+
+ + } + /> +
+ + {/* filter options */} +
+ + m.member)} + states={projectStore.states?.[projectId?.toString() ?? ""] ?? undefined} + /> + + + + +
); }); diff --git a/web/components/issues/issue-layouts/filters/applied-filters/roots/archived-issue.tsx b/web/components/issues/issue-layouts/filters/applied-filters/roots/archived-issue.tsx new file mode 100644 index 00000000000..20e20d34e37 --- /dev/null +++ b/web/components/issues/issue-layouts/filters/applied-filters/roots/archived-issue.tsx @@ -0,0 +1,81 @@ +import { useRouter } from "next/router"; +import { observer } from "mobx-react-lite"; + +// mobx store +import { useMobxStore } from "lib/mobx/store-provider"; +// components +import { AppliedFiltersList } from "components/issues"; +// types +import { IIssueFilterOptions } from "types"; + +export const ArchivedIssueAppliedFiltersRoot: React.FC = observer(() => { + const router = useRouter(); + const { workspaceSlug, projectId } = router.query; + + const { archivedIssueFilters: archivedIssueFiltersStore, project: projectStore } = useMobxStore(); + + const userFilters = archivedIssueFiltersStore.userFilters; + + // filters whose value not null or empty array + const appliedFilters: IIssueFilterOptions = {}; + Object.entries(userFilters).forEach(([key, value]) => { + if (!value) return; + + if (Array.isArray(value) && value.length === 0) return; + + appliedFilters[key as keyof IIssueFilterOptions] = value; + }); + + const handleRemoveFilter = (key: keyof IIssueFilterOptions, value: string | null) => { + if (!workspaceSlug || !projectId) return; + + // remove all values of the key if value is null + if (!value) { + archivedIssueFiltersStore.updateUserFilters(workspaceSlug.toString(), projectId.toString(), { + filters: { + [key]: null, + }, + }); + return; + } + + // remove the passed value from the key + let newValues = archivedIssueFiltersStore.userFilters?.[key] ?? []; + newValues = newValues.filter((val) => val !== value); + + archivedIssueFiltersStore.updateUserFilters(workspaceSlug.toString(), projectId.toString(), { + filters: { + [key]: newValues, + }, + }); + }; + + const handleClearAllFilters = () => { + if (!workspaceSlug || !projectId) return; + + const newFilters: IIssueFilterOptions = {}; + Object.keys(userFilters).forEach((key) => { + newFilters[key as keyof IIssueFilterOptions] = null; + }); + + archivedIssueFiltersStore.updateUserFilters(workspaceSlug.toString(), projectId.toString(), { + filters: { ...newFilters }, + }); + }; + + // return if no filters are applied + if (Object.keys(appliedFilters).length === 0) return null; + + return ( +
+ m.member)} + states={projectStore.states?.[projectId?.toString() ?? ""]} + /> +
+ ); +}); diff --git a/web/components/issues/issue-layouts/filters/applied-filters/roots/index.ts b/web/components/issues/issue-layouts/filters/applied-filters/roots/index.ts index c7280e4700b..e5030d7c4e6 100644 --- a/web/components/issues/issue-layouts/filters/applied-filters/roots/index.ts +++ b/web/components/issues/issue-layouts/filters/applied-filters/roots/index.ts @@ -3,3 +3,4 @@ export * from "./global-view-root"; export * from "./module-root"; export * from "./project-view-root"; export * from "./project-root"; +export * from "./archived-issue"; diff --git a/web/components/issues/issue-layouts/list/roots/archived-issue-root.tsx b/web/components/issues/issue-layouts/list/roots/archived-issue-root.tsx new file mode 100644 index 00000000000..7100e657891 --- /dev/null +++ b/web/components/issues/issue-layouts/list/roots/archived-issue-root.tsx @@ -0,0 +1,71 @@ +import { FC } from "react"; +import { useRouter } from "next/router"; +import { observer } from "mobx-react-lite"; +// hooks +import { useMobxStore } from "lib/mobx/store-provider"; +// components +import { List } from "../default"; +// helpers +import { orderArrayBy } from "helpers/array.helper"; +// types +import { IIssue } from "types"; +// constants +import { ISSUE_STATE_GROUPS, ISSUE_PRIORITIES } from "constants/issue"; + +export const ArchivedIssueListLayout: FC = observer(() => { + const router = useRouter(); + const { workspaceSlug, projectId } = router.query; + + const { + project: projectStore, + archivedIssueFilters: archivedIssueFiltersStore, + archivedIssues: archivedIssueStore, + } = useMobxStore(); + + // derived values + const issues = archivedIssueStore.getIssues; + const display_properties = archivedIssueFiltersStore?.userDisplayProperties || null; + const group_by: string | null = archivedIssueFiltersStore?.userDisplayFilters?.group_by || null; + + const handleIssues = (group_by: string | null, issue: IIssue, action: "update" | "delete" | "convertToIssue") => { + if (!workspaceSlug || !projectId) return; + + console.log({ + group_by, + issue, + action, + }); + }; + + const projectDetails = projectId ? projectStore.project_details[projectId.toString()] : null; + + const states = projectStore?.projectStates || null; + const priorities = ISSUE_PRIORITIES || null; + const labels = projectStore?.projectLabels || null; + const members = projectStore?.projectMembers || null; + const stateGroups = ISSUE_STATE_GROUPS || null; + const projects = workspaceSlug ? projectStore?.projects[workspaceSlug.toString()] || null : null; + const estimates = + projectDetails?.estimate !== null + ? projectStore.projectEstimates?.find((e) => e.id === projectDetails?.estimate) || null + : null; + + return ( +
+ null} + display_properties={display_properties} + states={states} + stateGroups={stateGroups} + priorities={priorities} + labels={labels} + members={members?.map((m) => m.member) ?? null} + projects={projects} + estimates={estimates?.points ? orderArrayBy(estimates.points, "key") : null} + /> +
+ ); +}); diff --git a/web/components/issues/issue-layouts/list/roots/index.ts b/web/components/issues/issue-layouts/list/roots/index.ts index 139c09a7ae0..aae6f833cc5 100644 --- a/web/components/issues/issue-layouts/list/roots/index.ts +++ b/web/components/issues/issue-layouts/list/roots/index.ts @@ -3,3 +3,4 @@ export * from "./module-root"; export * from "./profile-issues-root"; export * from "./project-root"; export * from "./project-view-root"; +export * from "./archived-issue-root"; diff --git a/web/components/issues/issue-layouts/roots/archived-issue-layout-root.tsx b/web/components/issues/issue-layouts/roots/archived-issue-layout-root.tsx new file mode 100644 index 00000000000..2906de3f193 --- /dev/null +++ b/web/components/issues/issue-layouts/roots/archived-issue-layout-root.tsx @@ -0,0 +1,31 @@ +import React from "react"; +import { useRouter } from "next/router"; +import { observer } from "mobx-react-lite"; +import useSWR from "swr"; +// mobx store +import { useMobxStore } from "lib/mobx/store-provider"; +// components +import { ArchivedIssueListLayout, ArchivedIssueAppliedFiltersRoot } from "components/issues"; + +export const ArchivedIssueLayoutRoot: React.FC = observer(() => { + const router = useRouter(); + const { workspaceSlug, projectId } = router.query; + + const { archivedIssueFilters: archivedIssueFiltersStore, archivedIssues: archivedIssueStore } = useMobxStore(); + + useSWR(workspaceSlug && projectId ? `ARCHIVED_FILTERS_AND_ISSUES_${projectId.toString()}` : null, async () => { + if (workspaceSlug && projectId) { + await archivedIssueFiltersStore.fetchUserProjectFilters(workspaceSlug.toString(), projectId.toString()); + await archivedIssueStore.fetchIssues(workspaceSlug.toString(), projectId.toString()); + } + }); + + return ( +
+ +
+ +
+
+ ); +}); diff --git a/web/components/issues/issue-layouts/roots/index.ts b/web/components/issues/issue-layouts/roots/index.ts index 742bc43b91c..515d73403f1 100644 --- a/web/components/issues/issue-layouts/roots/index.ts +++ b/web/components/issues/issue-layouts/roots/index.ts @@ -3,3 +3,4 @@ export * from "./global-view-layout-root"; export * from "./module-layout-root"; export * from "./project-layout-root"; export * from "./project-view-layout-root"; +export * from "./archived-issue-layout-root"; diff --git a/web/pages/[workspaceSlug]/projects/[projectId]/archived-issues/index.tsx b/web/pages/[workspaceSlug]/projects/[projectId]/archived-issues/index.tsx index 75e92448ec2..750927585fc 100644 --- a/web/pages/[workspaceSlug]/projects/[projectId]/archived-issues/index.tsx +++ b/web/pages/[workspaceSlug]/projects/[projectId]/archived-issues/index.tsx @@ -4,6 +4,7 @@ import { useRouter } from "next/router"; import { AppLayout } from "layouts/app-layout"; // contexts import { IssueViewContextProvider } from "contexts/issue-view.context"; +import { ArchivedIssueLayoutRoot } from "components/issues"; // ui import { ArchiveIcon } from "@plane/ui"; import { ProjectArchivedIssuesHeader } from "components/headers"; @@ -32,7 +33,7 @@ const ProjectArchivedIssues: NextPage = () => { - {/* */} + From 08ae2cfbbfd3ffb7dc9f86874682fb93df025d04 Mon Sep 17 00:00:00 2001 From: dakshesh14 Date: Thu, 2 Nov 2023 13:12:12 +0530 Subject: [PATCH 3/6] dev: archived issue detail store --- .../headers/project-archived-issues.tsx | 2 - .../issues/delete-archived-issue-modal.tsx | 111 ++++++++++++ web/components/issues/index.ts | 3 + .../list/roots/archived-issue-root.tsx | 17 +- .../quick-action-dropdowns/archived-issue.tsx | 76 ++++++++ .../quick-action-dropdowns/index.ts | 1 + web/store/archived-issues/index.ts | 1 + web/store/archived-issues/issue.store.ts | 46 +++++ .../archived-issues/issue_detail.store.ts | 162 ++++++++++++++++++ web/store/root.ts | 4 + 10 files changed, 413 insertions(+), 10 deletions(-) create mode 100644 web/components/issues/delete-archived-issue-modal.tsx create mode 100644 web/components/issues/issue-layouts/quick-action-dropdowns/archived-issue.tsx create mode 100644 web/store/archived-issues/issue_detail.store.ts diff --git a/web/components/headers/project-archived-issues.tsx b/web/components/headers/project-archived-issues.tsx index 6b02abc05a3..281fcbe9d49 100644 --- a/web/components/headers/project-archived-issues.tsx +++ b/web/components/headers/project-archived-issues.tsx @@ -55,8 +55,6 @@ export const ProjectArchivedIssuesHeader: FC = observer(() => { const handleDisplayFiltersUpdate = (updatedDisplayFilter: Partial) => { if (!workspaceSlug || !projectId) return; - console.log(updatedDisplayFilter); - archivedIssueFiltersStore.updateUserFilters(workspaceSlug.toString(), projectId.toString(), { display_filters: { ...archivedIssueFiltersStore.userDisplayFilters, diff --git a/web/components/issues/delete-archived-issue-modal.tsx b/web/components/issues/delete-archived-issue-modal.tsx new file mode 100644 index 00000000000..aa91757781f --- /dev/null +++ b/web/components/issues/delete-archived-issue-modal.tsx @@ -0,0 +1,111 @@ +import { useEffect, useState, Fragment } from "react"; +import { useRouter } from "next/router"; +import { observer } from "mobx-react-lite"; +import { Dialog, Transition } from "@headlessui/react"; +import { AlertTriangle } from "lucide-react"; +// mobx store +import { useMobxStore } from "lib/mobx/store-provider"; +// ui +import { Button } from "@plane/ui"; +// types +import type { IIssue } from "types"; + +type Props = { + isOpen: boolean; + handleClose: () => void; + data: IIssue; + onSubmit?: () => Promise; +}; + +export const DeleteArchivedIssueModal: React.FC = observer((props) => { + const { data, isOpen, handleClose, onSubmit } = props; + + const router = useRouter(); + const { workspaceSlug } = router.query; + + const { archivedIssues: archivedIssueStore } = useMobxStore(); + + const [isDeleteLoading, setIsDeleteLoading] = useState(false); + + useEffect(() => { + setIsDeleteLoading(false); + }, [isOpen]); + + const onClose = () => { + setIsDeleteLoading(false); + handleClose(); + }; + + const handleIssueDelete = async () => { + if (!workspaceSlug) return; + + setIsDeleteLoading(true); + + await archivedIssueStore.deleteArchivedIssue(workspaceSlug.toString(), data.project, data.id); + + if (onSubmit) await onSubmit().finally(() => setIsDeleteLoading(false)); + }; + + return ( + + + +
+ + +
+
+ + +
+
+ + + +

Delete Archived Issue

+
+
+ +

+ Are you sure you want to delete issue{" "} + + {data?.project_detail.identifier}-{data?.sequence_id} + + {""}? All of the data related to the archived issue will be permanently removed. This action + cannot be undone. +

+
+
+ + +
+
+
+
+
+
+
+
+ ); +}); diff --git a/web/components/issues/index.ts b/web/components/issues/index.ts index f48bb3894fc..3768d66cda4 100644 --- a/web/components/issues/index.ts +++ b/web/components/issues/index.ts @@ -20,3 +20,6 @@ export * from "./confirm-issue-discard"; export * from "./draft-issue-form"; export * from "./draft-issue-modal"; export * from "./delete-draft-issue-modal"; + +// archived issue +export * from "./delete-archived-issue-modal"; diff --git a/web/components/issues/issue-layouts/list/roots/archived-issue-root.tsx b/web/components/issues/issue-layouts/list/roots/archived-issue-root.tsx index 7100e657891..7992c2d1a39 100644 --- a/web/components/issues/issue-layouts/list/roots/archived-issue-root.tsx +++ b/web/components/issues/issue-layouts/list/roots/archived-issue-root.tsx @@ -5,6 +5,7 @@ import { observer } from "mobx-react-lite"; import { useMobxStore } from "lib/mobx/store-provider"; // components import { List } from "../default"; +import { ArchivedIssueQuickActions } from "components/issues"; // helpers import { orderArrayBy } from "helpers/array.helper"; // types @@ -18,8 +19,8 @@ export const ArchivedIssueListLayout: FC = observer(() => { const { project: projectStore, - archivedIssueFilters: archivedIssueFiltersStore, archivedIssues: archivedIssueStore, + archivedIssueFilters: archivedIssueFiltersStore, } = useMobxStore(); // derived values @@ -27,14 +28,12 @@ export const ArchivedIssueListLayout: FC = observer(() => { const display_properties = archivedIssueFiltersStore?.userDisplayProperties || null; const group_by: string | null = archivedIssueFiltersStore?.userDisplayFilters?.group_by || null; - const handleIssues = (group_by: string | null, issue: IIssue, action: "update" | "delete" | "convertToIssue") => { + const handleIssues = (group_by: string | null, issue: IIssue, action: "delete" | "update") => { if (!workspaceSlug || !projectId) return; - console.log({ - group_by, - issue, - action, - }); + if (action === "delete") { + archivedIssueStore.deleteArchivedIssue(group_by, null, issue); + } }; const projectDetails = projectId ? projectStore.project_details[projectId.toString()] : null; @@ -56,7 +55,9 @@ export const ArchivedIssueListLayout: FC = observer(() => { issues={issues} group_by={group_by} handleIssues={handleIssues} - quickActions={() => null} + quickActions={(group_by, issue) => ( + handleIssues(group_by, issue, "delete")} /> + )} display_properties={display_properties} states={states} stateGroups={stateGroups} diff --git a/web/components/issues/issue-layouts/quick-action-dropdowns/archived-issue.tsx b/web/components/issues/issue-layouts/quick-action-dropdowns/archived-issue.tsx new file mode 100644 index 00000000000..428fca5d485 --- /dev/null +++ b/web/components/issues/issue-layouts/quick-action-dropdowns/archived-issue.tsx @@ -0,0 +1,76 @@ +import { useState } from "react"; +import { useRouter } from "next/router"; +import { CustomMenu } from "@plane/ui"; +import { Link, Trash2 } from "lucide-react"; +// hooks +import useToast from "hooks/use-toast"; +// components +import { DeleteArchivedIssueModal } from "components/issues"; +// helpers +import { copyUrlToClipboard } from "helpers/string.helper"; +// types +import { IIssue } from "types"; + +type Props = { + issue: IIssue; + handleDelete: () => Promise; +}; + +export const ArchivedIssueQuickActions: React.FC = (props) => { + const { issue, handleDelete } = props; + + const router = useRouter(); + const { workspaceSlug } = router.query; + + // states + const [deleteIssueModal, setDeleteIssueModal] = useState(false); + + const { setToastAlert } = useToast(); + + const handleCopyIssueLink = () => { + copyUrlToClipboard(`/${workspaceSlug}/projects/${issue.project}/archived-issues/${issue.id}`).then(() => + setToastAlert({ + type: "success", + title: "Link copied", + message: "Issue link copied to clipboard", + }) + ); + }; + + return ( + <> + setDeleteIssueModal(false)} + onSubmit={handleDelete} + /> + + { + e.preventDefault(); + e.stopPropagation(); + handleCopyIssueLink(); + }} + > +
+ + Copy link +
+
+ { + e.preventDefault(); + e.stopPropagation(); + setDeleteIssueModal(true); + }} + > +
+ + Delete issue +
+
+
+ + ); +}; diff --git a/web/components/issues/issue-layouts/quick-action-dropdowns/index.ts b/web/components/issues/issue-layouts/quick-action-dropdowns/index.ts index 0d281a2a353..642c3903242 100644 --- a/web/components/issues/issue-layouts/quick-action-dropdowns/index.ts +++ b/web/components/issues/issue-layouts/quick-action-dropdowns/index.ts @@ -1,3 +1,4 @@ export * from "./cycle-issue"; export * from "./module-issue"; export * from "./project-issue"; +export * from "./archived-issue"; diff --git a/web/store/archived-issues/index.ts b/web/store/archived-issues/index.ts index abfd52768ec..b1230f1c9e4 100644 --- a/web/store/archived-issues/index.ts +++ b/web/store/archived-issues/index.ts @@ -1,2 +1,3 @@ export * from "./issue.store"; export * from "./issue_filters.store"; +export * from "./issue_detail.store"; diff --git a/web/store/archived-issues/issue.store.ts b/web/store/archived-issues/issue.store.ts index bc38f31ba5f..6d10cd615ac 100644 --- a/web/store/archived-issues/issue.store.ts +++ b/web/store/archived-issues/issue.store.ts @@ -24,6 +24,11 @@ export interface IArchivedIssueStore { ungrouped: IIssueUnGroupedStructure; }; }; + issueDetail: { + [project_id: string]: { + [issue_id: string]: IIssue; + }; + }; // services archivedIssueService: IssueArchiveService; @@ -35,6 +40,7 @@ export interface IArchivedIssueStore { // action fetchIssues: (workspaceSlug: string, projectId: string) => Promise; updateIssueStructure: (group_id: string | null, sub_group_id: string | null, issue: IIssue) => void; + deleteArchivedIssue: (group: string | null, sub_group: string | null, issue: IIssue) => Promise; } export class ArchivedIssueStore implements IArchivedIssueStore { @@ -53,6 +59,8 @@ export class ArchivedIssueStore implements IArchivedIssueStore { ungrouped: IIssue[]; }; } = {}; + issueDetail: IArchivedIssueStore["issueDetail"] = {}; + // service archivedIssueService; rootStore; @@ -63,12 +71,15 @@ export class ArchivedIssueStore implements IArchivedIssueStore { loader: observable.ref, error: observable.ref, issues: observable.ref, + // computed getIssueType: computed, getIssues: computed, + // actions fetchIssues: action, updateIssueStructure: action, + deleteArchivedIssue: action, }); this.rootStore = _rootStore; this.archivedIssueService = new IssueArchiveService(); @@ -181,4 +192,39 @@ export class ArchivedIssueStore implements IArchivedIssueStore { return error; } }; + + /** + * @description Function to delete issue from the store. NOTE: This function is not deleting issue from the backend. + */ + deleteArchivedIssue = async (group_id: string | null, sub_group_id: string | null, issue: IIssue) => { + const projectId: string | null = issue?.project; + const issueType = this.getIssueType; + if (!projectId || !issueType) return null; + + let issues: IIssueGroupedStructure | IIssueGroupWithSubGroupsStructure | IIssueUnGroupedStructure | null = + this.getIssues; + if (!issues) return null; + + if (issueType === "grouped" && group_id) { + issues = issues as IIssueGroupedStructure; + issues = { + ...issues, + [group_id]: issues[group_id].filter((i) => i?.id !== issue?.id), + }; + } + if (issueType === "groupWithSubGroups" && group_id && sub_group_id) { + issues = issues as IIssueGroupWithSubGroupsStructure; + issues = { + ...issues, + [sub_group_id]: { + ...issues[sub_group_id], + [group_id]: issues[sub_group_id][group_id].filter((i) => i?.id !== issue?.id), + }, + }; + } + + runInAction(() => { + this.issues = { ...this.issues, [projectId]: { ...this.issues[projectId], [issueType]: issues } }; + }); + }; } diff --git a/web/store/archived-issues/issue_detail.store.ts b/web/store/archived-issues/issue_detail.store.ts new file mode 100644 index 00000000000..ea7e99c2941 --- /dev/null +++ b/web/store/archived-issues/issue_detail.store.ts @@ -0,0 +1,162 @@ +import { observable, action, makeObservable, runInAction } from "mobx"; +// store +import { RootStore } from "../root"; +// types +import { IIssue } from "types"; +// services +import { IssueArchiveService } from "services/issue"; + +export interface IArchivedIssueDetailStore { + loader: boolean; + error: any | null; + // issues + issueDetail: { + [project_id: string]: { + [issue_id: string]: IIssue; + }; + }; + + // services + archivedIssueService: IssueArchiveService; + + // action + deleteArchivedIssue: (workspaceSlug: string, projectId: string, issuesId: string) => Promise; + unarchiveIssue: (workspaceSlug: string, projectId: string, issueId: string) => Promise; + fetchIssueDetails: (workspaceSlug: string, projectId: string, issueId: string) => Promise; +} + +export class ArchivedIssueDetailStore implements IArchivedIssueDetailStore { + loader: boolean = false; + error: any | null = null; + issueDetail: IArchivedIssueDetailStore["issueDetail"] = {}; + + // service + archivedIssueService; + rootStore; + + constructor(_rootStore: RootStore) { + makeObservable(this, { + // observable + loader: observable.ref, + error: observable.ref, + issueDetail: observable.ref, + + // actions + deleteArchivedIssue: action, + unarchiveIssue: action, + fetchIssueDetails: action, + }); + this.rootStore = _rootStore; + this.archivedIssueService = new IssueArchiveService(); + } + + /** + * @description Function to delete archived issue from the detail store and backend. + */ + deleteArchivedIssue = async (workspaceSlug: string, projectId: string, issuesId: string) => { + const originalIssues = { ...this.issueDetail[projectId] }; + + const _issues = { ...this.issueDetail[projectId] }; + delete _issues[issuesId]; + + // optimistically deleting item from store + runInAction(() => { + this.issueDetail = { + ...this.issueDetail, + [projectId]: _issues, + }; + }); + + try { + runInAction(() => { + this.loader = true; + this.error = null; + }); + + // deleting using api + const issueResponse = await this.archivedIssueService.deleteArchivedIssue(workspaceSlug, projectId, issuesId); + + runInAction(() => { + this.loader = false; + this.error = null; + }); + + return issueResponse; + } catch (error) { + console.error("Error: Fetching error in issues", error); + runInAction(() => { + this.loader = false; + this.error = error; + // reverting back to original issues + this.issueDetail = { + ...this.issueDetail, + [projectId]: originalIssues, + }; + }); + return error; + } + }; + + fetchIssueDetails = async (workspaceSlug: string, projectId: string, issueId: string) => { + try { + runInAction(() => { + this.loader = true; + this.error = null; + }); + + const issueResponse = await this.archivedIssueService.retrieveArchivedIssue(workspaceSlug, projectId, issueId); + + runInAction(() => { + this.loader = false; + this.error = null; + this.issueDetail = { + ...this.issueDetail, + [projectId]: { + ...this.issueDetail[projectId], + [issueId]: issueResponse, + }, + }; + }); + + return issueResponse; + } catch (error) { + console.error("Error: Fetching error in issues", error); + runInAction(() => { + this.loader = false; + this.error = error; + }); + return error; + } + }; + + unarchiveIssue = async (workspaceSlug: string, projectId: string, issueId: string) => { + try { + runInAction(() => { + this.loader = true; + this.error = null; + }); + + const issueResponse = await this.archivedIssueService.unarchiveIssue(workspaceSlug, projectId, issueId); + this.rootStore.archivedIssues.fetchIssues(workspaceSlug, projectId); + + // deleting from issue detail store + const _issues = { ...this.issueDetail[projectId] }; + delete _issues[issueId]; + runInAction(() => { + this.issueDetail = { + ...this.issueDetail, + [projectId]: _issues, + }; + }); + + return issueResponse; + } catch (error) { + console.error("Error: Fetching error in issues", error); + runInAction(() => { + this.loader = false; + this.error = error; + }); + return error; + } + }; +} diff --git a/web/store/root.ts b/web/store/root.ts index 525d18e7201..9db7f521c12 100644 --- a/web/store/root.ts +++ b/web/store/root.ts @@ -85,6 +85,8 @@ import { IArchivedIssueStore, ArchivedIssueFilterStore, IArchivedIssueFilterStore, + ArchivedIssueDetailStore, + IArchivedIssueDetailStore, } from "store/archived-issues"; import { DraftIssueStore, IDraftIssueStore, DraftIssueFilterStore, IDraftIssueFilterStore } from "store/draft-issues"; import { @@ -149,6 +151,7 @@ export class RootStore { profileIssueFilters: IProfileIssueFilterStore; archivedIssues: IArchivedIssueStore; + archivedIssueDetail: IArchivedIssueDetailStore; archivedIssueFilters: IArchivedIssueFilterStore; draftIssues: IDraftIssueStore; @@ -208,6 +211,7 @@ export class RootStore { this.profileIssueFilters = new ProfileIssueFilterStore(this); this.archivedIssues = new ArchivedIssueStore(this); + this.archivedIssueDetail = new ArchivedIssueDetailStore(this); this.archivedIssueFilters = new ArchivedIssueFilterStore(this); this.draftIssues = new DraftIssueStore(this); From 13242dd33d3dbb853e5f86ae24bd69a6d092e96f Mon Sep 17 00:00:00 2001 From: dakshesh14 Date: Thu, 2 Nov 2023 14:00:32 +0530 Subject: [PATCH 4/6] dev: is read only --- .../issues/issue-layouts/list/block.tsx | 4 +++- .../issues/issue-layouts/list/blocks-list.tsx | 4 +++- .../issues/issue-layouts/list/default.tsx | 13 +++++++++++++ .../issues/issue-layouts/list/properties.tsx | 17 +++++++++-------- .../list/roots/archived-issue-root.tsx | 1 + 5 files changed, 29 insertions(+), 10 deletions(-) diff --git a/web/components/issues/issue-layouts/list/block.tsx b/web/components/issues/issue-layouts/list/block.tsx index a7d24a6e838..325f0c58604 100644 --- a/web/components/issues/issue-layouts/list/block.tsx +++ b/web/components/issues/issue-layouts/list/block.tsx @@ -12,10 +12,11 @@ interface IssueBlockProps { handleIssues: (group_by: string | null, issue: IIssue, action: "update" | "delete") => void; quickActions: (group_by: string | null, issue: IIssue) => React.ReactNode; display_properties: any; + isReadonly?: boolean; } export const IssueBlock: React.FC = (props) => { - const { columnId, issue, handleIssues, quickActions, display_properties } = props; + const { columnId, issue, handleIssues, quickActions, display_properties, isReadonly } = props; const updateIssue = (group_by: string | null, issueToUpdate: IIssue) => { handleIssues(group_by, issueToUpdate, "update"); @@ -48,6 +49,7 @@ export const IssueBlock: React.FC = (props) => { diff --git a/web/components/issues/issue-layouts/list/blocks-list.tsx b/web/components/issues/issue-layouts/list/blocks-list.tsx index 31bba3bedd4..5ecd92e24cf 100644 --- a/web/components/issues/issue-layouts/list/blocks-list.tsx +++ b/web/components/issues/issue-layouts/list/blocks-list.tsx @@ -7,13 +7,14 @@ import { IIssue } from "types"; interface Props { columnId: string; issues: IIssue[]; + isReadonly?: boolean; handleIssues: (group_by: string | null, issue: IIssue, action: "update" | "delete") => void; quickActions: (group_by: string | null, issue: IIssue) => React.ReactNode; display_properties: any; } export const IssueBlocksList: FC = (props) => { - const { columnId, issues, handleIssues, quickActions, display_properties } = props; + const { columnId, issues, handleIssues, quickActions, display_properties, isReadonly } = props; return (
@@ -25,6 +26,7 @@ export const IssueBlocksList: FC = (props) => { issue={issue} handleIssues={handleIssues} quickActions={quickActions} + isReadonly={isReadonly} display_properties={display_properties} /> )) diff --git a/web/components/issues/issue-layouts/list/default.tsx b/web/components/issues/issue-layouts/list/default.tsx index 83e4a11e0c8..1d443a73f00 100644 --- a/web/components/issues/issue-layouts/list/default.tsx +++ b/web/components/issues/issue-layouts/list/default.tsx @@ -12,6 +12,7 @@ export interface IGroupByList { issues: any; group_by: string | null; list: any; + isReadonly?: boolean; listKey: string; handleIssues: (group_by: string | null, issue: IIssue, action: "update" | "delete") => void; quickActions: (group_by: string | null, issue: IIssue) => React.ReactNode; @@ -25,6 +26,7 @@ const GroupByList: React.FC = observer((props) => { issues, group_by, list, + isReadonly, listKey, handleIssues, quickActions, @@ -56,6 +58,7 @@ const GroupByList: React.FC = observer((props) => { handleIssues={handleIssues} quickActions={quickActions} display_properties={display_properties} + isReadonly={isReadonly} /> )} {enableQuickIssueCreate && ( @@ -76,6 +79,7 @@ const GroupByList: React.FC = observer((props) => { export interface IList { issues: any; group_by: string | null; + isReadonly?: boolean; handleDragDrop?: (result: any) => void | undefined; handleIssues: (group_by: string | null, issue: IIssue, action: "update" | "delete") => void; quickActions: (group_by: string | null, issue: IIssue) => React.ReactNode; @@ -94,6 +98,7 @@ export const List: React.FC = observer((props) => { const { issues, group_by, + isReadonly, handleIssues, quickActions, display_properties, @@ -120,6 +125,7 @@ export const List: React.FC = observer((props) => { display_properties={display_properties} is_list enableQuickIssueCreate={enableQuickIssueCreate} + isReadonly={isReadonly} /> )} @@ -133,6 +139,7 @@ export const List: React.FC = observer((props) => { quickActions={quickActions} display_properties={display_properties} enableQuickIssueCreate={enableQuickIssueCreate} + isReadonly={isReadonly} /> )} @@ -146,6 +153,7 @@ export const List: React.FC = observer((props) => { quickActions={quickActions} display_properties={display_properties} enableQuickIssueCreate={enableQuickIssueCreate} + isReadonly={isReadonly} /> )} @@ -159,6 +167,7 @@ export const List: React.FC = observer((props) => { quickActions={quickActions} display_properties={display_properties} enableQuickIssueCreate={enableQuickIssueCreate} + isReadonly={isReadonly} /> )} @@ -172,6 +181,7 @@ export const List: React.FC = observer((props) => { quickActions={quickActions} display_properties={display_properties} enableQuickIssueCreate={enableQuickIssueCreate} + isReadonly={isReadonly} /> )} @@ -185,6 +195,7 @@ export const List: React.FC = observer((props) => { quickActions={quickActions} display_properties={display_properties} enableQuickIssueCreate={enableQuickIssueCreate} + isReadonly={isReadonly} /> )} @@ -198,6 +209,7 @@ export const List: React.FC = observer((props) => { quickActions={quickActions} display_properties={display_properties} enableQuickIssueCreate={enableQuickIssueCreate} + isReadonly={isReadonly} /> )} @@ -211,6 +223,7 @@ export const List: React.FC = observer((props) => { quickActions={quickActions} display_properties={display_properties} enableQuickIssueCreate={enableQuickIssueCreate} + isReadonly={isReadonly} /> )}
diff --git a/web/components/issues/issue-layouts/list/properties.tsx b/web/components/issues/issue-layouts/list/properties.tsx index 7e624f8079a..9ca868c1e60 100644 --- a/web/components/issues/issue-layouts/list/properties.tsx +++ b/web/components/issues/issue-layouts/list/properties.tsx @@ -18,10 +18,11 @@ export interface IKanBanProperties { issue: IIssue; handleIssues: (group_by: string | null, issue: IIssue) => void; display_properties: any; + isReadonly?: boolean; } export const KanBanProperties: FC = observer((props) => { - const { columnId: group_id, issue, handleIssues, display_properties } = props; + const { columnId: group_id, issue, handleIssues, display_properties, isReadonly } = props; const handleState = (state: IState) => { handleIssues(!group_id && group_id === "null" ? null : group_id, { ...issue, state: state.id }); @@ -61,7 +62,7 @@ export const KanBanProperties: FC = observer((props) => { value={issue?.state_detail || null} hideDropdownArrow onChange={handleState} - disabled={false} + disabled={isReadonly} /> )} @@ -70,7 +71,7 @@ export const KanBanProperties: FC = observer((props) => { )} @@ -81,7 +82,7 @@ export const KanBanProperties: FC = observer((props) => { projectId={issue?.project_detail?.id || null} value={issue?.labels || null} onChange={handleLabel} - disabled={false} + disabled={isReadonly} hideDropdownArrow /> )} @@ -93,7 +94,7 @@ export const KanBanProperties: FC = observer((props) => { value={issue?.assignees || null} hideDropdownArrow onChange={handleAssignee} - disabled={false} + disabled={isReadonly} /> )} @@ -102,7 +103,7 @@ export const KanBanProperties: FC = observer((props) => { handleStartDate(date)} - disabled={false} + disabled={isReadonly} placeHolder="Start date" /> )} @@ -112,7 +113,7 @@ export const KanBanProperties: FC = observer((props) => { handleTargetDate(date)} - disabled={false} + disabled={isReadonly} placeHolder="Target date" /> )} @@ -124,7 +125,7 @@ export const KanBanProperties: FC = observer((props) => { value={issue?.estimate_point || null} hideDropdownArrow onChange={handleEstimate} - disabled={false} + disabled={isReadonly} /> )} diff --git a/web/components/issues/issue-layouts/list/roots/archived-issue-root.tsx b/web/components/issues/issue-layouts/list/roots/archived-issue-root.tsx index 7992c2d1a39..63c2f128194 100644 --- a/web/components/issues/issue-layouts/list/roots/archived-issue-root.tsx +++ b/web/components/issues/issue-layouts/list/roots/archived-issue-root.tsx @@ -54,6 +54,7 @@ export const ArchivedIssueListLayout: FC = observer(() => { ( handleIssues(group_by, issue, "delete")} /> From f070cfd5238a3fa619db055acf788c6cebe8a27d Mon Sep 17 00:00:00 2001 From: dakshesh14 Date: Thu, 2 Nov 2023 14:11:14 +0530 Subject: [PATCH 5/6] fix: archived issue delete --- .../issues/delete-archived-issue-modal.tsx | 26 ++++++++++++++++--- web/store/archived-issues/issue.store.ts | 2 +- .../archived-issues/issue_detail.store.ts | 6 ++--- 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/web/components/issues/delete-archived-issue-modal.tsx b/web/components/issues/delete-archived-issue-modal.tsx index aa91757781f..fd9c9b3fd5d 100644 --- a/web/components/issues/delete-archived-issue-modal.tsx +++ b/web/components/issues/delete-archived-issue-modal.tsx @@ -5,6 +5,8 @@ import { Dialog, Transition } from "@headlessui/react"; import { AlertTriangle } from "lucide-react"; // mobx store import { useMobxStore } from "lib/mobx/store-provider"; +// hooks +import useToast from "hooks/use-toast"; // ui import { Button } from "@plane/ui"; // types @@ -23,7 +25,9 @@ export const DeleteArchivedIssueModal: React.FC = observer((props) => { const router = useRouter(); const { workspaceSlug } = router.query; - const { archivedIssues: archivedIssueStore } = useMobxStore(); + const { setToastAlert } = useToast(); + + const { archivedIssueDetail: archivedIssueDetailStore } = useMobxStore(); const [isDeleteLoading, setIsDeleteLoading] = useState(false); @@ -41,9 +45,25 @@ export const DeleteArchivedIssueModal: React.FC = observer((props) => { setIsDeleteLoading(true); - await archivedIssueStore.deleteArchivedIssue(workspaceSlug.toString(), data.project, data.id); + await archivedIssueDetailStore + .deleteArchivedIssue(workspaceSlug.toString(), data.project, data.id) + .then(() => { + if (onSubmit) onSubmit(); + }) + .catch((err) => { + const error = err?.detail; + const errorString = Array.isArray(error) ? error[0] : error; - if (onSubmit) await onSubmit().finally(() => setIsDeleteLoading(false)); + setToastAlert({ + title: "Error", + type: "error", + message: errorString || "Something went wrong.", + }); + }) + .finally(() => { + setIsDeleteLoading(false); + onClose(); + }); }; return ( diff --git a/web/store/archived-issues/issue.store.ts b/web/store/archived-issues/issue.store.ts index 6d10cd615ac..d9ac8ddc913 100644 --- a/web/store/archived-issues/issue.store.ts +++ b/web/store/archived-issues/issue.store.ts @@ -189,7 +189,7 @@ export class ArchivedIssueStore implements IArchivedIssueStore { console.error("Error: Fetching error in issues", error); this.loader = false; this.error = error; - return error; + throw error; } }; diff --git a/web/store/archived-issues/issue_detail.store.ts b/web/store/archived-issues/issue_detail.store.ts index ea7e99c2941..65a971ad232 100644 --- a/web/store/archived-issues/issue_detail.store.ts +++ b/web/store/archived-issues/issue_detail.store.ts @@ -93,7 +93,7 @@ export class ArchivedIssueDetailStore implements IArchivedIssueDetailStore { [projectId]: originalIssues, }; }); - return error; + throw error; } }; @@ -125,7 +125,7 @@ export class ArchivedIssueDetailStore implements IArchivedIssueDetailStore { this.loader = false; this.error = error; }); - return error; + throw error; } }; @@ -156,7 +156,7 @@ export class ArchivedIssueDetailStore implements IArchivedIssueDetailStore { this.loader = false; this.error = error; }); - return error; + throw error; } }; } From ba4300df335dbaa397d1e95f60617f0d32c98e06 Mon Sep 17 00:00:00 2001 From: dakshesh14 Date: Fri, 3 Nov 2023 19:51:15 +0530 Subject: [PATCH 6/6] fix: build error --- web/components/issues/issue-peek-overview/root.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/components/issues/issue-peek-overview/root.tsx b/web/components/issues/issue-peek-overview/root.tsx index 4d19e8574c6..255e229293b 100644 --- a/web/components/issues/issue-peek-overview/root.tsx +++ b/web/components/issues/issue-peek-overview/root.tsx @@ -20,7 +20,7 @@ interface IIssuePeekOverview { issueId: string; handleIssue: (issue: Partial) => void; isArchived?: boolean; - children: ReactNode; + children?: ReactNode; } export const IssuePeekOverview: FC = observer((props) => {