[WEB-630] refactor: empty state (#3858)

* refactor: empty state global config file added and empty state component refactor

* refactor: empty state component refactor

* chore: empty state refactor

* chore: empty state config file updated

* chore: empty state action button permission logic updated

* chore: empty state config file updated

* chore: cycle and module empty filter state updated

* chore: empty state asset updated

* chore: empty state config file updated

* chore: empty state config file updated

* chore: empty state component improvement

* chore: empty state action button improvement

* fix: merge conflict
This commit is contained in:
Anmol Singh Bhatia 2024-03-06 20:16:54 +05:30 committed by GitHub
parent 4861be2773
commit a08f401452
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
32 changed files with 759 additions and 1155 deletions

View file

@ -1,39 +1,31 @@
import { Fragment, useCallback, useState, ReactElement } from "react";
import { observer } from "mobx-react-lite";
import { useRouter } from "next/router";
import { useTheme } from "next-themes";
import { Tab } from "@headlessui/react";
// hooks
import { Tooltip } from "@plane/ui";
import { PageHead } from "components/core";
import { CyclesView, ActiveCycleDetails, CycleCreateUpdateModal } from "components/cycles";
import { EmptyState, getEmptyStateImagePath } from "components/empty-state";
import { CyclesHeader } from "components/headers";
import { CycleModuleBoardLayout, CycleModuleListLayout, GanttLayoutLoader } from "components/ui";
import { CYCLE_TAB_LIST, CYCLE_VIEW_LAYOUTS } from "constants/cycle";
import { CYCLE_EMPTY_STATE_DETAILS } from "constants/empty-state";
import { EUserWorkspaceRoles } from "constants/workspace";
import { useEventTracker, useCycle, useUser, useProject } from "hooks/store";
import { useEventTracker, useCycle, useProject } from "hooks/store";
import useLocalStorage from "hooks/use-local-storage";
// layouts
import { AppLayout } from "layouts/app-layout";
// components
import { PageHead } from "components/core";
import { CyclesHeader } from "components/headers";
import { CyclesView, ActiveCycleDetails, CycleCreateUpdateModal } from "components/cycles";
import { EmptyState } from "components/empty-state";
import { CycleModuleBoardLayout, CycleModuleListLayout, GanttLayoutLoader } from "components/ui";
// ui
import { Tooltip } from "@plane/ui";
// types
import { NextPageWithLayout } from "lib/types";
import { TCycleView, TCycleLayout } from "@plane/types";
// constants
import { CYCLE_TAB_LIST, CYCLE_VIEW_LAYOUTS } from "constants/cycle";
import { EmptyStateType } from "constants/empty-state";
const ProjectCyclesPage: NextPageWithLayout = observer(() => {
const [createModal, setCreateModal] = useState(false);
// theme
const { resolvedTheme } = useTheme();
// store hooks
const { setTrackElement } = useEventTracker();
const {
membership: { currentProjectRole },
currentUser,
} = useUser();
const { currentProjectCycleIds, loader } = useCycle();
const { getProjectById } = useProject();
// router
@ -43,10 +35,7 @@ const ProjectCyclesPage: NextPageWithLayout = observer(() => {
const { storedValue: cycleTab, setValue: setCycleTab } = useLocalStorage<TCycleView>("cycle_tab", "active");
const { storedValue: cycleLayout, setValue: setCycleLayout } = useLocalStorage<TCycleLayout>("cycle_layout", "list");
// derived values
const isLightMode = resolvedTheme ? resolvedTheme === "light" : currentUser?.theme.theme === "light";
const EmptyStateImagePath = getEmptyStateImagePath("onboarding", "cycles", isLightMode);
const totalCycles = currentProjectCycleIds?.length ?? 0;
const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserWorkspaceRoles.MEMBER;
const project = projectId ? getProjectById(projectId?.toString()) : undefined;
const pageTitle = project?.name ? `${project?.name} - Cycles` : undefined;
@ -89,22 +78,11 @@ const ProjectCyclesPage: NextPageWithLayout = observer(() => {
{totalCycles === 0 ? (
<div className="h-full place-items-center">
<EmptyState
title={CYCLE_EMPTY_STATE_DETAILS["cycles"].title}
description={CYCLE_EMPTY_STATE_DETAILS["cycles"].description}
image={EmptyStateImagePath}
comicBox={{
title: CYCLE_EMPTY_STATE_DETAILS["cycles"].comicBox.title,
description: CYCLE_EMPTY_STATE_DETAILS["cycles"].comicBox.description,
type={EmptyStateType.PROJECT_CYCLES}
primaryButtonOnClick={() => {
setTrackElement("Cycle empty state");
setCreateModal(true);
}}
primaryButton={{
text: CYCLE_EMPTY_STATE_DETAILS["cycles"].primaryButton.text,
onClick: () => {
setTrackElement("Cycle empty state");
setCreateModal(true);
},
}}
size="lg"
disabled={!isEditingAllowed}
/>
</div>
) : (

View file

@ -2,18 +2,9 @@ import { useState, Fragment, ReactElement } from "react";
import { observer } from "mobx-react-lite";
import dynamic from "next/dynamic";
import { useRouter } from "next/router";
import { useTheme } from "next-themes";
import useSWR from "swr";
import { Tab } from "@headlessui/react";
// hooks
import { PageHead } from "components/core";
import { EmptyState, getEmptyStateImagePath } from "components/empty-state";
import { PagesHeader } from "components/headers";
import { RecentPagesList, CreateUpdatePageModal } from "components/pages";
import { PagesLoader } from "components/ui";
import { PAGE_EMPTY_STATE_DETAILS } from "constants/empty-state";
import { PAGE_TABS_LIST } from "constants/page";
import { EUserWorkspaceRoles } from "constants/workspace";
import { useApplication, useEventTracker, useUser, useProject } from "hooks/store";
import { useProjectPages } from "hooks/store/use-project-page";
import useLocalStorage from "hooks/use-local-storage";
@ -22,9 +13,16 @@ import useSize from "hooks/use-window-size";
// layouts
import { AppLayout } from "layouts/app-layout";
// components
import { RecentPagesList, CreateUpdatePageModal } from "components/pages";
import { EmptyState } from "components/empty-state";
import { PagesHeader } from "components/headers";
import { PagesLoader } from "components/ui";
import { PageHead } from "components/core";
// types
import { NextPageWithLayout } from "lib/types";
// constants
import { PAGE_TABS_LIST } from "constants/page";
import { EmptyStateType } from "constants/empty-state";
const AllPagesList = dynamic<any>(() => import("components/pages").then((a) => a.AllPagesList), {
ssr: false,
@ -52,14 +50,8 @@ const ProjectPagesPage: NextPageWithLayout = observer(() => {
const { workspaceSlug, projectId } = router.query;
// states
const [createUpdatePageModal, setCreateUpdatePageModal] = useState(false);
// theme
const { resolvedTheme } = useTheme();
// store hooks
const {
currentUser,
currentUserLoader,
membership: { currentProjectRole },
} = useUser();
const { currentUser, currentUserLoader } = useUser();
const {
commandPalette: { toggleCreatePageModal },
} = useApplication();
@ -103,9 +95,6 @@ const ProjectPagesPage: NextPageWithLayout = observer(() => {
};
// derived values
const isLightMode = resolvedTheme ? resolvedTheme === "light" : currentUser?.theme.theme === "light";
const EmptyStateImagePath = getEmptyStateImagePath("onboarding", "pages", isLightMode);
const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserWorkspaceRoles.MEMBER;
const project = projectId ? getProjectById(projectId.toString()) : undefined;
const pageTitle = project?.name ? `${project?.name} - Pages` : undefined;
@ -216,22 +205,11 @@ const ProjectPagesPage: NextPageWithLayout = observer(() => {
</>
) : (
<EmptyState
image={EmptyStateImagePath}
title={PAGE_EMPTY_STATE_DETAILS["pages"].title}
description={PAGE_EMPTY_STATE_DETAILS["pages"].description}
primaryButton={{
text: PAGE_EMPTY_STATE_DETAILS["pages"].primaryButton.text,
onClick: () => {
setTrackElement("Pages empty state");
toggleCreatePageModal(true);
},
type={EmptyStateType.PROJECT_PAGE}
primaryButtonOnClick={() => {
setTrackElement("Pages empty state");
toggleCreatePageModal(true);
}}
comicBox={{
title: PAGE_EMPTY_STATE_DETAILS["pages"].comicBox.title,
description: PAGE_EMPTY_STATE_DETAILS["pages"].comicBox.description,
}}
size="lg"
disabled={!isEditingAllowed}
/>
)}
</>

View file

@ -1,17 +1,9 @@
import { ReactElement } from "react";
import { observer } from "mobx-react";
import { useRouter } from "next/router";
import { useTheme } from "next-themes";
import useSWR from "swr";
// hooks
import { PageHead } from "components/core";
import { EmptyState, getEmptyStateImagePath } from "components/empty-state";
import { ProjectSettingHeader } from "components/headers";
import { IntegrationCard } from "components/project";
import { IntegrationsSettingsLoader } from "components/ui";
import { PROJECT_SETTINGS_EMPTY_STATE_DETAILS } from "constants/empty-state";
import { PROJECT_DETAILS, WORKSPACE_INTEGRATIONS } from "constants/fetch-keys";
import { useUser } from "hooks/store";
// layouts
import { AppLayout } from "layouts/app-layout";
import { ProjectSettingLayout } from "layouts/settings-layout";
@ -20,10 +12,17 @@ import { NextPageWithLayout } from "lib/types";
import { IntegrationService } from "services/integrations";
import { ProjectService } from "services/project";
// components
import { PageHead } from "components/core";
import { IntegrationCard } from "components/project";
import { ProjectSettingHeader } from "components/headers";
import { EmptyState } from "components/empty-state";
// ui
// types
import { IProject } from "@plane/types";
// fetch-keys
import { PROJECT_DETAILS, WORKSPACE_INTEGRATIONS } from "constants/fetch-keys";
// constants
import { EmptyStateType } from "constants/empty-state";
// services
const integrationService = new IntegrationService();
@ -32,10 +31,6 @@ const projectService = new ProjectService();
const ProjectIntegrationsPage: NextPageWithLayout = observer(() => {
const router = useRouter();
const { workspaceSlug, projectId } = router.query;
// theme
const { resolvedTheme } = useTheme();
// store hooks
const { currentUser } = useUser();
// fetch project details
const { data: projectDetails } = useSWR<IProject>(
workspaceSlug && projectId ? PROJECT_DETAILS(projectId as string) : null,
@ -47,9 +42,6 @@ const ProjectIntegrationsPage: NextPageWithLayout = observer(() => {
() => (workspaceSlug ? integrationService.getWorkspaceIntegrationsList(workspaceSlug as string) : null)
);
// derived values
const emptyStateDetail = PROJECT_SETTINGS_EMPTY_STATE_DETAILS["integrations"];
const isLightMode = resolvedTheme ? resolvedTheme === "light" : currentUser?.theme.theme === "light";
const emptyStateImage = getEmptyStateImagePath("project-settings", "integrations", isLightMode);
const isAdmin = projectDetails?.member_role === 20;
const pageTitle = projectDetails?.name ? `${projectDetails?.name} - Integrations` : undefined;
@ -70,15 +62,8 @@ const ProjectIntegrationsPage: NextPageWithLayout = observer(() => {
) : (
<div className="h-full w-full py-8">
<EmptyState
title={emptyStateDetail.title}
description={emptyStateDetail.description}
image={emptyStateImage}
primaryButton={{
text: "Configure now",
onClick: () => router.push(`/${workspaceSlug}/settings/integrations`),
}}
size="lg"
disabled={!isAdmin}
type={EmptyStateType.PROJECT_SETTINGS_INTEGRATIONS}
primaryButtonLink={`/${workspaceSlug}/settings/integrations`}
/>
</div>
)