From fad382444bbeac50b01b3d1d4694d8ac139d4346 Mon Sep 17 00:00:00 2001 From: gakshita Date: Thu, 6 Feb 2025 18:04:06 +0530 Subject: [PATCH 1/5] fix: home loading state --- .../home/home-dashboard-widgets.tsx | 16 +++++++++++--- .../home/widgets/loaders/home-loader.tsx | 22 +++++++++++++++++++ .../components/home/widgets/loaders/index.ts | 1 + .../components/home/widgets/recents/index.tsx | 14 ++---------- .../stickies/layout/stickies-list.tsx | 2 +- web/core/store/workspace/home.ts | 6 +++++ 6 files changed, 45 insertions(+), 16 deletions(-) create mode 100644 web/core/components/home/widgets/loaders/home-loader.tsx diff --git a/web/core/components/home/home-dashboard-widgets.tsx b/web/core/components/home/home-dashboard-widgets.tsx index 8ebdb70664e..4b13fd70076 100644 --- a/web/core/components/home/home-dashboard-widgets.tsx +++ b/web/core/components/home/home-dashboard-widgets.tsx @@ -1,5 +1,5 @@ import { observer } from "mobx-react"; -import { useParams } from "next/navigation"; +import { useParams, usePathname } from "next/navigation"; // plane types import { THomeWidgetKeys, THomeWidgetProps } from "@plane/types"; // components @@ -7,11 +7,12 @@ import { EmptyState } from "@/components/empty-state"; // constants import { EmptyStateType } from "@/constants/empty-state"; // hooks +import { useProject } from "@/hooks/store"; import { useHome } from "@/hooks/store/use-home"; // plane web components import { HomePageHeader } from "@/plane-web/components/home/header"; import { StickiesWidget } from "../stickies"; -import { RecentActivityWidget } from "./widgets"; +import { HomeLoader, NoProjectsEmptyState, RecentActivityWidget } from "./widgets"; import { DashboardQuickLinks } from "./widgets/links"; import { ManageWidgetsModal } from "./widgets/manage"; @@ -52,10 +53,17 @@ export const HOME_WIDGETS_LIST: { export const DashboardWidgets = observer(() => { // router const { workspaceSlug } = useParams(); + // navigation + const pathname = usePathname(); // store hooks - const { toggleWidgetSettings, widgetsMap, showWidgetSettings, orderedWidgets, isAnyWidgetEnabled } = useHome(); + const { toggleWidgetSettings, widgetsMap, showWidgetSettings, orderedWidgets, isAnyWidgetEnabled, loading } = + useHome(); + const { joinedProjectIds, loader } = useProject(); + // derived values + const isWikiApp = pathname.includes(`/${workspaceSlug.toString()}/pages`); if (!workspaceSlug) return null; + if (loading) return ; return (
@@ -65,6 +73,8 @@ export const DashboardWidgets = observer(() => { isModalOpen={showWidgetSettings} handleOnClose={() => toggleWidgetSettings(false)} /> + {loader === "loaded" && !isWikiApp && joinedProjectIds?.length === 0 && } + {isAnyWidgetEnabled ? (
{orderedWidgets.map((key) => { diff --git a/web/core/components/home/widgets/loaders/home-loader.tsx b/web/core/components/home/widgets/loaders/home-loader.tsx new file mode 100644 index 00000000000..56d32725bf2 --- /dev/null +++ b/web/core/components/home/widgets/loaders/home-loader.tsx @@ -0,0 +1,22 @@ +"use client"; + +import range from "lodash/range"; +// ui +import { Loader } from "@plane/ui"; + +export const HomeLoader = () => ( + <> + {range(3).map((index) => ( +
+
+
+ +
+ + + +
+
+ ))} + +); diff --git a/web/core/components/home/widgets/loaders/index.ts b/web/core/components/home/widgets/loaders/index.ts index ee5286f0fbf..a0925eccdf2 100644 --- a/web/core/components/home/widgets/loaders/index.ts +++ b/web/core/components/home/widgets/loaders/index.ts @@ -1 +1,2 @@ export * from "./loader"; +export * from "./home-loader"; diff --git a/web/core/components/home/widgets/recents/index.tsx b/web/core/components/home/widgets/recents/index.tsx index cb43384801f..aefa2eb38f2 100644 --- a/web/core/components/home/widgets/recents/index.tsx +++ b/web/core/components/home/widgets/recents/index.tsx @@ -2,7 +2,6 @@ import { useRef, useState } from "react"; import { observer } from "mobx-react"; -import { usePathname } from "next/navigation"; import useSWR from "swr"; import { Briefcase, FileText } from "lucide-react"; // plane types @@ -11,11 +10,9 @@ import { TActivityEntityData, THomeWidgetProps, TRecentActivityFilterKeys } from import { LayersIcon } from "@plane/ui"; // components import { ContentOverflowWrapper } from "@/components/core/content-overflow-HOC"; -// hooks -import { useProject } from "@/hooks/store"; // plane web services import { WorkspaceService } from "@/plane-web/services"; -import { NoProjectsEmptyState, RecentsEmptyState } from "../empty-states"; +import { RecentsEmptyState } from "../empty-states"; import { EWidgetKeys, WidgetLoader } from "../loaders"; import { FiltersDropdown } from "./filters"; import { RecentIssue } from "./issue"; @@ -40,14 +37,9 @@ export const RecentActivityWidget: React.FC = observer((prop const { presetFilter, showFilterSelect = true, workspaceSlug } = props; // states const [filter, setFilter] = useState(presetFilter ?? filters[0].name); - // navigation - const pathname = usePathname(); + // ref const ref = useRef(null); - // store hooks - const { joinedProjectIds, loader } = useProject(); - // derived values - const isWikiApp = pathname.includes(`/${workspaceSlug.toString()}/pages`); const { data: recents, isLoading } = useSWR( workspaceSlug ? `WORKSPACE_RECENT_ACTIVITY_${workspaceSlug}_${filter}` : null, @@ -79,8 +71,6 @@ export const RecentActivityWidget: React.FC = observer((prop } }; - if (loader === "loaded" && !isWikiApp && joinedProjectIds?.length === 0) return ; - if (!isLoading && recents?.length === 0) return (
diff --git a/web/core/components/stickies/layout/stickies-list.tsx b/web/core/components/stickies/layout/stickies-list.tsx index c960ff1cec8..63b337e8438 100644 --- a/web/core/components/stickies/layout/stickies-list.tsx +++ b/web/core/components/stickies/layout/stickies-list.tsx @@ -163,7 +163,7 @@ export const StickiesLayout = (props: TStickiesLayout) => { const columnCount = getColumnCount(containerWidth); return ( -
+
); diff --git a/web/core/store/workspace/home.ts b/web/core/store/workspace/home.ts index dc53b707b30..023767ef3d6 100644 --- a/web/core/store/workspace/home.ts +++ b/web/core/store/workspace/home.ts @@ -7,6 +7,7 @@ import { IWorkspaceLinkStore, WorkspaceLinkStore } from "./link.store"; export interface IHomeStore { // observables + loading: boolean; showWidgetSettings: boolean; widgetsMap: Record; widgets: THomeWidgetKeys[]; @@ -25,6 +26,7 @@ export interface IHomeStore { export class HomeStore implements IHomeStore { // observables showWidgetSettings = false; + loading = false; widgetsMap: Record = {}; widgets: THomeWidgetKeys[] = []; // stores @@ -35,6 +37,7 @@ export class HomeStore implements IHomeStore { constructor() { makeObservable(this, { // observables + loading: observable, showWidgetSettings: observable, widgetsMap: observable, widgets: observable, @@ -68,15 +71,18 @@ export class HomeStore implements IHomeStore { fetchWidgets = async (workspaceSlug: string) => { try { + this.loading = true; const widgets = await this.workspaceService.fetchWorkspaceWidgets(workspaceSlug); runInAction(() => { this.widgets = orderBy(Object.values(widgets), "sort_order", "desc").map((widget) => widget.key); widgets.forEach((widget) => { this.widgetsMap[widget.key] = widget; }); + this.loading = false; }); } catch (error) { console.error("Failed to fetch widgets"); + this.loading = false; throw error; } }; From e50379cf82119d0937a524fcdfa5aa3f7ee22063 Mon Sep 17 00:00:00 2001 From: gakshita Date: Thu, 6 Feb 2025 18:43:43 +0530 Subject: [PATCH 2/5] fix: quickstart guide --- .../home/home-dashboard-widgets.tsx | 6 +- .../home/widgets/empty-states/no-projects.tsx | 111 +++++++++++++----- 2 files changed, 85 insertions(+), 32 deletions(-) diff --git a/web/core/components/home/home-dashboard-widgets.tsx b/web/core/components/home/home-dashboard-widgets.tsx index 4b13fd70076..f4c6ba99993 100644 --- a/web/core/components/home/home-dashboard-widgets.tsx +++ b/web/core/components/home/home-dashboard-widgets.tsx @@ -58,12 +58,12 @@ export const DashboardWidgets = observer(() => { // store hooks const { toggleWidgetSettings, widgetsMap, showWidgetSettings, orderedWidgets, isAnyWidgetEnabled, loading } = useHome(); - const { joinedProjectIds, loader } = useProject(); + const { loader } = useProject(); // derived values const isWikiApp = pathname.includes(`/${workspaceSlug.toString()}/pages`); if (!workspaceSlug) return null; - if (loading) return ; + if (loading || loader !== "loaded") return ; return (
@@ -73,7 +73,7 @@ export const DashboardWidgets = observer(() => { isModalOpen={showWidgetSettings} handleOnClose={() => toggleWidgetSettings(false)} /> - {loader === "loaded" && !isWikiApp && joinedProjectIds?.length === 0 && } + {!isWikiApp && } {isAnyWidgetEnabled ? (
diff --git a/web/core/components/home/widgets/empty-states/no-projects.tsx b/web/core/components/home/widgets/empty-states/no-projects.tsx index c3b9a8807a7..f5c7624d0b6 100644 --- a/web/core/components/home/widgets/empty-states/no-projects.tsx +++ b/web/core/components/home/widgets/empty-states/no-projects.tsx @@ -1,13 +1,14 @@ import React from "react"; import Link from "next/link"; import { useParams } from "next/navigation"; -import { Briefcase, Hotel, Users } from "lucide-react"; +import { Briefcase, Check, Hotel, Users, X } from "lucide-react"; // plane ui +import { useLocalStorage } from "@plane/hooks"; import { Avatar } from "@plane/ui"; // helpers import { getFileURL } from "@/helpers/file.helper"; // hooks -import { useCommandPalette, useEventTracker, useUser, useUserPermissions } from "@/hooks/store"; +import { useCommandPalette, useEventTracker, useProject, useUser, useUserPermissions } from "@/hooks/store"; // plane web constants import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants"; @@ -19,6 +20,14 @@ export const NoProjectsEmptyState = () => { const { toggleCreateProjectModal } = useCommandPalette(); const { setTrackElement } = useEventTracker(); const { data: currentUser } = useUser(); + const { joinedProjectIds } = useProject(); + // local storage + const { storedValue, setValue } = useLocalStorage(`quickstart-guide-${workspaceSlug}`, { + hide: false, + visited_members: false, + visited_workspace: false, + visited_profile: false, + }); // derived values const canCreateProject = allowPermissions( [EUserPermissions.ADMIN, EUserPermissions.MEMBER], @@ -31,6 +40,7 @@ export const NoProjectsEmptyState = () => { title: "Create a project.", description: "Most things start with a project in Plane.", icon: , + flag: "projects", cta: { text: "Get started", onClick: (e: React.MouseEvent) => { @@ -47,6 +57,7 @@ export const NoProjectsEmptyState = () => { title: "Invite your team.", description: "Build, ship, and manage with coworkers.", icon: , + flag: "visited_members", cta: { text: "Get them in", link: `/${workspaceSlug}/settings/members`, @@ -57,6 +68,7 @@ export const NoProjectsEmptyState = () => { title: "Set up your workspace.", description: "Turn features on or off or go beyond that.", icon: , + flag: "visited_workspace", cta: { text: "Configure this workspace", link: "settings", @@ -75,43 +87,84 @@ export const NoProjectsEmptyState = () => { showTooltip={false} /> ), + flag: "visited_profile", cta: { text: "Personalize now", link: "/profile", }, }, ]; + const isComplete = (type: string) => { + switch (type) { + case "projects": + return joinedProjectIds?.length > 0; + case "visited_members": + return storedValue?.visited_members; + case "visited_workspace": + return storedValue?.visited_workspace; + case "visited_profile": + return storedValue?.visited_profile; + } + }; + + if (storedValue?.hide) return null; return ( -
- {EMPTY_STATE_DATA.map((item) => ( -
+
+
Your quickstart guide
+ +
+
+ {EMPTY_STATE_DATA.map((item) => ( +
+
+ {item.icon} +
+

{item.title}

+

{item.description}

+ {isComplete(item.flag) ? ( +
+ +
+ ) : item.cta.link ? ( + { + if (!storedValue) return; + setValue({ + ...storedValue, + [item.flag]: true, + }); + }} + className="text-custom-primary-100 hover:text-custom-primary-200 text-sm font-medium" + > + {item.cta.text} + + ) : ( + + )}
-

{item.title}

-

{item.description}

- {item.cta.link ? ( - - {item.cta.text} - - ) : ( - - )} -
- ))} + ))} +
); }; From bcf796a3474d56cc2d2727c9d21d9680a4ea2a0a Mon Sep 17 00:00:00 2001 From: gakshita Date: Thu, 6 Feb 2025 20:07:04 +0530 Subject: [PATCH 3/5] fix: link handling --- .../home/widgets/empty-states/no-projects.tsx | 10 +++++----- web/core/components/home/widgets/links/use-links.tsx | 4 ++-- web/core/store/workspace/link.store.ts | 1 - 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/web/core/components/home/widgets/empty-states/no-projects.tsx b/web/core/components/home/widgets/empty-states/no-projects.tsx index f5c7624d0b6..659c4b64d4a 100644 --- a/web/core/components/home/widgets/empty-states/no-projects.tsx +++ b/web/core/components/home/widgets/empty-states/no-projects.tsx @@ -39,7 +39,7 @@ export const NoProjectsEmptyState = () => { id: "create-project", title: "Create a project.", description: "Most things start with a project in Plane.", - icon: , + icon: , flag: "projects", cta: { text: "Get started", @@ -56,7 +56,7 @@ export const NoProjectsEmptyState = () => { id: "invite-team", title: "Invite your team.", description: "Build, ship, and manage with coworkers.", - icon: , + icon: , flag: "visited_members", cta: { text: "Get them in", @@ -67,7 +67,7 @@ export const NoProjectsEmptyState = () => { id: "configure-workspace", title: "Set up your workspace.", description: "Turn features on or off or go beyond that.", - icon: , + icon: , flag: "visited_workspace", cta: { text: "Configure this workspace", @@ -82,7 +82,7 @@ export const NoProjectsEmptyState = () => { @@ -130,7 +130,7 @@ export const NoProjectsEmptyState = () => { key={item.id} className="flex flex-col items-center justify-center p-6 bg-custom-background-100 rounded-lg text-center border border-custom-border-200/40" > -
+
{item.icon}

{item.title}

diff --git a/web/core/components/home/widgets/links/use-links.tsx b/web/core/components/home/widgets/links/use-links.tsx index a6668d92efc..6c85291385b 100644 --- a/web/core/components/home/widgets/links/use-links.tsx +++ b/web/core/components/home/widgets/links/use-links.tsx @@ -40,9 +40,9 @@ export const useLinks = (workspaceSlug: string) => { }); toggleLinkModal(false); } catch (error: any) { - console.error("error", error); + console.error("error", error?.data?.url?.error); setToast({ - message: error?.data?.error ?? "The link could not be created", + message: error?.data?.url?.error ?? "The link could not be created", type: TOAST_TYPE.ERROR, title: "Link not created", }); diff --git a/web/core/store/workspace/link.store.ts b/web/core/store/workspace/link.store.ts index a6b2cd40ecd..4a19f92c0f7 100644 --- a/web/core/store/workspace/link.store.ts +++ b/web/core/store/workspace/link.store.ts @@ -93,7 +93,6 @@ export class WorkspaceLinkStore implements IWorkspaceLinkStore { }; createLink = async (workspaceSlug: string, data: Partial) => { - console.log("hereee"); const response = await this.workspaceService.createWorkspaceLink(workspaceSlug, data); runInAction(() => { From d041dea6b54d9fb4976a5f1b009900d9f735ec4d Mon Sep 17 00:00:00 2001 From: gakshita Date: Thu, 6 Feb 2025 20:18:35 +0530 Subject: [PATCH 4/5] fix: home completed state --- .../home/widgets/empty-states/no-projects.tsx | 99 +++++++++++-------- 1 file changed, 56 insertions(+), 43 deletions(-) diff --git a/web/core/components/home/widgets/empty-states/no-projects.tsx b/web/core/components/home/widgets/empty-states/no-projects.tsx index 659c4b64d4a..eec65232207 100644 --- a/web/core/components/home/widgets/empty-states/no-projects.tsx +++ b/web/core/components/home/widgets/empty-states/no-projects.tsx @@ -1,4 +1,6 @@ import React from "react"; +// mobx +import { observer } from "mobx-react"; import Link from "next/link"; import { useParams } from "next/navigation"; import { Briefcase, Check, Hotel, Users, X } from "lucide-react"; @@ -6,13 +8,14 @@ import { Briefcase, Check, Hotel, Users, X } from "lucide-react"; import { useLocalStorage } from "@plane/hooks"; import { Avatar } from "@plane/ui"; // helpers +import { cn } from "@plane/utils"; import { getFileURL } from "@/helpers/file.helper"; // hooks import { useCommandPalette, useEventTracker, useProject, useUser, useUserPermissions } from "@/hooks/store"; // plane web constants import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants"; -export const NoProjectsEmptyState = () => { +export const NoProjectsEmptyState = observer(() => { // navigation const { workspaceSlug } = useParams(); // store hooks @@ -39,7 +42,7 @@ export const NoProjectsEmptyState = () => { id: "create-project", title: "Create a project.", description: "Most things start with a project in Plane.", - icon: , + icon: , flag: "projects", cta: { text: "Get started", @@ -56,7 +59,7 @@ export const NoProjectsEmptyState = () => { id: "invite-team", title: "Invite your team.", description: "Build, ship, and manage with coworkers.", - icon: , + icon: , flag: "visited_members", cta: { text: "Get them in", @@ -67,7 +70,7 @@ export const NoProjectsEmptyState = () => { id: "configure-workspace", title: "Set up your workspace.", description: "Turn features on or off or go beyond that.", - icon: , + icon: , flag: "visited_workspace", cta: { text: "Configure this workspace", @@ -125,46 +128,56 @@ export const NoProjectsEmptyState = () => {
- {EMPTY_STATE_DATA.map((item) => ( -
-
- {item.icon} -
-

{item.title}

-

{item.description}

- {isComplete(item.flag) ? ( -
- -
- ) : item.cta.link ? ( - { - if (!storedValue) return; - setValue({ - ...storedValue, - [item.flag]: true, - }); - }} - className="text-custom-primary-100 hover:text-custom-primary-200 text-sm font-medium" - > - {item.cta.text} - - ) : ( - - )} -
- ))} + {item.icon} +
+

{item.title}

+

{item.description}

+ {isStateComplete ? ( +
+ +
+ ) : item.cta.link ? ( + { + if (!storedValue) return; + setValue({ + ...storedValue, + [item.flag]: true, + }); + }} + className="text-custom-primary-100 hover:text-custom-primary-200 text-sm font-medium" + > + {item.cta.text} + + ) : ( + + )} +
+ ); + })}
); -}; +}); From 5031f806102004302cc72cae1e34baa117889bc7 Mon Sep 17 00:00:00 2001 From: gakshita Date: Fri, 7 Feb 2025 14:52:36 +0530 Subject: [PATCH 5/5] fix: translations --- packages/i18n/src/locales/en/translations.json | 2 ++ packages/i18n/src/locales/es/translations.json | 2 ++ packages/i18n/src/locales/fr/translations.json | 2 ++ packages/i18n/src/locales/ja/translations.json | 2 ++ packages/i18n/src/locales/zh-CN/translations.json | 2 ++ 5 files changed, 10 insertions(+) diff --git a/packages/i18n/src/locales/en/translations.json b/packages/i18n/src/locales/en/translations.json index a1705decebf..32eb6b64f7a 100644 --- a/packages/i18n/src/locales/en/translations.json +++ b/packages/i18n/src/locales/en/translations.json @@ -376,6 +376,8 @@ "home": { "empty": { + "quickstart_guide": "Your quickstart guide", + "not_right_now": "Not right now", "create_project": { "title": "Create a project", "description": "Most things start with a project in Plane.", diff --git a/packages/i18n/src/locales/es/translations.json b/packages/i18n/src/locales/es/translations.json index 1b38b1cbe0a..c7a3da312dc 100644 --- a/packages/i18n/src/locales/es/translations.json +++ b/packages/i18n/src/locales/es/translations.json @@ -546,6 +546,8 @@ "home": { "empty": { + "quickstart_guide": "Guía de inicio rápido", + "not_right_now": "Ahora no", "create_project": { "title": "Crear un proyecto", "description": "La mayoría de las cosas comienzan con un proyecto en Plane.", diff --git a/packages/i18n/src/locales/fr/translations.json b/packages/i18n/src/locales/fr/translations.json index c67ceceaf2e..83b1b31db10 100644 --- a/packages/i18n/src/locales/fr/translations.json +++ b/packages/i18n/src/locales/fr/translations.json @@ -546,6 +546,8 @@ "home": { "empty": { + "quickstart_guide": "Guide de démarrage rapide", + "not_right_now": "Pas maintenant", "create_project": { "title": "Créer un projet", "description": "La plupart des choses commencent par un projet dans Plane.", diff --git a/packages/i18n/src/locales/ja/translations.json b/packages/i18n/src/locales/ja/translations.json index 3b71443fa08..d34a26aaaed 100644 --- a/packages/i18n/src/locales/ja/translations.json +++ b/packages/i18n/src/locales/ja/translations.json @@ -546,6 +546,8 @@ "home": { "empty": { + "quickstart_guide": "クイックスタートガイド", + "not_right_now": "今はしない", "create_project": { "title": "プロジェクトを作成", "description": "Planeのほとんどはプロジェクトから始まります。", diff --git a/packages/i18n/src/locales/zh-CN/translations.json b/packages/i18n/src/locales/zh-CN/translations.json index 3bfe142ea88..f715a21ae51 100644 --- a/packages/i18n/src/locales/zh-CN/translations.json +++ b/packages/i18n/src/locales/zh-CN/translations.json @@ -546,6 +546,8 @@ "home": { "empty": { + "quickstart_guide": "快速入门指南", + "not_right_now": "暂时不要", "create_project": { "title": "创建项目", "description": "在Plane中,大多数事情都从项目开始。",