plane/web/components/view/root.tsx

347 lines
11 KiB
TypeScript
Raw Normal View History

import { FC, Fragment, useCallback, useEffect, useMemo, useState } from "react";
2024-02-16 12:58:21 +00:00
import { useRouter } from "next/router";
2024-02-05 14:39:17 +00:00
import { observer } from "mobx-react-lite";
// hooks
import { useView, useViewDetail } from "hooks/store";
import useToast from "hooks/use-toast";
// components
import {
ViewRoot,
ViewCreateEditForm,
ViewEditDropdown,
ViewLayoutRoot,
ViewFiltersDropdown,
ViewFiltersEditDropdown,
ViewDisplayFiltersDropdown,
ViewAppliedFiltersRoot,
ViewDuplicateConfirmationModal,
ViewDeleteConfirmationModal,
2024-02-16 12:58:21 +00:00
} from "./";
2024-02-05 14:39:17 +00:00
// ui
2024-02-16 12:58:21 +00:00
import { Loader } from "@plane/ui";
2024-02-05 14:39:17 +00:00
// constants
2024-02-16 12:58:21 +00:00
import {
ELocalViews,
EViewLayouts,
EViewPageType,
TViewCRUD,
viewDefaultFilterParametersByViewTypeAndLayout,
} from "constants/view";
2024-02-05 14:39:17 +00:00
// types
import { TViewOperations } from "./types";
2024-02-16 12:58:21 +00:00
import { TViewTypes } from "@plane/types";
2024-02-05 14:39:17 +00:00
type TGlobalViewRoot = {
2024-02-05 14:39:17 +00:00
workspaceSlug: string;
projectId: string | undefined;
viewId: string;
viewType: TViewTypes;
viewPageType: EViewPageType;
baseRoute: string;
};
type TViewOperationsToggle = {
2024-02-16 12:58:21 +00:00
type: "DUPLICATE" | "DELETE" | undefined;
viewId: string | undefined;
2024-02-05 14:39:17 +00:00
};
export const GlobalViewRoot: FC<TGlobalViewRoot> = observer((props) => {
2024-02-16 12:58:21 +00:00
// router
const router = useRouter();
const { workspaceSlug, projectId, viewId, viewType, viewPageType, baseRoute } = props;
2024-02-05 14:39:17 +00:00
// hooks
const viewStore = useView(workspaceSlug, projectId, viewType);
const viewDetailStore = useViewDetail(workspaceSlug, projectId, viewId, viewType);
const { setToastAlert } = useToast();
// states
const [viewOperationsToggle, setViewOperationsToggle] = useState<TViewOperationsToggle>({
type: undefined,
viewId: undefined,
});
const handleViewOperationsToggle = useCallback(
(type: TViewOperationsToggle["type"], viewId: string | undefined) => setViewOperationsToggle({ type, viewId }),
[]
);
2024-02-05 14:39:17 +00:00
const viewOperations: TViewOperations = useMemo(
() => ({
2024-02-16 12:58:21 +00:00
localViewCreateEdit: (viewId: string | undefined, status: TViewCRUD) => {
viewStore?.localViewHandler(viewId, status);
},
fetch: async () => {
try {
2024-02-14 14:36:59 +00:00
await viewStore?.fetch(workspaceSlug, projectId);
} catch {
setToastAlert({
type: "error",
title: "Error!",
message: "Something went wrong. Please try again later or contact the support team.",
});
}
},
2024-02-16 12:58:21 +00:00
create: async () => {
2024-02-05 14:39:17 +00:00
try {
2024-02-16 12:58:21 +00:00
await viewStore?.create();
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.",
});
}
},
2024-02-16 12:58:21 +00:00
update: async () => {
try {
await viewDetailStore?.saveChanges();
handleViewOperationsToggle(undefined, undefined);
setToastAlert({
type: "success",
title: "Success!",
message: "View updated successfully.",
});
} catch {
setToastAlert({
type: "error",
title: "Error!",
message: "Something went wrong. Please try again later or contact the support team.",
});
}
},
remove: async (viewId: string) => {
try {
2024-02-16 12:58:21 +00:00
await viewStore?.remove(viewId);
handleViewOperationsToggle(undefined, undefined);
setToastAlert({
type: "success",
title: "Success!",
message: "View removed successfully.",
});
} catch {
setToastAlert({
type: "error",
title: "Error!",
message: "Something went wrong. Please try again later or contact the support team.",
});
}
},
2024-02-16 12:58:21 +00:00
duplicate: async (viewId: string) => {
try {
2024-02-16 12:58:21 +00:00
await viewStore?.duplicate(viewId);
handleViewOperationsToggle(undefined, undefined);
setToastAlert({
type: "success",
title: "Success!",
2024-02-16 12:58:21 +00:00
message: "View removed successfully.",
});
2024-02-05 14:39:17 +00:00
} catch {
setToastAlert({
type: "error",
title: "Error!",
message: "Something went wrong. Please try again later or contact the support team.",
});
2024-02-05 14:39:17 +00:00
}
},
}),
2024-02-16 12:58:21 +00:00
[viewStore, viewDetailStore, handleViewOperationsToggle, setToastAlert, workspaceSlug, projectId]
2024-02-05 14:39:17 +00:00
);
2024-02-16 12:58:21 +00:00
const applyFIltersFromRouter = () => {
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;
const acceptedFilters = viewDefaultFilterParametersByViewTypeAndLayout(
viewPageType,
EViewLayouts.SPREADSHEET,
"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);
});
}
});
}
// eslint-disable-next-line react-hooks/exhaustive-deps
};
// fetch all views
2024-02-05 14:39:17 +00:00
useEffect(() => {
const fetchViews = async () => {
2024-02-14 14:36:59 +00:00
await viewStore?.fetch(
workspaceSlug,
projectId,
2024-02-16 12:58:21 +00:00
viewStore?.viewIds.length > 0 ? "view-mutation-loader" : "view-loader"
2024-02-14 14:36:59 +00:00
);
};
2024-02-09 16:46:17 +00:00
if (workspaceSlug && viewType && viewStore) fetchViews();
}, [workspaceSlug, projectId, viewType, viewStore]);
2024-02-09 16:46:17 +00:00
// fetch view by id
useEffect(() => {
2024-02-16 12:58:21 +00:00
const fetchViewByViewId = async () => {
await viewStore?.fetchById(workspaceSlug, projectId, viewId);
// applyFIltersFromRouter();
2024-02-09 16:46:17 +00:00
};
2024-02-16 12:58:21 +00:00
if (workspaceSlug && viewId && viewType && viewStore) {
fetchViewByViewId();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
2024-02-09 16:46:17 +00:00
}, [workspaceSlug, projectId, viewId, viewType, viewStore]);
2024-02-05 14:39:17 +00:00
2024-02-16 12:58:21 +00:00
console.log("viewStore? -->", viewStore?.viewMapCEN?.id);
2024-02-05 14:39:17 +00:00
return (
<div className="relative w-full h-full">
2024-02-16 12:58:21 +00:00
{viewStore?.loader && viewStore?.loader === "view-loader" ? (
<Loader className="relative w-full flex items-center gap-2 pt-2 pb-1 px-5 border-b border-custom-border-300">
<div>
<Loader.Item height="30px" width="120px" />
<div className="border-t-2 rounded-t border-custom-primary-100" />
</div>
<Loader.Item height="30px" width="120px" />
<Loader.Item height="30px" width="120px" />
<Loader.Item height="30px" width="120px" />
<Loader.Item height="30px" width="120px" />
<div className="ml-auto">
<Loader.Item height="30px" width="120px" />
</div>
</Loader>
2024-02-05 14:39:17 +00:00
) : (
<>
<div className="border-b border-custom-border-200 pt-2">
<ViewRoot
workspaceSlug={workspaceSlug}
projectId={projectId}
viewId={viewId}
viewType={viewType}
viewOperations={viewOperations}
baseRoute={baseRoute}
/>
</div>
2024-02-05 14:39:17 +00:00
2024-02-16 12:58:21 +00:00
{viewStore?.loader === "view-detail-loader" ? (
<Loader className="relative w-full flex items-center gap-2 py-3 px-5 border-b border-custom-border-300">
<div className="mr-auto relative flex items-center gap-2">
<Loader.Item width="140px" height="30px" />
<Loader.Item width="140px" height="30px" />
<Loader.Item width="140px" height="30px" />
<Loader.Item width="140px" height="30px" />
</div>
<Loader.Item width="30px" height="30px" />
<Loader.Item width="30px" height="30px" />
<Loader.Item width="30px" height="30px" />
<Loader.Item width="120px" height="30px" />
</Loader>
) : (
<div className="p-5 py-2 border-b border-custom-border-200 relative flex items-start gap-1">
<div className="w-full overflow-hidden">
<ViewAppliedFiltersRoot
workspaceSlug={workspaceSlug}
projectId={projectId}
viewId={viewId}
viewType={viewType}
propertyVisibleCount={5}
/>
</div>
2024-02-05 14:39:17 +00:00
2024-02-16 12:58:21 +00:00
<div className="flex-shrink-0">
<ViewLayoutRoot
workspaceSlug={workspaceSlug}
projectId={projectId}
viewId={viewId}
viewType={viewType}
viewPageType={viewPageType}
/>
</div>
2024-02-05 14:39:17 +00:00
2024-02-16 12:58:21 +00:00
<div className="flex-shrink-0">
<ViewFiltersDropdown
workspaceSlug={workspaceSlug}
projectId={projectId}
viewId={viewId}
viewType={viewType}
viewPageType={viewPageType}
displayDropdownText={false}
/>
</div>
2024-02-05 14:39:17 +00:00
2024-02-16 12:58:21 +00:00
<div className="flex-shrink-0">
<ViewDisplayFiltersDropdown
workspaceSlug={workspaceSlug}
projectId={projectId}
viewId={viewId}
viewType={viewType}
viewPageType={viewPageType}
displayDropdownText={false}
/>
</div>
2024-02-05 14:39:17 +00:00
2024-02-16 12:58:21 +00:00
<div className="flex-shrink-0">
<ViewEditDropdown
workspaceSlug={workspaceSlug}
projectId={projectId}
viewId={viewId}
viewType={viewType}
viewOperations={viewOperations}
/>
</div>
2024-02-09 16:46:17 +00:00
2024-02-16 12:58:21 +00:00
<div className="flex-shrink-0">
<ViewFiltersEditDropdown
workspaceSlug={workspaceSlug}
projectId={projectId}
viewId={viewId}
viewType={viewType}
viewOperations={viewOperations}
/>
</div>
</div>
2024-02-16 12:58:21 +00:00
)}
2024-02-05 14:39:17 +00:00
</>
)}
{/* create edit modal */}
2024-02-16 12:58:21 +00:00
{viewStore?.viewMapCEN?.id && (
<ViewCreateEditForm
workspaceSlug={workspaceSlug}
projectId={projectId}
viewId={viewStore?.viewMapCEN?.id}
viewType={viewType}
viewPageType={viewPageType}
viewOperations={viewOperations}
isLocalView={true}
/>
)}
{viewOperationsToggle.type && viewOperationsToggle.viewId && (
<Fragment>
{["DUPLICATE"].includes(viewOperationsToggle.type) && (
<ViewDuplicateConfirmationModal viewId={viewOperationsToggle.viewId} viewOperations={viewOperations} />
)}
{["DELETE"].includes(viewOperationsToggle.type) && (
<ViewDeleteConfirmationModal viewId={viewOperationsToggle.viewId} viewOperations={viewOperations} />
)}
</Fragment>
)}
2024-02-05 14:39:17 +00:00
</div>
);
});