From 9a60cfd44e88344d9d994c3f75350902c814a9f3 Mon Sep 17 00:00:00 2001 From: gurusainath Date: Sat, 17 Feb 2024 22:14:01 +0530 Subject: [PATCH] chore: updated view create and edit flow --- .../view/applied-filters/filter.tsx | 4 +- web/components/view/filters/edit-dropdown.tsx | 4 +- web/components/view/root.tsx | 50 +++++++++----- web/components/view/types.d.ts | 2 +- .../view/views/create-edit-form.tsx | 27 ++++++-- web/components/view/views/root.tsx | 4 +- .../view/views/view-dropdown-item.tsx | 10 +-- web/components/view/views/view-dropdown.tsx | 4 +- web/components/view/views/view-item.tsx | 6 +- web/hooks/store/views/use-view-detail.tsx | 6 +- web/hooks/use-global-views.tsx | 9 --- .../[projectId]/views/private/index.tsx | 1 - .../[projectId]/views/public/index.tsx | 1 - web/store/view/view-root.store.ts | 65 +++++++++++++++++-- web/store/view/view.store.ts | 1 - 15 files changed, 132 insertions(+), 62 deletions(-) delete mode 100644 web/hooks/use-global-views.tsx diff --git a/web/components/view/applied-filters/filter.tsx b/web/components/view/applied-filters/filter.tsx index c98e98df9..a03cebc8f 100644 --- a/web/components/view/applied-filters/filter.tsx +++ b/web/components/view/applied-filters/filter.tsx @@ -45,11 +45,11 @@ export const ViewAppliedFilters: FC = observer((props) => { if (!propertyValues || propertyValues.length <= 0) return <>; return ( -
+
{filterKey.replaceAll("_", " ")}
{propertyVisibleCount && propertyValues.length >= propertyVisibleCount ? ( -
+
{currentDefaultFilterDetails?.icon}
{propertyValues.length} {currentDefaultFilterDetails?.label} diff --git a/web/components/view/filters/edit-dropdown.tsx b/web/components/view/filters/edit-dropdown.tsx index c8bf726e0..852a2ef0e 100644 --- a/web/components/view/filters/edit-dropdown.tsx +++ b/web/components/view/filters/edit-dropdown.tsx @@ -55,7 +55,7 @@ export const ViewFiltersEditDropdown: FC = observer((p key: "save_as_new", label: "Save as new view", onClick: () => { - viewOperations.localViewCreateEdit(undefined, "SAVE_AS_NEW"); + viewOperations.localViewCreateEdit(viewId, "SAVE_AS_NEW"); }, }, { @@ -65,7 +65,7 @@ export const ViewFiltersEditDropdown: FC = observer((p onClick: () => viewDetailStore?.resetChanges(), }, ], - [viewOperations, viewDetailStore] + [viewId, viewOperations, viewDetailStore] ); if (viewDetailStore?.is_local_view) return <>; diff --git a/web/components/view/root.tsx b/web/components/view/root.tsx index 74abef638..ebc231aa0 100644 --- a/web/components/view/root.tsx +++ b/web/components/view/root.tsx @@ -1,6 +1,7 @@ import { FC, Fragment, useCallback, useEffect, useMemo, useState } from "react"; import { useRouter } from "next/router"; import { observer } from "mobx-react-lite"; +import isEmpty from "lodash/isEmpty"; // hooks import { useView, useViewDetail } from "hooks/store"; import useToast from "hooks/use-toast"; @@ -29,7 +30,7 @@ import { } from "constants/view"; // types import { TViewOperations } from "./types"; -import { TViewTypes } from "@plane/types"; +import { TViewFilters, TViewTypes } from "@plane/types"; type TGlobalViewRoot = { workspaceSlug: string; @@ -98,6 +99,23 @@ export const GlobalViewRoot: FC = observer((props) => { } }, update: async () => { + try { + await viewStore?.update(); + handleViewOperationsToggle(undefined, undefined); + setToastAlert({ + type: "success", + title: "Success!", + message: "View created successfully.", + }); + } catch { + setToastAlert({ + type: "error", + title: "Error!", + message: "Something went wrong. Please try again later or contact the support team.", + }); + } + }, + saveChanges: async () => { try { await viewDetailStore?.saveChanges(); handleViewOperationsToggle(undefined, undefined); @@ -152,10 +170,12 @@ export const GlobalViewRoot: FC = observer((props) => { [viewStore, viewDetailStore, handleViewOperationsToggle, setToastAlert, workspaceSlug, projectId] ); + // eslint-disable-next-line @typescript-eslint/no-unused-vars const applyFIltersFromRouter = () => { + const routerParams: Partial> = {}; + if (workspaceSlug && viewId && Object.values(ELocalViews).includes(viewId as ELocalViews)) { const routerQueryParams = { ...router.query }; - // eslint-disable-next-line @typescript-eslint/no-unused-vars const { ["workspaceSlug"]: _workspaceSlug, ["viewId"]: _viewId, ...filters } = routerQueryParams; @@ -165,20 +185,19 @@ export const GlobalViewRoot: FC = observer((props) => { "filters" ); - Object.keys(filters).forEach((key) => { - const filterKey: any = key; - const filterValue = filters[key]?.toString() || undefined; - if (filterKey && filterValue && acceptedFilters.includes(filterKey)) { - const _filterValues = filterValue.split(","); - _filterValues.forEach((element) => { - console.log("filterKey", filterKey); - console.log("element", element); - viewDetailStore?.setFilters(filterKey, element); - }); + Object.keys(filters).forEach((key: string) => { + const filterKey: keyof TViewFilters | undefined = key as keyof TViewFilters; + if (filterKey) { + const filterValue = filters[key]?.toString() || undefined; + if (filterKey && filterValue && acceptedFilters.includes(filterKey)) { + const _filterValues = filterValue.split(","); + routerParams[filterKey] = _filterValues; + } } }); } - // eslint-disable-next-line react-hooks/exhaustive-deps + + return isEmpty(routerParams) ? undefined : routerParams; }; // fetch all views @@ -196,8 +215,7 @@ export const GlobalViewRoot: FC = observer((props) => { // fetch view by id useEffect(() => { const fetchViewByViewId = async () => { - await viewStore?.fetchById(workspaceSlug, projectId, viewId); - // applyFIltersFromRouter(); + await viewStore?.fetchById(workspaceSlug, projectId, viewId, applyFIltersFromRouter()); }; if (workspaceSlug && viewId && viewType && viewStore) { fetchViewByViewId(); @@ -205,8 +223,6 @@ export const GlobalViewRoot: FC = observer((props) => { // eslint-disable-next-line react-hooks/exhaustive-deps }, [workspaceSlug, projectId, viewId, viewType, viewStore]); - console.log("viewStore? -->", viewStore?.viewMapCEN?.id); - return (
{viewStore?.loader && viewStore?.loader === "view-loader" ? ( diff --git a/web/components/view/types.d.ts b/web/components/view/types.d.ts index e4bb10008..53d7c43cf 100644 --- a/web/components/view/types.d.ts +++ b/web/components/view/types.d.ts @@ -8,7 +8,7 @@ export type TViewOperations = { localViewCreateEdit: (viewId: string | undefined, status: TViewCRUD) => void; fetch: () => Promise; - create: (data: Partial) => Promise; + create: () => Promise; update: () => Promise; remove: (viewId: string) => Promise; duplicate: (viewId: string) => Promise; diff --git a/web/components/view/views/create-edit-form.tsx b/web/components/view/views/create-edit-form.tsx index 6c8217763..879a3069c 100644 --- a/web/components/view/views/create-edit-form.tsx +++ b/web/components/view/views/create-edit-form.tsx @@ -3,7 +3,7 @@ import { observer } from "mobx-react-lite"; import { Dialog, Transition } from "@headlessui/react"; import { Briefcase, Globe2, Plus, X } from "lucide-react"; // hooks -import { useViewDetail, useProject } from "hooks/store"; +import { useViewDetail, useProject, useView } from "hooks/store"; // components import { ViewAppliedFiltersRoot, ViewFiltersDropdown } from "../"; // ui @@ -27,11 +27,11 @@ type TViewCreateEditForm = { export const ViewCreateEditForm: FC = observer((props) => { const { workspaceSlug, projectId, viewId, viewType, viewPageType, viewOperations, isLocalView } = props; // hooks + const viewStore = useView(workspaceSlug, projectId, viewType); const viewDetailStore = useViewDetail(workspaceSlug, projectId, viewId, viewType, isLocalView); const { getProjectById } = useProject(); // states const [modalToggle, setModalToggle] = useState(false); - const [loader, setLoader] = useState(false); const modalOpen = useCallback(() => setModalToggle(true), [setModalToggle]); const modalClose = useCallback(() => { @@ -46,7 +46,15 @@ export const ViewCreateEditForm: FC = observer((props) => { }, [viewId, modalOpen, modalClose]); const onContinue = async () => { - setLoader(true); + if (!viewDetailStore) return; + if (viewDetailStore?.id === "create") { + await viewOperations.create(); + modalClose(); + } else { + console.log("coming here..."); + await viewOperations.update(); + // modalClose(); + } // if (viewDetailStore?.id != "create") { // const payload = viewDetailStore?.filtersToUpdate; // await viewOperations.create(payload); @@ -57,7 +65,6 @@ export const ViewCreateEditForm: FC = observer((props) => { // await viewOperations.update(); // modalClose(); // } - setLoader(false); }; const projectDetails = projectId ? getProjectById(projectId) : undefined; @@ -168,11 +175,17 @@ export const ViewCreateEditForm: FC = observer((props) => {
- -
diff --git a/web/components/view/views/root.tsx b/web/components/view/views/root.tsx index a5e7589a0..f7e8973eb 100644 --- a/web/components/view/views/root.tsx +++ b/web/components/view/views/root.tsx @@ -31,7 +31,7 @@ export const ViewRoot: FC = observer((props) => { const handleViewTabsVisibility = () => { const tabContainer = document.getElementById("tab-container"); const tabItemViewMore = document.getElementById("tab-item-view-more"); - const itemWidth = 122; + const itemWidth = 120; if (!tabContainer || !tabItemViewMore) return; const containerWidth = tabContainer.clientWidth; @@ -83,7 +83,7 @@ export const ViewRoot: FC = observer((props) => { viewOperations={viewOperations} baseRoute={baseRoute} > -
+
diff --git a/web/components/view/views/view-dropdown-item.tsx b/web/components/view/views/view-dropdown-item.tsx index 07b5c0733..e9764cddf 100644 --- a/web/components/view/views/view-dropdown-item.tsx +++ b/web/components/view/views/view-dropdown-item.tsx @@ -32,7 +32,7 @@ export const ViewDropdownItem: FC = (props) => { return ( @@ -45,7 +45,7 @@ export const ViewDropdownItem: FC = (props) => { )} = (props) => { } `} > -
+
-
+
{viewDetailStore?.name}
@@ -65,7 +65,7 @@ export const ViewDropdownItem: FC = (props) => { {isEditable && ( -
+
)} diff --git a/web/components/view/views/view-dropdown.tsx b/web/components/view/views/view-dropdown.tsx index 70dd5b746..da56e7f27 100644 --- a/web/components/view/views/view-dropdown.tsx +++ b/web/components/view/views/view-dropdown.tsx @@ -105,7 +105,7 @@ export const ViewDropdown: FC = (props) => {
setQuery(e.target.value)} placeholder="Search for a view..." @@ -133,7 +133,7 @@ export const ViewDropdown: FC = (props) => {
viewOperations?.localViewCreateEdit(undefined, "CREATE")} > diff --git a/web/components/view/views/view-item.tsx b/web/components/view/views/view-item.tsx index 00da5e7b4..588d99c93 100644 --- a/web/components/view/views/view-item.tsx +++ b/web/components/view/views/view-item.tsx @@ -24,11 +24,11 @@ export const ViewItem: FC = observer((props) => { if (!viewDetailStore) return <>; return ( -
+
viewItemId === viewId && e.preventDefault()} @@ -36,7 +36,7 @@ export const ViewItem: FC = observer((props) => {
-
+
{viewDetailStore?.name}
diff --git a/web/hooks/store/views/use-view-detail.tsx b/web/hooks/store/views/use-view-detail.tsx index 972064377..96c5d1561 100644 --- a/web/hooks/store/views/use-view-detail.tsx +++ b/web/hooks/store/views/use-view-detail.tsx @@ -25,15 +25,15 @@ export const useViewDetail = ( if (isEditable) return context.view.workspacePrivateViewStore.viewMapCEN; return context.view.workspacePrivateViewStore.viewById(viewId); case VIEW_TYPES.WORKSPACE_PUBLIC_VIEWS: - if (isEditable) return context.view.workspacePrivateViewStore.viewMapCEN; + if (isEditable) return context.view.workspacePublicViewStore.viewMapCEN; return context.view.workspacePublicViewStore.viewById(viewId); case VIEW_TYPES.PROJECT_PRIVATE_VIEWS: if (!projectId) return undefined; - if (isEditable) return context.view.workspacePrivateViewStore.viewMapCEN; + if (isEditable) return context.view.projectPrivateViewStore.viewMapCEN; return context.view.projectPrivateViewStore.viewById(viewId); case VIEW_TYPES.PROJECT_PUBLIC_VIEWS: if (!projectId) return undefined; - if (isEditable) return context.view.workspacePrivateViewStore.viewMapCEN; + if (isEditable) return context.view.projectPublicViewStore.viewMapCEN; return context.view.projectPublicViewStore.viewById(viewId); default: return undefined; diff --git a/web/hooks/use-global-views.tsx b/web/hooks/use-global-views.tsx deleted file mode 100644 index 3a07ac0c3..000000000 --- a/web/hooks/use-global-views.tsx +++ /dev/null @@ -1,9 +0,0 @@ -type TUseViewsProps = {}; - -export const useViews = (issueId: string | undefined): TUseViewsProps => { - console.log("issueId", issueId); - - return { - issueId, - }; -}; diff --git a/web/pages/[workspaceSlug]/projects/[projectId]/views/private/index.tsx b/web/pages/[workspaceSlug]/projects/[projectId]/views/private/index.tsx index ba71a05ee..1f4419ce6 100644 --- a/web/pages/[workspaceSlug]/projects/[projectId]/views/private/index.tsx +++ b/web/pages/[workspaceSlug]/projects/[projectId]/views/private/index.tsx @@ -38,7 +38,6 @@ const ProjectPublicViewPage: NextPageWithLayout = observer(() => { async () => { if (workspaceSlug && projectId) { await viewStore?.fetch(workspaceSlug?.toString(), projectId?.toString()); - console.log("viewStore", viewStore?.viewIds); } } ); diff --git a/web/pages/[workspaceSlug]/projects/[projectId]/views/public/index.tsx b/web/pages/[workspaceSlug]/projects/[projectId]/views/public/index.tsx index 26bda4da8..cec34ce36 100644 --- a/web/pages/[workspaceSlug]/projects/[projectId]/views/public/index.tsx +++ b/web/pages/[workspaceSlug]/projects/[projectId]/views/public/index.tsx @@ -38,7 +38,6 @@ const ProjectPublicViewPage: NextPageWithLayout = observer(() => { async () => { if (workspaceSlug && projectId) { await viewStore?.fetch(workspaceSlug.toString(), projectId.toString()); - console.log("viewStore", viewStore?.viewIds); } } ); diff --git a/web/store/view/view-root.store.ts b/web/store/view/view-root.store.ts index 343dbd274..521b86194 100644 --- a/web/store/view/view-root.store.ts +++ b/web/store/view/view-root.store.ts @@ -9,7 +9,7 @@ import { RootStore } from "store/root.store"; import { ViewStore } from "./view.store"; // types import { TUserViewService, TViewService } from "services/view/types"; -import { TView, TViewTypes } from "@plane/types"; +import { TView, TViewFilters, TViewTypes } from "@plane/types"; // constants import { EViewPageType, TViewCRUD, generateViewStoreKey, viewLocalPayload } from "constants/view"; @@ -18,6 +18,7 @@ export type TLoader = | "view-mutation-loader" | "view-detail-loader" | "create-submitting" + | "edit-submitting" | "delete-submitting" | "duplicate-submitting" | undefined; @@ -33,10 +34,16 @@ type TViewRootStore = { localView: () => ViewStore | undefined; // actions fetch: (workspaceSlug: string, projectId: string | undefined, _loader?: TLoader) => Promise; - fetchById: (workspaceSlug: string, projectId: string | undefined, viewId: string) => Promise; + fetchById: ( + workspaceSlug: string, + projectId: string | undefined, + viewId: string, + defaultFilters?: Partial> | undefined + ) => Promise; remove: (viewId: string) => Promise; localViewHandler: (viewId: string | undefined, status: TViewCRUD) => void; create: () => Promise; + update: () => Promise; duplicate: (viewId: string) => Promise; }; @@ -66,6 +73,7 @@ export class ViewRootStore implements TViewRootStore { fetch: action, fetchById: action, create: action, + update: action, remove: action, duplicate: action, }); @@ -83,7 +91,7 @@ export class ViewRootStore implements TViewRootStore { let apiViews = views.filter((view) => !view.is_local_view); apiViews = reverse(sortBy(apiViews, "sort_order")); const _viewIds = [...localViews.map((view) => view.id), ...apiViews.map((view) => view.id)]; - return _viewIds as string[]; + return _viewIds.filter((view) => view !== undefined) as string[]; } viewById = computedFn((viewId: string) => { @@ -136,7 +144,12 @@ export class ViewRootStore implements TViewRootStore { } }; - fetchById = async (workspaceSlug: string, projectId: string | undefined, viewId: string) => { + fetchById = async ( + workspaceSlug: string, + projectId: string | undefined, + viewId: string, + defaultFilters: Partial> | undefined = undefined + ) => { try { runInAction(() => (this.loader = "view-detail-loader")); @@ -154,6 +167,7 @@ export class ViewRootStore implements TViewRootStore { const currentView = { ...this.viewById(viewId) } as TView; if (!currentView) return; view = currentView; + defaultFilters && (view.filters = defaultFilters as TViewFilters); } else { const currentView = await this.service.fetchById(workspaceSlug, viewId, projectId); if (!currentView) return; @@ -221,7 +235,14 @@ export class ViewRootStore implements TViewRootStore { _view = cloneDeep(this.viewMap?.[viewRootSlug]?.[viewId]); } else if (status === "SAVE_AS_NEW") { if (!viewId) return; - _view = cloneDeep(this.viewMap?.[viewRootSlug]?.[viewId]); + const clonedView = cloneDeep(this.viewMap?.[viewRootSlug]?.[viewId]); + _view = { + id: "create", + name: clonedView?.name, + filters: clonedView?.filtersToUpdate?.filters, + display_filters: clonedView?.filtersToUpdate?.display_filters, + display_properties: clonedView?.filtersToUpdate?.display_properties, + }; } else return; runInAction(() => { @@ -243,7 +264,39 @@ export class ViewRootStore implements TViewRootStore { const viewRootSlug = generateViewStoreKey(workspaceSlug, projectId, this.viewType); - const view = await this.service.create(workspaceSlug, this.viewMapCEN, projectId); + const view = await this.service.create(workspaceSlug, this.viewMapCEN.filtersToUpdate, projectId); + if (!view) return; + + runInAction(() => { + if (view.id) + set( + this.viewMap, + [viewRootSlug, view.id], + new ViewStore(this.store, view, this.service, this.userService, this.viewPageType) + ); + this.viewMapCEN = undefined; + this.loader = undefined; + }); + } catch { + runInAction(() => (this.loader = undefined)); + } + }; + + update = async () => { + try { + const { workspaceSlug, projectId } = this.store.view; + if (!workspaceSlug || !this.viewMapCEN || !this.viewMapCEN.id) return; + + runInAction(() => (this.loader = "edit-submitting")); + + const viewRootSlug = generateViewStoreKey(workspaceSlug, projectId, this.viewType); + + const view = await this.service.update( + workspaceSlug, + this.viewMapCEN.id, + this.viewMapCEN.filtersToUpdate, + projectId + ); if (!view) return; runInAction(() => { diff --git a/web/store/view/view.store.ts b/web/store/view/view.store.ts index f155e3f2d..47932183d 100644 --- a/web/store/view/view.store.ts +++ b/web/store/view/view.store.ts @@ -302,7 +302,6 @@ export class ViewStore extends FiltersHelper implements TViewStore { saveChanges = async () => { try { if (!this.id) return; - console.log("coming here"); await this.update(this.filtersToUpdate); } catch { Object.keys(this.filtersToUpdate).forEach((key) => {