From e1bf31831798301f81c4cbb731d546574ba130a5 Mon Sep 17 00:00:00 2001 From: Anmol Singh Bhatia <121005188+anmolsinghbhatia@users.noreply.github.com> Date: Mon, 19 Feb 2024 21:07:43 +0530 Subject: [PATCH 001/127] chore : project inbox improvement (#3695) * chore: project inbox layout loader added * chore: project inbox layout loader added * chore: project inbox added in sidebar project items * chore: inbox loader improvement * chore: project inbox improvement --- web/components/headers/project-issues.tsx | 35 +++----- web/components/inbox/sidebar/root.tsx | 22 ++--- web/components/project/sidebar-list-item.tsx | 88 ++++++++++++++----- web/components/ui/loader/layouts/index.ts | 1 + .../project-inbox/inbox-layout-loader.tsx | 19 ++++ .../project-inbox/inbox-sidebar-loader.tsx | 24 +++++ .../ui/loader/layouts/project-inbox/index.ts | 2 + .../projects/[projectId]/inbox/index.tsx | 4 +- 8 files changed, 136 insertions(+), 59 deletions(-) create mode 100644 web/components/ui/loader/layouts/project-inbox/inbox-layout-loader.tsx create mode 100644 web/components/ui/loader/layouts/project-inbox/inbox-sidebar-loader.tsx create mode 100644 web/components/ui/loader/layouts/project-inbox/index.ts diff --git a/web/components/headers/project-issues.tsx b/web/components/headers/project-issues.tsx index 5c44a84d668..0331967584e 100644 --- a/web/components/headers/project-issues.tsx +++ b/web/components/headers/project-issues.tsx @@ -1,8 +1,7 @@ import { useCallback, useState } from "react"; -import Link from "next/link"; import { useRouter } from "next/router"; import { observer } from "mobx-react-lite"; -import { Briefcase, Circle, ExternalLink, Plus, Inbox } from "lucide-react"; +import { Briefcase, Circle, ExternalLink, Plus } from "lucide-react"; // hooks import { useApplication, @@ -11,7 +10,6 @@ import { useProject, useProjectState, useUser, - useInbox, useMember, } from "hooks/store"; // components @@ -54,7 +52,6 @@ export const ProjectIssuesHeader: React.FC = observer(() => { const { currentProjectDetails } = useProject(); const { projectStates } = useProjectState(); const { projectLabels } = useLabel(); - const { getInboxesByProjectId, getInboxById } = useInbox(); const activeLayout = issueFilters?.displayFilters?.layout; @@ -101,9 +98,6 @@ export const ProjectIssuesHeader: React.FC = observer(() => { [workspaceSlug, projectId, updateFilters] ); - const inboxesMap = currentProjectDetails?.inbox_view ? getInboxesByProjectId(currentProjectDetails.id) : undefined; - const inboxDetails = inboxesMap && inboxesMap.length > 0 ? getInboxById(inboxesMap[0]) : undefined; - const deployUrl = process.env.NEXT_PUBLIC_DEPLOY_URL; const canUserCreateIssue = currentProjectRole && [EUserProjectRoles.ADMIN, EUserProjectRoles.MEMBER].includes(currentProjectRole); @@ -154,7 +148,9 @@ export const ProjectIssuesHeader: React.FC = observer(() => { } />} + link={ + } /> + } /> @@ -201,24 +197,15 @@ export const ProjectIssuesHeader: React.FC = observer(() => { /> - {currentProjectDetails?.inbox_view && inboxDetails && ( - - - - - - - )} + {canUserCreateIssue && ( <> - + <> + +
+
+ +
+
- - + ); -}; +}); ProjectArchivedIssuesPage.getLayout = function getLayout(page: ReactElement) { return ( diff --git a/web/pages/[workspaceSlug]/projects/[projectId]/cycles/[cycleId].tsx b/web/pages/[workspaceSlug]/projects/[projectId]/cycles/[cycleId].tsx index f24fc559731..58431bd388e 100644 --- a/web/pages/[workspaceSlug]/projects/[projectId]/cycles/[cycleId].tsx +++ b/web/pages/[workspaceSlug]/projects/[projectId]/cycles/[cycleId].tsx @@ -1,12 +1,14 @@ import { ReactElement } from "react"; import { useRouter } from "next/router"; import useSWR from "swr"; +import { observer } from "mobx-react"; // hooks -import { useCycle } from "hooks/store"; +import { 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 { CycleIssuesHeader } from "components/headers"; import { CycleDetailsSidebar } from "components/cycles"; import { CycleLayoutRoot } from "components/issues/issue-layouts"; @@ -17,27 +19,36 @@ import emptyCycle from "public/empty-state/cycle.svg"; // types import { NextPageWithLayout } from "lib/types"; -const CycleDetailPage: NextPageWithLayout = () => { +const CycleDetailPage: NextPageWithLayout = observer(() => { // router const router = useRouter(); const { workspaceSlug, projectId, cycleId } = router.query; // store hooks - const { fetchCycleDetails } = useCycle(); - + const { fetchCycleDetails, getCycleById } = useCycle(); + const { getProjectById } = useProject(); + // hooks const { setValue, storedValue } = useLocalStorage("cycle_sidebar_collapsed", "false"); - const isSidebarCollapsed = storedValue ? (storedValue === "true" ? true : false) : false; - + // fetching cycle details const { error } = useSWR( workspaceSlug && projectId && cycleId ? `CYCLE_DETAILS_${cycleId.toString()}` : null, workspaceSlug && projectId && cycleId ? () => fetchCycleDetails(workspaceSlug.toString(), projectId.toString(), cycleId.toString()) : null ); + // derived values + const isSidebarCollapsed = storedValue ? (storedValue === "true" ? true : false) : false; + const cycle = cycleId ? getCycleById(cycleId.toString()) : undefined; + const project = projectId ? getProjectById(projectId.toString()) : undefined; + const pageTitle = project?.name && cycle?.name ? `${project?.name} - ${cycle?.name}` : undefined; + /** + * Toggles the sidebar + */ const toggleSidebar = () => setValue(`${!isSidebarCollapsed}`); return ( <> + {error ? ( { )} ); -}; +}); CycleDetailPage.getLayout = function getLayout(page: ReactElement) { return ( diff --git a/web/pages/[workspaceSlug]/projects/[projectId]/cycles/index.tsx b/web/pages/[workspaceSlug]/projects/[projectId]/cycles/index.tsx index 0b9af62fd24..0f86089aa69 100644 --- a/web/pages/[workspaceSlug]/projects/[projectId]/cycles/index.tsx +++ b/web/pages/[workspaceSlug]/projects/[projectId]/cycles/index.tsx @@ -4,11 +4,12 @@ import { observer } from "mobx-react-lite"; import { Tab } from "@headlessui/react"; import { useTheme } from "next-themes"; // hooks -import { useEventTracker, useCycle, useUser } from "hooks/store"; +import { useEventTracker, useCycle, useUser, 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, getEmptyStateImagePath } from "components/empty-state"; @@ -34,12 +35,20 @@ const ProjectCyclesPage: NextPageWithLayout = observer(() => { currentUser, } = useUser(); const { currentProjectCycleIds, loader } = useCycle(); + const { getProjectById } = useProject(); // router const router = useRouter(); const { workspaceSlug, projectId, peekCycle } = router.query; // local storage const { storedValue: cycleTab, setValue: setCycleTab } = useLocalStorage("cycle_tab", "active"); const { storedValue: cycleLayout, setValue: setCycleLayout } = useLocalStorage("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; const handleCurrentLayout = useCallback( (_layout: TCycleLayout) => { @@ -56,13 +65,6 @@ const ProjectCyclesPage: NextPageWithLayout = observer(() => { [handleCurrentLayout, setCycleTab] ); - 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; - if (!workspaceSlug || !projectId) return null; if (loader) @@ -75,143 +77,146 @@ const ProjectCyclesPage: NextPageWithLayout = observer(() => { ); return ( -
- setCreateModal(false)} - /> - {totalCycles === 0 ? ( -
- { - setTrackElement("Cycle empty state"); - setCreateModal(true); - }, - }} - size="lg" - disabled={!isEditingAllowed} - /> -
- ) : ( - i.key == cycleTab)} - selectedIndex={CYCLE_TAB_LIST.findIndex((i) => i.key == cycleTab)} - onChange={(i) => handleCurrentView(CYCLE_TAB_LIST[i]?.key ?? "active")} - > -
- - {CYCLE_TAB_LIST.map((tab) => ( - - `border-b-2 p-4 text-sm font-medium outline-none ${ - selected ? "border-custom-primary-100 text-custom-primary-100" : "border-transparent" - }` - } - > - {tab.name} - - ))} - -
- {cycleTab !== "active" && ( -
- {CYCLE_VIEW_LAYOUTS.map((layout) => { - if (layout.key === "gantt" && cycleTab === "draft") return null; - - return ( - - - - ); - })} -
- )} + onClick={() => handleCurrentLayout(layout.key as TCycleLayout)} + > + + + + ); + })} +
+ )} +
- - - - - {cycleTab && cycleLayout && ( - - )} - - - - - - - - {cycleTab && cycleLayout && ( - - )} - - - - {cycleTab && cycleLayout && workspaceSlug && projectId && ( - - )} - - - {cycleTab && cycleLayout && workspaceSlug && projectId && ( - - )} - - - - )} - + + + {cycleTab && cycleLayout && ( + + )} + + + + + + + + {cycleTab && cycleLayout && ( + + )} + + + + {cycleTab && cycleLayout && workspaceSlug && projectId && ( + + )} + + + + {cycleTab && cycleLayout && workspaceSlug && projectId && ( + + )} + + + + )} + + ); }); diff --git a/web/pages/[workspaceSlug]/projects/[projectId]/draft-issues/index.tsx b/web/pages/[workspaceSlug]/projects/[projectId]/draft-issues/index.tsx index eaf7ce3d35a..e2ad2521492 100644 --- a/web/pages/[workspaceSlug]/projects/[projectId]/draft-issues/index.tsx +++ b/web/pages/[workspaceSlug]/projects/[projectId]/draft-issues/index.tsx @@ -5,31 +5,43 @@ import { X, PenSquare } from "lucide-react"; import { AppLayout } from "layouts/app-layout"; // components import { DraftIssueLayoutRoot } from "components/issues/issue-layouts/roots/draft-issue-layout-root"; +import { PageHead } from "components/core"; import { ProjectDraftIssueHeader } from "components/headers"; // types import { NextPageWithLayout } from "lib/types"; +// hooks +import { useProject } from "hooks/store"; +import { observer } from "mobx-react"; -const ProjectDraftIssuesPage: NextPageWithLayout = () => { +const ProjectDraftIssuesPage: NextPageWithLayout = observer(() => { const router = useRouter(); const { workspaceSlug, projectId } = router.query; + // store + const { getProjectById } = useProject(); + // derived values + const project = projectId ? getProjectById(projectId.toString()) : undefined; + const pageTitle = project?.name ? `${project?.name} - Draft Issues` : undefined; return ( -
-
- - +
+
- - + ); -}; +}); ProjectDraftIssuesPage.getLayout = function getLayout(page: ReactElement) { return ( diff --git a/web/pages/[workspaceSlug]/projects/[projectId]/inbox/[inboxId].tsx b/web/pages/[workspaceSlug]/projects/[projectId]/inbox/[inboxId].tsx index 5cd1e6c2c60..125ee4245d1 100644 --- a/web/pages/[workspaceSlug]/projects/[projectId]/inbox/[inboxId].tsx +++ b/web/pages/[workspaceSlug]/projects/[projectId]/inbox/[inboxId].tsx @@ -7,9 +7,9 @@ import { useProject, useInboxIssues } from "hooks/store"; // layouts import { AppLayout } from "layouts/app-layout"; // components +import { PageHead } from "components/core"; import { ProjectInboxHeader } from "components/headers"; import { InboxSidebarRoot, InboxContentRoot } from "components/inbox"; - // types import { NextPageWithLayout } from "lib/types"; @@ -22,7 +22,7 @@ const ProjectInboxPage: NextPageWithLayout = observer(() => { filters: { fetchInboxFilters }, issues: { fetchInboxIssues }, } = useInboxIssues(); - + // fetching the Inbox filters and issues useSWR( workspaceSlug && projectId && currentProjectDetails && currentProjectDetails?.inbox_view ? `INBOX_ISSUES_${workspaceSlug.toString()}_${projectId.toString()}` @@ -34,26 +34,32 @@ const ProjectInboxPage: NextPageWithLayout = observer(() => { } } ); + // derived values + const pageTitle = currentProjectDetails?.name ? `${currentProjectDetails?.name} - Inbox` : undefined; if (!workspaceSlug || !projectId || !inboxId || !currentProjectDetails?.inbox_view) return <>; + return ( -
-
- -
-
- + <> + +
+
+ +
+
+ +
-
+ ); }); diff --git a/web/pages/[workspaceSlug]/projects/[projectId]/issues/[issueId].tsx b/web/pages/[workspaceSlug]/projects/[projectId]/issues/[issueId].tsx index 64f43939e66..6ff7d5aa512 100644 --- a/web/pages/[workspaceSlug]/projects/[projectId]/issues/[issueId].tsx +++ b/web/pages/[workspaceSlug]/projects/[projectId]/issues/[issueId].tsx @@ -5,14 +5,15 @@ import { observer } from "mobx-react-lite"; // layouts import { AppLayout } from "layouts/app-layout"; // components +import { PageHead } from "components/core"; import { ProjectIssueDetailsHeader } from "components/headers"; import { IssueDetailRoot } from "components/issues"; // ui import { Loader } from "@plane/ui"; // types import { NextPageWithLayout } from "lib/types"; -// fetch-keys -import { useApplication, useIssueDetail } from "hooks/store"; +// store hooks +import { useApplication, useIssueDetail, useProject } from "hooks/store"; const IssueDetailsPage: NextPageWithLayout = observer(() => { // router @@ -23,17 +24,20 @@ const IssueDetailsPage: NextPageWithLayout = observer(() => { fetchIssue, issue: { getIssueById }, } = useIssueDetail(); + const { getProjectById } = useProject(); const { theme: themeStore } = useApplication(); - + // fetching issue details const { isLoading } = useSWR( workspaceSlug && projectId && issueId ? `ISSUE_DETAIL_${workspaceSlug}_${projectId}_${issueId}` : null, workspaceSlug && projectId && issueId ? () => fetchIssue(workspaceSlug.toString(), projectId.toString(), issueId.toString()) : null ); - + // derived values const issue = getIssueById(issueId?.toString() || "") || undefined; + const project = (issue?.project_id && getProjectById(issue?.project_id)) || undefined; const issueLoader = !issue || isLoading ? true : false; + const pageTitle = project && issue ? `${project?.identifier}-${issue?.sequence_id} ${issue?.name}` : undefined; useEffect(() => { const handleToggleIssueDetailSidebar = () => { @@ -52,6 +56,7 @@ const IssueDetailsPage: NextPageWithLayout = observer(() => { return ( <> + {issueLoader ? (
diff --git a/web/pages/[workspaceSlug]/projects/[projectId]/issues/index.tsx b/web/pages/[workspaceSlug]/projects/[projectId]/issues/index.tsx index 3193fe64e21..2aa9ab2e6a2 100644 --- a/web/pages/[workspaceSlug]/projects/[projectId]/issues/index.tsx +++ b/web/pages/[workspaceSlug]/projects/[projectId]/issues/index.tsx @@ -1,4 +1,7 @@ import { ReactElement } from "react"; +import Head from "next/head"; +import { useRouter } from "next/router"; +import { observer } from "mobx-react"; // components import { ProjectLayoutRoot } from "components/issues"; import { ProjectIssuesHeader } from "components/headers"; @@ -6,12 +9,36 @@ import { ProjectIssuesHeader } from "components/headers"; import { NextPageWithLayout } from "lib/types"; // layouts import { AppLayout } from "layouts/app-layout"; +// hooks +import { useProject } from "hooks/store"; +import { PageHead } from "components/core"; -const ProjectIssuesPage: NextPageWithLayout = () => ( -
- -
-); +const ProjectIssuesPage: NextPageWithLayout = observer(() => { + const router = useRouter(); + const { projectId } = router.query; + // store + const { getProjectById } = useProject(); + + if (!projectId) { + return <>; + } + + // derived values + const project = getProjectById(projectId.toString()); + const pageTitle = project?.name ? `${project?.name} - Issues` : undefined; + + return ( + <> + + + {project?.name} - Issues + +
+ +
+ + ); +}); ProjectIssuesPage.getLayout = function getLayout(page: ReactElement) { return ( diff --git a/web/pages/[workspaceSlug]/projects/[projectId]/modules/[moduleId].tsx b/web/pages/[workspaceSlug]/projects/[projectId]/modules/[moduleId].tsx index 946041176fe..37f7e6b0ecf 100644 --- a/web/pages/[workspaceSlug]/projects/[projectId]/modules/[moduleId].tsx +++ b/web/pages/[workspaceSlug]/projects/[projectId]/modules/[moduleId].tsx @@ -1,8 +1,9 @@ import { ReactElement } from "react"; import { useRouter } from "next/router"; import useSWR from "swr"; +import { observer } from "mobx-react"; // hooks -import { useModule } from "hooks/store"; +import { useModule, useProject } from "hooks/store"; import useLocalStorage from "hooks/use-local-storage"; // layouts import { AppLayout } from "layouts/app-layout"; @@ -10,37 +11,44 @@ import { AppLayout } from "layouts/app-layout"; import { ModuleDetailsSidebar } from "components/modules"; import { ModuleLayoutRoot } from "components/issues"; import { ModuleIssuesHeader } from "components/headers"; -// ui +import { PageHead } from "components/core"; import { EmptyState } from "components/common"; // assets import emptyModule from "public/empty-state/module.svg"; // types import { NextPageWithLayout } from "lib/types"; -const ModuleIssuesPage: NextPageWithLayout = () => { +const ModuleIssuesPage: NextPageWithLayout = observer(() => { // router const router = useRouter(); const { workspaceSlug, projectId, moduleId } = router.query; // store hooks - const { fetchModuleDetails } = useModule(); + const { fetchModuleDetails, getModuleById } = useModule(); + const { getProjectById } = useProject(); // local storage const { setValue, storedValue } = useLocalStorage("module_sidebar_collapsed", "false"); const isSidebarCollapsed = storedValue ? (storedValue === "true" ? true : false) : false; - + // fetching module details const { error } = useSWR( workspaceSlug && projectId && moduleId ? `CURRENT_MODULE_DETAILS_${moduleId.toString()}` : null, workspaceSlug && projectId && moduleId ? () => fetchModuleDetails(workspaceSlug.toString(), projectId.toString(), moduleId.toString()) : null ); + // derived values + const projectModule = moduleId ? getModuleById(moduleId.toString()) : undefined; + const project = projectId ? getProjectById(projectId.toString()) : undefined; + const pageTitle = project?.name && projectModule?.name ? `${project?.name} - ${projectModule?.name}` : undefined; const toggleSidebar = () => { setValue(`${!isSidebarCollapsed}`); }; if (!workspaceSlug || !projectId || !moduleId) return <>; + return ( <> + {error ? ( { )} ); -}; +}); ModuleIssuesPage.getLayout = function getLayout(page: ReactElement) { return ( diff --git a/web/pages/[workspaceSlug]/projects/[projectId]/modules/index.tsx b/web/pages/[workspaceSlug]/projects/[projectId]/modules/index.tsx index 8e63c28993e..085f1e3c3bc 100644 --- a/web/pages/[workspaceSlug]/projects/[projectId]/modules/index.tsx +++ b/web/pages/[workspaceSlug]/projects/[projectId]/modules/index.tsx @@ -1,13 +1,33 @@ import { ReactElement } from "react"; +import { useRouter } from "next/router"; // layouts import { AppLayout } from "layouts/app-layout"; // components +import { PageHead } from "components/core"; import { ModulesListView } from "components/modules"; import { ModulesListHeader } from "components/headers"; // types import { NextPageWithLayout } from "lib/types"; +// hooks +import { useProject } from "hooks/store"; +import { observer } from "mobx-react"; -const ProjectModulesPage: NextPageWithLayout = () => ; +const ProjectModulesPage: NextPageWithLayout = observer(() => { + const router = useRouter(); + const { projectId } = router.query; + // store + const { getProjectById } = useProject(); + // derived values + const project = projectId ? getProjectById(projectId.toString()) : undefined; + const pageTitle = project?.name ? `${project?.name} - Modules` : undefined; + + return ( + <> + + + + ); +}); ProjectModulesPage.getLayout = function getLayout(page: ReactElement) { return ( diff --git a/web/pages/[workspaceSlug]/projects/[projectId]/pages/[pageId].tsx b/web/pages/[workspaceSlug]/projects/[projectId]/pages/[pageId].tsx index 93a814d57ed..bee4fc9c724 100644 --- a/web/pages/[workspaceSlug]/projects/[projectId]/pages/[pageId].tsx +++ b/web/pages/[workspaceSlug]/projects/[projectId]/pages/[pageId].tsx @@ -14,7 +14,7 @@ import { FileService } from "services/file.service"; // layouts import { AppLayout } from "layouts/app-layout"; // components -import { GptAssistantPopover } from "components/core"; +import { GptAssistantPopover, PageHead } from "components/core"; import { PageDetailsHeader } from "components/headers/page-details"; // ui import { DocumentEditorWithRef, DocumentReadOnlyEditorWithRef } from "@plane/document-editor"; @@ -256,113 +256,116 @@ const PageDetailsPage: NextPageWithLayout = observer(() => { currentProjectRole && [EUserProjectRoles.ADMIN, EUserProjectRoles.MEMBER].includes(currentProjectRole); return pageIdMobx ? ( -
-
- {isPageReadOnly ? ( - - ) : ( -
- ( - { - setShowAlert(true); - onChange(description_html); - handleSubmit(updatePage)(); - }} - duplicationConfig={userCanDuplicate ? { action: duplicate_page } : undefined} - pageArchiveConfig={ - userCanArchive - ? { - is_archived: archived_at ? true : false, - action: archived_at ? unArchivePage : archivePage, - } - : undefined - } - pageLockConfig={userCanLock ? { is_locked: false, action: lockPage } : undefined} - /> - )} + <> + +
+
+ {isPageReadOnly ? ( + - {projectId && envConfig?.has_openai_configured && ( -
- { - setGptModal((prevData) => !prevData); - // this is done so that the title do not reset after gpt popover closed - reset(getValues()); - }} - onResponse={(response) => { - handleAiAssistance(response); - }} - placement="top-end" - button={ - - } - className="!min-w-[38rem]" - /> -
- )} -
- )} - + ) : ( +
+ ( + { + setShowAlert(true); + onChange(description_html); + handleSubmit(updatePage)(); + }} + duplicationConfig={userCanDuplicate ? { action: duplicate_page } : undefined} + pageArchiveConfig={ + userCanArchive + ? { + is_archived: archived_at ? true : false, + action: archived_at ? unArchivePage : archivePage, + } + : undefined + } + pageLockConfig={userCanLock ? { is_locked: false, action: lockPage } : undefined} + /> + )} + /> + {projectId && envConfig?.has_openai_configured && ( +
+ { + setGptModal((prevData) => !prevData); + // this is done so that the title do not reset after gpt popover closed + reset(getValues()); + }} + onResponse={(response) => { + handleAiAssistance(response); + }} + placement="top-end" + button={ + + } + className="!min-w-[38rem]" + /> +
+ )} +
+ )} + +
-
+ ) : (
diff --git a/web/pages/[workspaceSlug]/projects/[projectId]/pages/index.tsx b/web/pages/[workspaceSlug]/projects/[projectId]/pages/index.tsx index 5dad4ede01c..fd2da02582f 100644 --- a/web/pages/[workspaceSlug]/projects/[projectId]/pages/index.tsx +++ b/web/pages/[workspaceSlug]/projects/[projectId]/pages/index.tsx @@ -6,7 +6,7 @@ import useSWR from "swr"; import { observer } from "mobx-react-lite"; import { useTheme } from "next-themes"; // hooks -import { useApplication, useEventTracker, useUser } from "hooks/store"; +import { useApplication, useEventTracker, useUser, useProject } from "hooks/store"; import useLocalStorage from "hooks/use-local-storage"; import useUserAuth from "hooks/use-user-auth"; import useSize from "hooks/use-window-size"; @@ -24,6 +24,7 @@ import { PAGE_TABS_LIST } from "constants/page"; import { useProjectPages } from "hooks/store/use-project-page"; import { EUserWorkspaceRoles } from "constants/workspace"; import { PAGE_EMPTY_STATE_DETAILS } from "constants/empty-state"; +import { PageHead } from "components/core"; const AllPagesList = dynamic(() => import("components/pages").then((a) => a.AllPagesList), { ssr: false, @@ -63,7 +64,7 @@ const ProjectPagesPage: NextPageWithLayout = observer(() => { commandPalette: { toggleCreatePageModal }, } = useApplication(); const { setTrackElement } = useEventTracker(); - + const { getProjectById } = useProject(); const { fetchProjectPages, fetchArchivedProjectPages, loader, archivedPageLoader, projectPageIds, archivedPageIds } = useProjectPages(); // hooks @@ -101,10 +102,12 @@ 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; const MobileTabList = () => ( @@ -129,6 +132,7 @@ const ProjectPagesPage: NextPageWithLayout = observer(() => { return ( <> + {projectPageIds && archivedPageIds && projectPageIds.length + archivedPageIds.length > 0 ? ( <> {workspaceSlug && projectId && ( diff --git a/web/pages/[workspaceSlug]/projects/[projectId]/settings/automations.tsx b/web/pages/[workspaceSlug]/projects/[projectId]/settings/automations.tsx index f17b7cc8a9a..8c4780cba0c 100644 --- a/web/pages/[workspaceSlug]/projects/[projectId]/settings/automations.tsx +++ b/web/pages/[workspaceSlug]/projects/[projectId]/settings/automations.tsx @@ -10,6 +10,7 @@ import { ProjectSettingLayout } from "layouts/settings-layout"; import useToast from "hooks/use-toast"; // components import { AutoArchiveAutomation, AutoCloseAutomation } from "components/automation"; +import { PageHead } from "components/core"; import { ProjectSettingHeader } from "components/headers"; // types import { NextPageWithLayout } from "lib/types"; @@ -41,16 +42,21 @@ const AutomationSettingsPage: NextPageWithLayout = observer(() => { }); }; + // derived values const isAdmin = currentProjectRole === EUserProjectRoles.ADMIN; + const pageTitle = projectDetails?.name ? `${projectDetails?.name} - Automations` : undefined; return ( -
-
-

Automations

-
- - -
+ <> + +
+
+

Automations

+
+ + +
+ ); }); diff --git a/web/pages/[workspaceSlug]/projects/[projectId]/settings/estimates.tsx b/web/pages/[workspaceSlug]/projects/[projectId]/settings/estimates.tsx index bc90c110fb1..3aea45adbc0 100644 --- a/web/pages/[workspaceSlug]/projects/[projectId]/settings/estimates.tsx +++ b/web/pages/[workspaceSlug]/projects/[projectId]/settings/estimates.tsx @@ -1,11 +1,12 @@ import { ReactElement } from "react"; import { observer } from "mobx-react-lite"; // hooks -import { useUser } from "hooks/store"; +import { useUser, useProject } from "hooks/store"; // layouts import { AppLayout } from "layouts/app-layout"; import { ProjectSettingLayout } from "layouts/settings-layout"; // components +import { PageHead } from "components/core"; import { ProjectSettingHeader } from "components/headers"; import { EstimatesList } from "components/estimates"; // types @@ -17,13 +18,18 @@ const EstimatesSettingsPage: NextPageWithLayout = observer(() => { const { membership: { currentProjectRole }, } = useUser(); - + const { currentProjectDetails } = useProject(); + // derived values const isAdmin = currentProjectRole === EUserProjectRoles.ADMIN; + const pageTitle = currentProjectDetails?.name ? `${currentProjectDetails?.name} - Estimates` : undefined; return ( -
- -
+ <> + +
+ +
+ ); }); diff --git a/web/pages/[workspaceSlug]/projects/[projectId]/settings/features.tsx b/web/pages/[workspaceSlug]/projects/[projectId]/settings/features.tsx index a6f66d9630a..b618437abd0 100644 --- a/web/pages/[workspaceSlug]/projects/[projectId]/settings/features.tsx +++ b/web/pages/[workspaceSlug]/projects/[projectId]/settings/features.tsx @@ -1,41 +1,48 @@ import { ReactElement } from "react"; import { useRouter } from "next/router"; import useSWR from "swr"; +import { observer } from "mobx-react"; // hooks -import { useUser } from "hooks/store"; +import { useProject, useUser } from "hooks/store"; // layouts import { AppLayout } from "layouts/app-layout"; import { ProjectSettingLayout } from "layouts/settings-layout"; // components +import { PageHead } from "components/core"; import { ProjectSettingHeader } from "components/headers"; import { ProjectFeaturesList } from "components/project"; // types import { NextPageWithLayout } from "lib/types"; -const FeaturesSettingsPage: NextPageWithLayout = () => { +const FeaturesSettingsPage: NextPageWithLayout = observer(() => { const router = useRouter(); const { workspaceSlug, projectId } = router.query; // store const { membership: { fetchUserProjectInfo }, } = useUser(); - + const { currentProjectDetails } = useProject(); + // fetch the project details const { data: memberDetails } = useSWR( workspaceSlug && projectId ? `PROJECT_MEMBERS_ME_${workspaceSlug}_${projectId}` : null, workspaceSlug && projectId ? () => fetchUserProjectInfo(workspaceSlug.toString(), projectId.toString()) : null ); - + // derived values const isAdmin = memberDetails?.role === 20; + const pageTitle = currentProjectDetails?.name ? `${currentProjectDetails?.name} - Features` : undefined; return ( -
-
-

Features

-
- -
+ <> + +
+
+

Features

+
+ +
+ ); -}; +}); FeaturesSettingsPage.getLayout = function getLayout(page: ReactElement) { return ( diff --git a/web/pages/[workspaceSlug]/projects/[projectId]/settings/index.tsx b/web/pages/[workspaceSlug]/projects/[projectId]/settings/index.tsx index 72b8ce6f559..347d64f8440 100644 --- a/web/pages/[workspaceSlug]/projects/[projectId]/settings/index.tsx +++ b/web/pages/[workspaceSlug]/projects/[projectId]/settings/index.tsx @@ -8,6 +8,7 @@ import { useProject } from "hooks/store"; import { AppLayout } from "layouts/app-layout"; import { ProjectSettingLayout } from "layouts/settings-layout"; // components +import { PageHead } from "components/core"; import { ProjectSettingHeader } from "components/headers"; import { DeleteProjectModal, @@ -32,14 +33,15 @@ const GeneralSettingsPage: NextPageWithLayout = observer(() => { workspaceSlug && projectId ? `PROJECT_DETAILS_${projectId}` : null, workspaceSlug && projectId ? () => fetchProjectDetails(workspaceSlug.toString(), projectId.toString()) : null ); - + // derived values + const isAdmin = currentProjectDetails?.member_role === 20; + const pageTitle = currentProjectDetails?.name ? `${currentProjectDetails?.name} - General Settings` : undefined; // const currentNetwork = NETWORK_CHOICES.find((n) => n.key === projectDetails?.network); // const selectedNetwork = NETWORK_CHOICES.find((n) => n.key === watch("network")); - const isAdmin = currentProjectDetails?.member_role === 20; - return ( <> + {currentProjectDetails && ( { +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( workspaceSlug && projectId ? PROJECT_DETAILS(projectId as string) : null, workspaceSlug && projectId ? () => projectService.getProject(workspaceSlug as string, projectId as string) : null ); - + // fetch Integrations list const { data: workspaceIntegrations } = useSWR( workspaceSlug ? WORKSPACE_INTEGRATIONS(workspaceSlug as string) : null, () => (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; return ( -
-
-

Integrations

-
- {workspaceIntegrations ? ( - workspaceIntegrations.length > 0 ? ( -
- {workspaceIntegrations.map((integration) => ( - - ))} -
+ <> + +
+
+

Integrations

+
+ {workspaceIntegrations ? ( + workspaceIntegrations.length > 0 ? ( +
+ {workspaceIntegrations.map((integration) => ( + + ))} +
+ ) : ( +
+ router.push(`/${workspaceSlug}/settings/integrations`), + }} + size="lg" + disabled={!isAdmin} + /> +
+ ) ) : ( -
- router.push(`/${workspaceSlug}/settings/integrations`), - }} - size="lg" - disabled={!isAdmin} - /> -
- ) - ) : ( - - )} -
+ + )} +
+ ); -}; +}); ProjectIntegrationsPage.getLayout = function getLayout(page: ReactElement) { return ( diff --git a/web/pages/[workspaceSlug]/projects/[projectId]/settings/labels.tsx b/web/pages/[workspaceSlug]/projects/[projectId]/settings/labels.tsx index 02a700bbee5..3bb1c8c04b6 100644 --- a/web/pages/[workspaceSlug]/projects/[projectId]/settings/labels.tsx +++ b/web/pages/[workspaceSlug]/projects/[projectId]/settings/labels.tsx @@ -1,18 +1,30 @@ import { ReactElement } from "react"; +import { observer } from "mobx-react"; // layouts import { AppLayout } from "layouts/app-layout"; import { ProjectSettingLayout } from "layouts/settings-layout"; // components +import { PageHead } from "components/core"; import { ProjectSettingsLabelList } from "components/labels"; import { ProjectSettingHeader } from "components/headers"; // types import { NextPageWithLayout } from "lib/types"; +// hooks +import { useProject } from "hooks/store"; -const LabelsSettingsPage: NextPageWithLayout = () => ( -
- -
-); +const LabelsSettingsPage: NextPageWithLayout = observer(() => { + const { currentProjectDetails } = useProject(); + const pageTitle = currentProjectDetails?.name ? `${currentProjectDetails?.name} - Labels` : undefined; + + return ( + <> + +
+ +
+ + ); +}); LabelsSettingsPage.getLayout = function getLayout(page: ReactElement) { return ( diff --git a/web/pages/[workspaceSlug]/projects/[projectId]/settings/members.tsx b/web/pages/[workspaceSlug]/projects/[projectId]/settings/members.tsx index 3b66f40bb85..f74d464d591 100644 --- a/web/pages/[workspaceSlug]/projects/[projectId]/settings/members.tsx +++ b/web/pages/[workspaceSlug]/projects/[projectId]/settings/members.tsx @@ -1,19 +1,33 @@ import { ReactElement } from "react"; +import { observer } from "mobx-react"; // layouts import { AppLayout } from "layouts/app-layout"; import { ProjectSettingLayout } from "layouts/settings-layout"; // components +import { PageHead } from "components/core"; import { ProjectSettingHeader } from "components/headers"; import { ProjectMemberList, ProjectSettingsMemberDefaults } from "components/project"; // types import { NextPageWithLayout } from "lib/types"; +// hooks +import { useProject } from "hooks/store"; -const MembersSettingsPage: NextPageWithLayout = () => ( -
- - -
-); +const MembersSettingsPage: NextPageWithLayout = observer(() => { + // store + const { currentProjectDetails } = useProject(); + // derived values + const pageTitle = currentProjectDetails?.name ? `${currentProjectDetails?.name} - Members` : undefined; + + return ( + <> + +
+ + +
+ + ); +}); MembersSettingsPage.getLayout = function getLayout(page: ReactElement) { return ( diff --git a/web/pages/[workspaceSlug]/projects/[projectId]/views/[viewId].tsx b/web/pages/[workspaceSlug]/projects/[projectId]/views/[viewId].tsx index 51c81119574..2ac6b2e0046 100644 --- a/web/pages/[workspaceSlug]/projects/[projectId]/views/[viewId].tsx +++ b/web/pages/[workspaceSlug]/projects/[projectId]/views/[viewId].tsx @@ -1,13 +1,15 @@ import { ReactElement } from "react"; import { useRouter } from "next/router"; import useSWR from "swr"; +import { observer } from "mobx-react"; // hooks -import { useProjectView } from "hooks/store"; +import { useProject, useProjectView } from "hooks/store"; // layouts import { AppLayout } from "layouts/app-layout"; // components import { ProjectViewLayoutRoot } from "components/issues"; import { ProjectViewIssuesHeader } from "components/headers"; +import { PageHead } from "components/core"; // ui import { EmptyState } from "components/common"; // assets @@ -15,12 +17,17 @@ import emptyView from "public/empty-state/view.svg"; // types import { NextPageWithLayout } from "lib/types"; -const ProjectViewIssuesPage: NextPageWithLayout = () => { +const ProjectViewIssuesPage: NextPageWithLayout = observer(() => { // router const router = useRouter(); const { workspaceSlug, projectId, viewId } = router.query; // store hooks - const { fetchViewDetails } = useProjectView(); + const { fetchViewDetails, getViewById } = useProjectView(); + const { getProjectById } = useProject(); + // derived values + const projectView = viewId ? getViewById(viewId.toString()) : undefined; + const project = projectId ? getProjectById(projectId.toString()) : undefined; + const pageTitle = project?.name && projectView?.name ? `${project?.name} - ${projectView?.name}` : undefined; const { error } = useSWR( workspaceSlug && projectId && viewId ? `VIEW_DETAILS_${viewId.toString()}` : null, @@ -42,11 +49,14 @@ const ProjectViewIssuesPage: NextPageWithLayout = () => { }} /> ) : ( - + <> + + + )} ); -}; +}); ProjectViewIssuesPage.getLayout = function getLayout(page: ReactElement) { return ( diff --git a/web/pages/[workspaceSlug]/projects/[projectId]/views/index.tsx b/web/pages/[workspaceSlug]/projects/[projectId]/views/index.tsx index 2393187635c..33be5d10201 100644 --- a/web/pages/[workspaceSlug]/projects/[projectId]/views/index.tsx +++ b/web/pages/[workspaceSlug]/projects/[projectId]/views/index.tsx @@ -1,13 +1,34 @@ import { ReactElement } from "react"; +import { useRouter } from "next/router"; +import { observer } from "mobx-react"; // components import { ProjectViewsHeader } from "components/headers"; import { ProjectViewsList } from "components/views"; +import { PageHead } from "components/core"; +// hooks +import { useProject } from "hooks/store"; // layouts import { AppLayout } from "layouts/app-layout"; // types import { NextPageWithLayout } from "lib/types"; -const ProjectViewsPage: NextPageWithLayout = () => ; +const ProjectViewsPage: NextPageWithLayout = observer(() => { + // router + const router = useRouter(); + const { projectId } = router.query; + // store + const { getProjectById } = useProject(); + // derived values + const project = projectId ? getProjectById(projectId.toString()) : undefined; + const pageTitle = project?.name ? `${project?.name} - Views` : undefined; + + return ( + <> + + + + ); +}); ProjectViewsPage.getLayout = function getLayout(page: ReactElement) { return ( diff --git a/web/pages/[workspaceSlug]/projects/index.tsx b/web/pages/[workspaceSlug]/projects/index.tsx index 734098280f6..1a145a2d1c9 100644 --- a/web/pages/[workspaceSlug]/projects/index.tsx +++ b/web/pages/[workspaceSlug]/projects/index.tsx @@ -1,13 +1,28 @@ import { ReactElement } from "react"; +import { observer } from "mobx-react"; // components +import { PageHead } from "components/core"; import { ProjectCardList } from "components/project"; import { ProjectsHeader } from "components/headers"; // layouts import { AppLayout } from "layouts/app-layout"; // type import { NextPageWithLayout } from "lib/types"; +import { useWorkspace } from "hooks/store"; -const ProjectsPage: NextPageWithLayout = () => ; +const ProjectsPage: NextPageWithLayout = observer(() => { + // store + const { currentWorkspace } = useWorkspace(); + // derived values + const pageTitle = currentWorkspace?.name ? `${currentWorkspace?.name} - Projects` : undefined; + + return ( + <> + + + + ); +}); ProjectsPage.getLayout = function getLayout(page: ReactElement) { return }>{page}; diff --git a/web/pages/[workspaceSlug]/settings/api-tokens.tsx b/web/pages/[workspaceSlug]/settings/api-tokens.tsx index 3d65c2d7b53..1f203ff04c5 100644 --- a/web/pages/[workspaceSlug]/settings/api-tokens.tsx +++ b/web/pages/[workspaceSlug]/settings/api-tokens.tsx @@ -4,7 +4,7 @@ import { observer } from "mobx-react-lite"; import useSWR from "swr"; import { useTheme } from "next-themes"; // store hooks -import { useUser } from "hooks/store"; +import { useUser, useWorkspace } from "hooks/store"; // layouts import { AppLayout } from "layouts/app-layout"; import { WorkspaceSettingLayout } from "layouts/settings-layout"; @@ -23,6 +23,7 @@ import { NextPageWithLayout } from "lib/types"; import { API_TOKENS_LIST } from "constants/fetch-keys"; import { EUserWorkspaceRoles } from "constants/workspace"; import { WORKSPACE_SETTINGS_EMPTY_STATE_DETAILS } from "constants/empty-state"; +import { PageHead } from "components/core"; const apiTokenService = new APITokenService(); @@ -39,6 +40,7 @@ const ApiTokensPage: NextPageWithLayout = observer(() => { membership: { currentWorkspaceRole }, currentUser, } = useUser(); + const { currentWorkspace } = useWorkspace(); const isAdmin = currentWorkspaceRole === EUserWorkspaceRoles.ADMIN; @@ -49,12 +51,16 @@ const ApiTokensPage: NextPageWithLayout = observer(() => { const emptyStateDetail = WORKSPACE_SETTINGS_EMPTY_STATE_DETAILS["api-tokens"]; const isLightMode = resolvedTheme ? resolvedTheme === "light" : currentUser?.theme.theme === "light"; const emptyStateImage = getEmptyStateImagePath("workspace-settings", "api-tokens", isLightMode); + const pageTitle = currentWorkspace?.name ? `${currentWorkspace.name} - API Tokens` : undefined; if (!isAdmin) return ( -
-

You are not authorized to access this page.

-
+ <> + +
+

You are not authorized to access this page.

+
+ ); if (!tokens) { @@ -63,6 +69,7 @@ const ApiTokensPage: NextPageWithLayout = observer(() => { return ( <> + setIsCreateTokenModalOpen(false)} />
{tokens.length > 0 ? ( diff --git a/web/pages/[workspaceSlug]/settings/billing.tsx b/web/pages/[workspaceSlug]/settings/billing.tsx index 7cc9c7a2835..f4f5d5397dd 100644 --- a/web/pages/[workspaceSlug]/settings/billing.tsx +++ b/web/pages/[workspaceSlug]/settings/billing.tsx @@ -1,11 +1,12 @@ import { observer } from "mobx-react-lite"; // hooks -import { useUser } from "hooks/store"; +import { useUser, useWorkspace } from "hooks/store"; // layouts import { AppLayout } from "layouts/app-layout"; import { WorkspaceSettingLayout } from "layouts/settings-layout"; // component import { WorkspaceSettingHeader } from "components/headers"; +import { PageHead } from "components/core"; // ui import { Button } from "@plane/ui"; // types @@ -18,33 +19,41 @@ const BillingSettingsPage: NextPageWithLayout = observer(() => { const { membership: { currentWorkspaceRole }, } = useUser(); - + const { currentWorkspace } = useWorkspace(); + // derived values const isAdmin = currentWorkspaceRole === EUserWorkspaceRoles.ADMIN; + const pageTitle = currentWorkspace?.name ? `${currentWorkspace.name} - Billing & Plans` : undefined; if (!isAdmin) return ( -
-

You are not authorized to access this page.

-
+ <> + +
+

You are not authorized to access this page.

+
+ ); return ( -
-
-
-

Billing & Plans

-
-
-
+ <> + +
-

Current plan

-

You are currently using the free plan

- - - +
+

Billing & Plans

+
+
+
+
+

Current plan

+

You are currently using the free plan

+ + + +
-
-
+
+ ); }); diff --git a/web/pages/[workspaceSlug]/settings/exports.tsx b/web/pages/[workspaceSlug]/settings/exports.tsx index f491581fd30..c124a642392 100644 --- a/web/pages/[workspaceSlug]/settings/exports.tsx +++ b/web/pages/[workspaceSlug]/settings/exports.tsx @@ -1,12 +1,13 @@ import { observer } from "mobx-react-lite"; // hooks -import { useUser } from "hooks/store"; +import { useUser, useWorkspace } from "hooks/store"; // layout import { AppLayout } from "layouts/app-layout"; import { WorkspaceSettingLayout } from "layouts/settings-layout"; // components import { WorkspaceSettingHeader } from "components/headers"; import ExportGuide from "components/exporter/guide"; +import { PageHead } from "components/core"; // types import { NextPageWithLayout } from "lib/types"; // constants @@ -17,24 +18,33 @@ const ExportsPage: NextPageWithLayout = observer(() => { const { membership: { currentWorkspaceRole }, } = useUser(); + const { currentWorkspace } = useWorkspace(); + // derived values const hasPageAccess = currentWorkspaceRole && [EUserWorkspaceRoles.ADMIN, EUserWorkspaceRoles.MEMBER].includes(currentWorkspaceRole); + const pageTitle = currentWorkspace?.name ? `${currentWorkspace.name} - Exports` : undefined; if (!hasPageAccess) return ( -
-

You are not authorized to access this page.

-
+ <> + +
+

You are not authorized to access this page.

+
+ ); return ( -
-
-

Exports

+ <> + +
+
+

Exports

+
+
- -
+ ); }); diff --git a/web/pages/[workspaceSlug]/settings/imports.tsx b/web/pages/[workspaceSlug]/settings/imports.tsx index 2e4be4cc1a8..5178209d272 100644 --- a/web/pages/[workspaceSlug]/settings/imports.tsx +++ b/web/pages/[workspaceSlug]/settings/imports.tsx @@ -1,12 +1,13 @@ import { observer } from "mobx-react-lite"; // hooks -import { useUser } from "hooks/store"; +import { useUser, useWorkspace } from "hooks/store"; // layouts import { WorkspaceSettingLayout } from "layouts/settings-layout"; import { AppLayout } from "layouts/app-layout"; // components import IntegrationGuide from "components/integration/guide"; import { WorkspaceSettingHeader } from "components/headers"; +import { PageHead } from "components/core"; // types import { NextPageWithLayout } from "lib/types"; // constants @@ -17,23 +18,32 @@ const ImportsPage: NextPageWithLayout = observer(() => { const { membership: { currentWorkspaceRole }, } = useUser(); + const { currentWorkspace } = useWorkspace(); + // derived values const isAdmin = currentWorkspaceRole === EUserWorkspaceRoles.ADMIN; + const pageTitle = currentWorkspace?.name ? `${currentWorkspace.name} - Imports` : undefined; if (!isAdmin) return ( -
-

You are not authorized to access this page.

-
+ <> + +
+

You are not authorized to access this page.

+
+ ); return ( -
-
-

Imports

-
- -
+ <> + +
+
+

Imports

+
+ +
+ ); }); diff --git a/web/pages/[workspaceSlug]/settings/index.tsx b/web/pages/[workspaceSlug]/settings/index.tsx index b2920aade03..2924b13c4a8 100644 --- a/web/pages/[workspaceSlug]/settings/index.tsx +++ b/web/pages/[workspaceSlug]/settings/index.tsx @@ -1,14 +1,30 @@ import { ReactElement } from "react"; +import { observer } from "mobx-react"; // layouts import { AppLayout } from "layouts/app-layout"; import { WorkspaceSettingLayout } from "layouts/settings-layout"; +// hooks +import { useWorkspace } from "hooks/store"; // components import { WorkspaceSettingHeader } from "components/headers"; import { WorkspaceDetails } from "components/workspace"; +import { PageHead } from "components/core"; // types import { NextPageWithLayout } from "lib/types"; -const WorkspaceSettingsPage: NextPageWithLayout = () => ; +const WorkspaceSettingsPage: NextPageWithLayout = observer(() => { + // store hooks + const { currentWorkspace } = useWorkspace(); + // derived values + const pageTitle = currentWorkspace?.name ? `${currentWorkspace.name} - General Settings` : undefined; + + return ( + <> + + + + ); +}); WorkspaceSettingsPage.getLayout = function getLayout(page: ReactElement) { return ( diff --git a/web/pages/[workspaceSlug]/settings/integrations.tsx b/web/pages/[workspaceSlug]/settings/integrations.tsx index 940c90f3a67..500533877a4 100644 --- a/web/pages/[workspaceSlug]/settings/integrations.tsx +++ b/web/pages/[workspaceSlug]/settings/integrations.tsx @@ -3,7 +3,7 @@ import { useRouter } from "next/router"; import { observer } from "mobx-react-lite"; import useSWR from "swr"; // hooks -import { useUser } from "hooks/store"; +import { useUser, useWorkspace } from "hooks/store"; // services import { IntegrationService } from "services/integrations"; // layouts @@ -12,6 +12,7 @@ import { WorkspaceSettingLayout } from "layouts/settings-layout"; // components import { SingleIntegrationCard } from "components/integration"; import { WorkspaceSettingHeader } from "components/headers"; +import { PageHead } from "components/core"; // ui import { IntegrationAndImportExportBanner, IntegrationsSettingsLoader } from "components/ui"; // types @@ -31,14 +32,20 @@ const WorkspaceIntegrationsPage: NextPageWithLayout = observer(() => { const { membership: { currentWorkspaceRole }, } = useUser(); + const { currentWorkspace } = useWorkspace(); + // derived values const isAdmin = currentWorkspaceRole === EUserWorkspaceRoles.ADMIN; + const pageTitle = currentWorkspace?.name ? `${currentWorkspace.name} - Integrations` : undefined; if (!isAdmin) return ( -
-

You are not authorized to access this page.

-
+ <> + +
+

You are not authorized to access this page.

+
+ ); const { data: appIntegrations } = useSWR(workspaceSlug && isAdmin ? APP_INTEGRATIONS : null, () => @@ -46,16 +53,21 @@ const WorkspaceIntegrationsPage: NextPageWithLayout = observer(() => { ); return ( -
- -
- {appIntegrations ? ( - appIntegrations.map((integration) => ) - ) : ( - - )} -
-
+ <> + +
+ +
+ {appIntegrations ? ( + appIntegrations.map((integration) => ( + + )) + ) : ( + + )} +
+
+ ); }); diff --git a/web/pages/[workspaceSlug]/settings/members.tsx b/web/pages/[workspaceSlug]/settings/members.tsx index 6e9d8d924a8..b8739ae7756 100644 --- a/web/pages/[workspaceSlug]/settings/members.tsx +++ b/web/pages/[workspaceSlug]/settings/members.tsx @@ -3,7 +3,7 @@ import { useRouter } from "next/router"; import { observer } from "mobx-react-lite"; import { Search } from "lucide-react"; // hooks -import { useEventTracker, useMember, useUser } from "hooks/store"; +import { useEventTracker, useMember, useUser, useWorkspace } from "hooks/store"; import useToast from "hooks/use-toast"; // layouts import { AppLayout } from "layouts/app-layout"; @@ -11,6 +11,7 @@ import { WorkspaceSettingLayout } from "layouts/settings-layout"; // components import { WorkspaceSettingHeader } from "components/headers"; import { SendWorkspaceInvitationModal, WorkspaceMembersList } from "components/workspace"; +import { PageHead } from "components/core"; // ui import { Button } from "@plane/ui"; // types @@ -37,6 +38,7 @@ const WorkspaceMembersSettingsPage: NextPageWithLayout = observer(() => { const { workspace: { inviteMembersToWorkspace }, } = useMember(); + const { currentWorkspace } = useWorkspace(); // toast alert const { setToastAlert } = useToast(); @@ -83,11 +85,14 @@ const WorkspaceMembersSettingsPage: NextPageWithLayout = observer(() => { }); }; + // derived values const hasAddMemberPermission = currentWorkspaceRole && [EUserWorkspaceRoles.ADMIN, EUserWorkspaceRoles.MEMBER].includes(currentWorkspaceRole); + const pageTitle = currentWorkspace?.name ? `${currentWorkspace.name} - Members` : undefined; return ( <> + setInviteModal(false)} diff --git a/web/pages/[workspaceSlug]/settings/webhooks/[webhookId].tsx b/web/pages/[workspaceSlug]/settings/webhooks/[webhookId].tsx index 562578b6620..60e65e90544 100644 --- a/web/pages/[workspaceSlug]/settings/webhooks/[webhookId].tsx +++ b/web/pages/[workspaceSlug]/settings/webhooks/[webhookId].tsx @@ -3,7 +3,7 @@ import { useRouter } from "next/router"; import { observer } from "mobx-react-lite"; import useSWR from "swr"; // hooks -import { useUser, useWebhook } from "hooks/store"; +import { useUser, useWebhook, useWorkspace } from "hooks/store"; // layouts import { AppLayout } from "layouts/app-layout"; import { WorkspaceSettingLayout } from "layouts/settings-layout"; @@ -12,6 +12,7 @@ import useToast from "hooks/use-toast"; // components import { WorkspaceSettingHeader } from "components/headers"; import { DeleteWebhookModal, WebhookDeleteSection, WebhookForm } from "components/web-hooks"; +import { PageHead } from "components/core"; // ui import { Spinner } from "@plane/ui"; // types @@ -29,6 +30,7 @@ const WebhookDetailsPage: NextPageWithLayout = observer(() => { membership: { currentWorkspaceRole }, } = useUser(); const { currentWebhook, fetchWebhookById, updateWebhook } = useWebhook(); + const { currentWorkspace } = useWorkspace(); // toast const { setToastAlert } = useToast(); @@ -38,6 +40,7 @@ const WebhookDetailsPage: NextPageWithLayout = observer(() => { // }, [clearSecretKey, isCreated]); const isAdmin = currentWorkspaceRole === 20; + const pageTitle = currentWorkspace?.name ? `${currentWorkspace.name} - Webhook` : undefined; useSWR( workspaceSlug && webhookId && isAdmin ? `WEBHOOK_DETAILS_${workspaceSlug}_${webhookId}` : null, @@ -76,9 +79,12 @@ const WebhookDetailsPage: NextPageWithLayout = observer(() => { if (!isAdmin) return ( -
-

You are not authorized to access this page.

-
+ <> + +
+

You are not authorized to access this page.

+
+ ); if (!currentWebhook) @@ -90,6 +96,7 @@ const WebhookDetailsPage: NextPageWithLayout = observer(() => { return ( <> + setDeleteWebhookModal(false)} />
await handleUpdateWebhook(data)} data={currentWebhook} /> diff --git a/web/pages/[workspaceSlug]/settings/webhooks/index.tsx b/web/pages/[workspaceSlug]/settings/webhooks/index.tsx index cafac485e62..46c7e99cb7b 100644 --- a/web/pages/[workspaceSlug]/settings/webhooks/index.tsx +++ b/web/pages/[workspaceSlug]/settings/webhooks/index.tsx @@ -19,6 +19,7 @@ import { WebhookSettingsLoader } from "components/ui"; import { NextPageWithLayout } from "lib/types"; // constants import { WORKSPACE_SETTINGS_EMPTY_STATE_DETAILS } from "constants/empty-state"; +import { PageHead } from "components/core"; const WebhooksListPage: NextPageWithLayout = observer(() => { // states @@ -47,6 +48,7 @@ const WebhooksListPage: NextPageWithLayout = observer(() => { const isLightMode = resolvedTheme ? resolvedTheme === "light" : currentUser?.theme.theme === "light"; const emptyStateImage = getEmptyStateImagePath("workspace-settings", "webhooks", isLightMode); + const pageTitle = currentWorkspace?.name ? `${currentWorkspace.name} - Webhooks` : undefined; // clear secret key when modal is closed. useEffect(() => { @@ -55,53 +57,59 @@ const WebhooksListPage: NextPageWithLayout = observer(() => { if (!isAdmin) return ( -
-

You are not authorized to access this page.

-
+ <> + +
+

You are not authorized to access this page.

+
+ ); if (!webhooks) return ; return ( -
- { - setShowCreateWebhookModal(false); - }} - /> - {Object.keys(webhooks).length > 0 ? ( -
-
-
Webhooks
- -
- -
- ) : ( -
-
-
Webhooks
- + <> + +
+ { + setShowCreateWebhookModal(false); + }} + /> + {Object.keys(webhooks).length > 0 ? ( +
+
+
Webhooks
+ +
+
-
- + ) : ( +
+
+
Webhooks
+ +
+
+ +
-
- )} -
+ )} +
+ ); }); diff --git a/web/pages/[workspaceSlug]/workspace-views/[globalViewId].tsx b/web/pages/[workspaceSlug]/workspace-views/[globalViewId].tsx index e89e2c70f9b..43a0ad49439 100644 --- a/web/pages/[workspaceSlug]/workspace-views/[globalViewId].tsx +++ b/web/pages/[workspaceSlug]/workspace-views/[globalViewId].tsx @@ -7,15 +7,26 @@ import { AllIssueLayoutRoot } from "components/issues"; import { GlobalIssuesHeader } from "components/headers"; // types import { NextPageWithLayout } from "lib/types"; +import { observer } from "mobx-react"; +import { useWorkspace } from "hooks/store"; +import { PageHead } from "components/core"; -const GlobalViewIssuesPage: NextPageWithLayout = () => ( -
-
- - -
-
-); +const GlobalViewIssuesPage: NextPageWithLayout = observer(() => { + const { currentWorkspace } = useWorkspace(); + // derived values + const pageTitle = currentWorkspace?.name ? `${currentWorkspace?.name} - Views` : undefined; + return ( + <> + +
+
+ + +
+
+ + ); +}); GlobalViewIssuesPage.getLayout = function getLayout(page: ReactElement) { return }>{page}; diff --git a/web/pages/[workspaceSlug]/workspace-views/index.tsx b/web/pages/[workspaceSlug]/workspace-views/index.tsx index db1a6fca72f..656120a3eac 100644 --- a/web/pages/[workspaceSlug]/workspace-views/index.tsx +++ b/web/pages/[workspaceSlug]/workspace-views/index.tsx @@ -1,7 +1,9 @@ import React, { useState, ReactElement } from "react"; +import { observer } from "mobx-react"; // layouts import { AppLayout } from "layouts/app-layout"; // components +import { PageHead } from "components/core"; import { GlobalDefaultViewListItem, GlobalViewsList } from "components/workspace"; import { GlobalIssuesHeader } from "components/headers"; // ui @@ -12,31 +14,40 @@ import { Search } from "lucide-react"; import { NextPageWithLayout } from "lib/types"; // constants import { DEFAULT_GLOBAL_VIEWS_LIST } from "constants/workspace"; +// hooks +import { useWorkspace } from "hooks/store"; -const WorkspaceViewsPage: NextPageWithLayout = () => { +const WorkspaceViewsPage: NextPageWithLayout = observer(() => { const [query, setQuery] = useState(""); + // store + const { currentWorkspace } = useWorkspace(); + // derived values + const pageTitle = currentWorkspace?.name ? `${currentWorkspace?.name} - All Views` : undefined; return ( -
-
-
- - setQuery(e.target.value)} - placeholder="Search" - mode="true-transparent" - /> + <> + +
+
+
+ + setQuery(e.target.value)} + placeholder="Search" + mode="true-transparent" + /> +
+ {DEFAULT_GLOBAL_VIEWS_LIST.filter((v) => v.label.toLowerCase().includes(query.toLowerCase())).map((option) => ( + + ))} +
- {DEFAULT_GLOBAL_VIEWS_LIST.filter((v) => v.label.toLowerCase().includes(query.toLowerCase())).map((option) => ( - - ))} - -
+ ); -}; +}); WorkspaceViewsPage.getLayout = function getLayout(page: ReactElement) { return }>{page}; diff --git a/web/pages/accounts/forgot-password.tsx b/web/pages/accounts/forgot-password.tsx index 07fa86045ec..0eef16009c3 100644 --- a/web/pages/accounts/forgot-password.tsx +++ b/web/pages/accounts/forgot-password.tsx @@ -12,6 +12,7 @@ import { useEventTracker } from "hooks/store"; import DefaultLayout from "layouts/default-layout"; // components import { LatestFeatureBlock } from "components/common"; +import { PageHead } from "components/core"; // ui import { Button, Input } from "@plane/ui"; // images @@ -85,59 +86,62 @@ const ForgotPasswordPage: NextPageWithLayout = () => { }; return ( -
-
-
- Plane Logo - Plane + <> + +
+
+
+ Plane Logo + Plane +
-
-
-
-
-

- Get on your flight deck -

-

Get a link to reset your password

- - checkEmailValidity(value) || "Email is invalid", - }} - render={({ field: { value, onChange, ref } }) => ( - - )} - /> - - +
+
+
+

+ Get on your flight deck +

+

Get a link to reset your password

+
+ checkEmailValidity(value) || "Email is invalid", + }} + render={({ field: { value, onChange, ref } }) => ( + + )} + /> + + +
+
-
-
+ ); }; diff --git a/web/pages/accounts/reset-password.tsx b/web/pages/accounts/reset-password.tsx index c4258f39e4a..c848245ac1c 100644 --- a/web/pages/accounts/reset-password.tsx +++ b/web/pages/accounts/reset-password.tsx @@ -12,6 +12,7 @@ import { useEventTracker } from "hooks/store"; import DefaultLayout from "layouts/default-layout"; // components import { LatestFeatureBlock } from "components/common"; +import { PageHead } from "components/core"; // ui import { Button, Input } from "@plane/ui"; // images @@ -90,90 +91,93 @@ const ResetPasswordPage: NextPageWithLayout = () => { }; return ( -
-
-
- Plane Logo - Plane + <> + +
+
+
+ Plane Logo + Plane +
-
-
-
-
-

- Let{"'"}s get a new password -

-
- checkEmailValidity(value) || "Email is invalid", - }} - render={({ field: { value, onChange, ref } }) => ( - - )} - /> - ( -
+
+
+
+

+ Let{"'"}s get a new password +

+ + checkEmailValidity(value) || "Email is invalid", + }} + render={({ field: { value, onChange, ref } }) => ( - {showPassword ? ( - setShowPassword(false)} - /> - ) : ( - setShowPassword(true)} + )} + /> + ( +
+ - )} -
- )} - /> - - + {showPassword ? ( + setShowPassword(false)} + /> + ) : ( + setShowPassword(true)} + /> + )} +
+ )} + /> + + +
+
-
-
+ ); }; diff --git a/web/pages/accounts/sign-up.tsx b/web/pages/accounts/sign-up.tsx index 5b5648439c5..cba9c0166e8 100644 --- a/web/pages/accounts/sign-up.tsx +++ b/web/pages/accounts/sign-up.tsx @@ -7,6 +7,7 @@ import { useApplication, useUser } from "hooks/store"; import DefaultLayout from "layouts/default-layout"; // components import { SignUpRoot } from "components/account"; +import { PageHead } from "components/core"; // ui import { Spinner } from "@plane/ui"; // assets @@ -29,20 +30,23 @@ const SignUpPage: NextPageWithLayout = observer(() => { ); return ( -
-
-
- Plane Logo - Plane + <> + +
+
+
+ Plane Logo + Plane +
-
-
-
- +
+
+ +
-
+ ); }); diff --git a/web/pages/create-workspace.tsx b/web/pages/create-workspace.tsx index 10eb11f554e..952ed0b6874 100644 --- a/web/pages/create-workspace.tsx +++ b/web/pages/create-workspace.tsx @@ -11,6 +11,7 @@ import DefaultLayout from "layouts/default-layout"; import { UserAuthWrapper } from "layouts/auth-layout"; // components import { CreateWorkspaceForm } from "components/workspace"; +import { PageHead } from "components/core"; // images import BlackHorizontalLogo from "public/plane-logos/black-horizontal-with-blue-logo.svg"; import WhiteHorizontalLogo from "public/plane-logos/white-horizontal-with-blue-logo.svg"; @@ -37,38 +38,41 @@ const CreateWorkspacePage: NextPageWithLayout = observer(() => { }; return ( -
-
-
- -
- {theme === "light" ? ( - Plane black logo - ) : ( - Plane white logo - )} + <> + +
+
+
+ +
+ {theme === "light" ? ( + Plane black logo + ) : ( + Plane white logo + )} +
+ +
+ {currentUser?.email}
- -
- {currentUser?.email}
-
-
-
-

Create your workspace

-
- +
+
+

Create your workspace

+
+ +
-
+ ); }); diff --git a/web/pages/god-mode/ai.tsx b/web/pages/god-mode/ai.tsx index 3ceb902c902..b84e98098d3 100644 --- a/web/pages/god-mode/ai.tsx +++ b/web/pages/god-mode/ai.tsx @@ -13,6 +13,7 @@ import { Loader } from "@plane/ui"; import { Lightbulb } from "lucide-react"; // components import { InstanceAIForm } from "components/instance"; +import { PageHead } from "components/core"; const InstanceAdminAIPage: NextPageWithLayout = observer(() => { // store @@ -23,37 +24,40 @@ const InstanceAdminAIPage: NextPageWithLayout = observer(() => { useSWR("INSTANCE_CONFIGURATIONS", () => fetchInstanceConfigurations()); return ( -
-
-
AI features for all your workspaces
-
- Configure your AI API credentials so Plane AI features are turned on for all your workspaces. -
-
- {formattedConfig ? ( - <> -
-
OpenAI
-
If you use ChatGPT, this is for you.
+ <> + +
+
+
AI features for all your workspaces
+
+ Configure your AI API credentials so Plane AI features are turned on for all your workspaces.
- -
-
- -
If you have a preferred AI models vendor, please get in touch with us.
+
+ {formattedConfig ? ( + <> +
+
OpenAI
+
If you use ChatGPT, this is for you.
+
+ +
+
+ +
If you have a preferred AI models vendor, please get in touch with us.
+
+
+ + ) : ( + +
+ +
-
- - ) : ( - -
- -
- -
- )} -
+ + )} +
+ ); }); diff --git a/web/pages/god-mode/authorization.tsx b/web/pages/god-mode/authorization.tsx index 5085ff61ae1..e36a1a455c3 100644 --- a/web/pages/god-mode/authorization.tsx +++ b/web/pages/god-mode/authorization.tsx @@ -14,6 +14,7 @@ import useToast from "hooks/use-toast"; import { Loader, ToggleSwitch } from "@plane/ui"; // components import { InstanceGithubConfigForm, InstanceGoogleConfigForm } from "components/instance"; +import { PageHead } from "components/core"; const InstanceAdminAuthorizationPage: NextPageWithLayout = observer(() => { // store @@ -64,69 +65,71 @@ const InstanceAdminAuthorizationPage: NextPageWithLayout = observer(() => { }; return ( -
-
-
Single sign-on and OAuth
-
- Make your teams life easy by letting them sign-up with their Google and GitHub accounts, and below are the - settings. + <> + +
+
+
Single sign-on and OAuth
+
+ Make your teams life easy by letting them sign-up with their Google and GitHub accounts, and below are the + settings. +
-
- {formattedConfig ? ( - <> -
-
-
-
- Turn Magic Links {Boolean(parseInt(enableMagicLogin)) ? "off" : "on"} + {formattedConfig ? ( + <> +
+
+
+
+ Turn Magic Links {Boolean(parseInt(enableMagicLogin)) ? "off" : "on"} +
+
+

Slack-like emails for authentication.

+ You need to have set up email{" "} + + here + {" "} + to enable this. +
-
-

Slack-like emails for authentication.

- You need to have set up email{" "} - - here - {" "} - to enable this. +
+ { + // Boolean(parseInt(enableMagicLogin)) === true + // ? updateConfig("ENABLE_MAGIC_LINK_LOGIN", "0") + // : updateConfig("ENABLE_MAGIC_LINK_LOGIN", "1"); + // }} + onChange={() => {}} + size="sm" + disabled={isSubmitting} + />
-
- { - // Boolean(parseInt(enableMagicLogin)) === true - // ? updateConfig("ENABLE_MAGIC_LINK_LOGIN", "0") - // : updateConfig("ENABLE_MAGIC_LINK_LOGIN", "1"); - // }} - onChange={() => {}} - size="sm" - disabled={isSubmitting} - /> -
-
-
-
-
- Let your users log in via the methods below +
+
+
+ Let your users log in via the methods below +
+
+ Toggling this off will disable all previous configs. Users will only be able to login with an e-mail + and password combo. +
-
- Toggling this off will disable all previous configs. Users will only be able to login with an e-mail - and password combo. +
+ { + Boolean(parseInt(enableSignup)) === true + ? updateConfig("ENABLE_SIGNUP", "0") + : updateConfig("ENABLE_SIGNUP", "1"); + }} + size="sm" + disabled={isSubmitting} + />
-
- { - Boolean(parseInt(enableSignup)) === true - ? updateConfig("ENABLE_SIGNUP", "0") - : updateConfig("ENABLE_SIGNUP", "1"); - }} - size="sm" - disabled={isSubmitting} - /> -
-
- {/*
+ {/*
Turn Email Password {Boolean(parseInt(enableEmailPassword)) ? "off" : "on"} @@ -146,36 +149,37 @@ const InstanceAdminAuthorizationPage: NextPageWithLayout = observer(() => { />
*/} -
-
-
-
- Google -
-
- -
-
-
- Github +
+
+
+ Google +
+
+ +
-
- +
+
+ Github +
+
+ +
-
- - ) : ( - -
- + + ) : ( + +
+ + +
-
- -
- )} -
+ + )} +
+ ); }); diff --git a/web/pages/god-mode/email.tsx b/web/pages/god-mode/email.tsx index bea14a357dd..65889607fa6 100644 --- a/web/pages/god-mode/email.tsx +++ b/web/pages/god-mode/email.tsx @@ -11,6 +11,7 @@ import { useApplication } from "hooks/store"; import { Loader } from "@plane/ui"; // components import { InstanceEmailForm } from "components/instance"; +import { PageHead } from "components/core"; const InstanceAdminEmailPage: NextPageWithLayout = observer(() => { // store @@ -21,29 +22,32 @@ const InstanceAdminEmailPage: NextPageWithLayout = observer(() => { useSWR("INSTANCE_CONFIGURATIONS", () => fetchInstanceConfigurations()); return ( -
-
-
Secure emails from your own instance
-
- Plane can send useful emails to you and your users from your own instance without talking to the Internet. -
-
- Set it up below and please test your settings before you save them.{" "} - Misconfigs can lead to email bounces and errors. + <> + +
+
+
Secure emails from your own instance
+
+ Plane can send useful emails to you and your users from your own instance without talking to the Internet. +
+
+ Set it up below and please test your settings before you save them.{" "} + Misconfigs can lead to email bounces and errors. +
-
- {formattedConfig ? ( - - ) : ( - -
+ {formattedConfig ? ( + + ) : ( + +
+ + +
- -
- -
- )} -
+ + )} +
+ ); }); diff --git a/web/pages/god-mode/image.tsx b/web/pages/god-mode/image.tsx index f7cf21c2f55..349dccf4bf6 100644 --- a/web/pages/god-mode/image.tsx +++ b/web/pages/god-mode/image.tsx @@ -11,6 +11,7 @@ import { useApplication } from "hooks/store"; import { Loader } from "@plane/ui"; // components import { InstanceImageConfigForm } from "components/instance"; +import { PageHead } from "components/core"; const InstanceAdminImagePage: NextPageWithLayout = observer(() => { // store @@ -21,25 +22,28 @@ const InstanceAdminImagePage: NextPageWithLayout = observer(() => { useSWR("INSTANCE_CONFIGURATIONS", () => fetchInstanceConfigurations()); return ( -
-
-
Third-party image libraries
-
- Let your users search and choose images from third-party libraries + <> + +
+
+
Third-party image libraries
+
+ Let your users search and choose images from third-party libraries +
-
- {formattedConfig ? ( - - ) : ( - -
- + {formattedConfig ? ( + + ) : ( + +
+ + +
-
- -
- )} -
+ + )} +
+ ); }); diff --git a/web/pages/god-mode/index.tsx b/web/pages/god-mode/index.tsx index 35a8ed0c4de..a93abad3105 100644 --- a/web/pages/god-mode/index.tsx +++ b/web/pages/god-mode/index.tsx @@ -11,6 +11,7 @@ import { useApplication } from "hooks/store"; import { Loader } from "@plane/ui"; // components import { InstanceGeneralForm } from "components/instance"; +import { PageHead } from "components/core"; const InstanceAdminPage: NextPageWithLayout = observer(() => { // store hooks @@ -22,26 +23,29 @@ const InstanceAdminPage: NextPageWithLayout = observer(() => { useSWR("INSTANCE_ADMINS", () => fetchInstanceAdmins()); return ( -
-
-
ID your instance easily
-
- Change the name of your instance and instance admin e-mail addresses. If you have a paid subscription, you - will find your license key here. + <> + +
+
+
ID your instance easily
+
+ Change the name of your instance and instance admin e-mail addresses. If you have a paid subscription, you + will find your license key here. +
-
- {instance && instanceAdmins ? ( - - ) : ( - -
- + {instance && instanceAdmins ? ( + + ) : ( + +
+ + +
-
- -
- )} -
+ + )} +
+ ); }); diff --git a/web/pages/invitations/index.tsx b/web/pages/invitations/index.tsx index 26ced201012..b5acec19689 100644 --- a/web/pages/invitations/index.tsx +++ b/web/pages/invitations/index.tsx @@ -32,7 +32,7 @@ import { ROLE } from "constants/workspace"; import { MEMBER_ACCEPTED } from "constants/event-tracker"; // components import { EmptyState } from "components/common"; - +import { PageHead } from "components/core"; // services const workspaceService = new WorkspaceService(); const userService = new UserService(); @@ -126,108 +126,111 @@ const UserInvitationsPage: NextPageWithLayout = observer(() => { }; return ( -
-
-
-
-
- {theme === "light" ? ( - Plane black logo - ) : ( - Plane white logo - )} + <> + +
+
+
+
+
+ {theme === "light" ? ( + Plane black logo + ) : ( + Plane white logo + )} +
+
+
+ {currentUser?.email}
-
- {currentUser?.email} -
-
- {invitations ? ( - invitations.length > 0 ? ( -
-
-
We see that someone has invited you to
-

Join a workspace

-
- {invitations.map((invitation) => { - const isSelected = invitationsRespond.includes(invitation.id); + {invitations ? ( + invitations.length > 0 ? ( +
+
+
We see that someone has invited you to
+

Join a workspace

+
+ {invitations.map((invitation) => { + const isSelected = invitationsRespond.includes(invitation.id); - return ( -
handleInvitation(invitation, isSelected ? "withdraw" : "accepted")} - > -
-
- {invitation.workspace.logo && invitation.workspace.logo.trim() !== "" ? ( - {invitation.workspace.name} - ) : ( - - {invitation.workspace.name[0]} - - )} + return ( +
handleInvitation(invitation, isSelected ? "withdraw" : "accepted")} + > +
+
+ {invitation.workspace.logo && invitation.workspace.logo.trim() !== "" ? ( + {invitation.workspace.name} + ) : ( + + {invitation.workspace.name[0]} + + )} +
+
+
{truncateText(invitation.workspace.name, 30)}
+

{ROLE[invitation.role]}

+
+ + +
-
-
{truncateText(invitation.workspace.name, 30)}
-

{ROLE[invitation.role]}

-
- - - -
- ); - })} -
-
- - - - - - + ); + })} +
+
+ + + + + + +
-
- ) : ( -
- router.push("/"), - }} - /> -
- ) - ) : null} -
+ ) : ( +
+ router.push("/"), + }} + /> +
+ ) + ) : null} +
+ ); }); diff --git a/web/pages/onboarding/index.tsx b/web/pages/onboarding/index.tsx index 99886156dcc..5b5b91280f6 100644 --- a/web/pages/onboarding/index.tsx +++ b/web/pages/onboarding/index.tsx @@ -17,6 +17,7 @@ import DefaultLayout from "layouts/default-layout"; import { UserAuthWrapper } from "layouts/auth-layout"; // components import { InviteMembers, JoinWorkspaces, UserDetails, SwitchOrDeleteAccountModal } from "components/onboarding"; +import { PageHead } from "components/core"; // ui import { Avatar, Spinner } from "@plane/ui"; // images @@ -142,6 +143,7 @@ const OnboardingPage: NextPageWithLayout = observer(() => { return ( <> + setShowDeleteAccountModal(false)} /> {user && step !== null ? (
diff --git a/web/pages/profile/activity.tsx b/web/pages/profile/activity.tsx index 051127dd354..4460f2ec506 100644 --- a/web/pages/profile/activity.tsx +++ b/web/pages/profile/activity.tsx @@ -9,7 +9,7 @@ import { UserService } from "services/user.service"; // layouts import { ProfileSettingsLayout } from "layouts/settings-layout"; // components -import { ActivityIcon, ActivityMessage, IssueLink } from "components/core"; +import { ActivityIcon, ActivityMessage, IssueLink, PageHead } from "components/core"; import { RichReadOnlyEditor } from "@plane/rich-text-editor"; // icons import { History, MessageSquare } from "lucide-react"; @@ -32,159 +32,162 @@ const ProfileActivityPage: NextPageWithLayout = observer(() => { const { theme: themeStore } = useApplication(); return ( -
-
- themeStore.toggleSidebar()} /> -

Activity

-
- {userActivity ? ( -
-
    - {userActivity.results.map((activityItem: any) => { - if (activityItem.field === "comment") { - return ( -
    -
    -
    - {activityItem.field ? ( - activityItem.new_value === "restore" && ( - - ) - ) : activityItem.actor_detail.avatar && activityItem.actor_detail.avatar !== "" ? ( - {activityItem.actor_detail.display_name} - ) : ( -
    - {activityItem.actor_detail.display_name?.charAt(0)} -
    - )} + <> + +
    +
    + themeStore.toggleSidebar()} /> +

    Activity

    +
    + {userActivity ? ( +
    +
      + {userActivity.results.map((activityItem: any) => { + if (activityItem.field === "comment") { + return ( +
      +
      +
      + {activityItem.field ? ( + activityItem.new_value === "restore" && ( + + ) + ) : activityItem.actor_detail.avatar && activityItem.actor_detail.avatar !== "" ? ( + {activityItem.actor_detail.display_name} + ) : ( +
      + {activityItem.actor_detail.display_name?.charAt(0)} +
      + )} - - -
      -
      -
      -
      - {activityItem.actor_detail.is_bot - ? activityItem.actor_detail.first_name + " Bot" - : activityItem.actor_detail.display_name} -
      -

      - Commented {calculateTimeAgo(activityItem.created_at)} -

      + +
      -
      - +
      +
      +
      + {activityItem.actor_detail.is_bot + ? activityItem.actor_detail.first_name + " Bot" + : activityItem.actor_detail.display_name} +
      +

      + Commented {calculateTimeAgo(activityItem.created_at)} +

      +
      +
      + +
      -
      - ); - } + ); + } - const message = - activityItem.verb === "created" && + const message = + activityItem.verb === "created" && activityItem.field !== "cycles" && activityItem.field !== "modules" && activityItem.field !== "attachment" && activityItem.field !== "link" && activityItem.field !== "estimate" && !activityItem.field ? ( - - created - - ) : ( - - ); + + created + + ) : ( + + ); - if ("field" in activityItem && activityItem.field !== "updated_by") { - return ( -
    • -
      -
      - <> -
      -
      -
      -
      - {activityItem.field ? ( - activityItem.new_value === "restore" ? ( - + if ("field" in activityItem && activityItem.field !== "updated_by") { + return ( +
    • +
      +
      + <> +
      +
      +
      +
      + {activityItem.field ? ( + activityItem.new_value === "restore" ? ( + + ) : ( + + ) + ) : activityItem.actor_detail.avatar && activityItem.actor_detail.avatar !== "" ? ( + {activityItem.actor_detail.display_name} ) : ( - - ) - ) : activityItem.actor_detail.avatar && activityItem.actor_detail.avatar !== "" ? ( - {activityItem.actor_detail.display_name} - ) : ( -
      - {activityItem.actor_detail.display_name?.charAt(0)} -
      - )} +
      + {activityItem.actor_detail.display_name?.charAt(0)} +
      + )} +
      -
      -
      -
      - {activityItem.field === "archived_at" && activityItem.new_value !== "restore" ? ( - Plane - ) : activityItem.actor_detail.is_bot ? ( - - {activityItem.actor_detail.first_name} Bot - - ) : ( - +
      +
      + {activityItem.field === "archived_at" && activityItem.new_value !== "restore" ? ( + Plane + ) : activityItem.actor_detail.is_bot ? ( - {currentUser?.id === activityItem.actor_detail.id - ? "You" - : activityItem.actor_detail.display_name} + {activityItem.actor_detail.first_name} Bot - - )}{" "} -
      - {message}{" "} - - {calculateTimeAgo(activityItem.created_at)} - + ) : ( + + + {currentUser?.id === activityItem.actor_detail.id + ? "You" + : activityItem.actor_detail.display_name} + + + )}{" "} +
      + {message}{" "} + + {calculateTimeAgo(activityItem.created_at)} + +
      -
      - + +
      -
      -
    • - ); - } - })} -
    -
    - ) : ( - - )} -
    + + ); + } + })} +
+
+ ) : ( + + )} +
+ ); }); diff --git a/web/pages/profile/change-password.tsx b/web/pages/profile/change-password.tsx index 15cb946ed05..80e2965d67e 100644 --- a/web/pages/profile/change-password.tsx +++ b/web/pages/profile/change-password.tsx @@ -6,6 +6,8 @@ import { Controller, useForm } from "react-hook-form"; import { useApplication, useUser } from "hooks/store"; // services import { UserService } from "services/user.service"; +// components +import { PageHead } from "components/core"; // hooks import useToast from "hooks/use-toast"; // layout @@ -88,93 +90,98 @@ const ChangePasswordPage: NextPageWithLayout = observer(() => { ); return ( -
-
- themeStore.toggleSidebar()} /> -
-
-

Change password

-
-
-

Current password

- ( - - )} - /> - {errors.old_password && {errors.old_password.message}} -
- -
-

New password

- ( - + <> + +
+
+ themeStore.toggleSidebar()} /> +
+ +

Change password

+
+
+

Current password

+ ( + + )} + /> + {errors.old_password && {errors.old_password.message}} +
+ +
+

New password

+ ( + + )} + /> + {errors.new_password && {errors.new_password.message}} +
+ +
+

Confirm password

+ ( + + )} + /> + {errors.confirm_password && ( + {errors.confirm_password.message} )} - /> - {errors.new_password && {errors.new_password.message}} +
-
-

Confirm password

- ( - - )} - /> - {errors.confirm_password && {errors.confirm_password.message}} +
+
-
- -
- -
- -
+ +
+ ); }); diff --git a/web/pages/profile/index.tsx b/web/pages/profile/index.tsx index da8626dd19a..e967df82829 100644 --- a/web/pages/profile/index.tsx +++ b/web/pages/profile/index.tsx @@ -11,7 +11,7 @@ import useToast from "hooks/use-toast"; // layouts import { ProfileSettingsLayout } from "layouts/settings-layout"; // components -import { ImagePickerPopover, UserImageUploadModal } from "components/core"; +import { ImagePickerPopover, UserImageUploadModal, PageHead } from "components/core"; import { DeactivateAccountModal } from "components/account"; // ui import { Button, CustomSelect, CustomSearchSelect, Input, Spinner } from "@plane/ui"; @@ -57,7 +57,7 @@ const ProfileSettingsPage: NextPageWithLayout = observer(() => { // store hooks const { currentUser: myProfile, updateCurrentUser, currentUserLoader } = useUser(); // custom hooks - const { } = useUserAuth({ user: myProfile, isLoading: currentUserLoader }); + const {} = useUserAuth({ user: myProfile, isLoading: currentUserLoader }); const { theme: themeStore } = useApplication(); useEffect(() => { @@ -138,6 +138,7 @@ const ProfileSettingsPage: NextPageWithLayout = observer(() => { return ( <> +
themeStore.toggleSidebar()} /> @@ -296,8 +297,9 @@ const ProfileSettingsPage: NextPageWithLayout = observer(() => { ref={ref} hasError={Boolean(errors.email)} placeholder="Enter your email" - className={`w-full rounded-md cursor-not-allowed !bg-custom-background-80 ${errors.email ? "border-red-500" : "" - }`} + className={`w-full rounded-md cursor-not-allowed !bg-custom-background-80 ${ + errors.email ? "border-red-500" : "" + }`} disabled /> )} @@ -385,7 +387,9 @@ const ProfileSettingsPage: NextPageWithLayout = observer(() => { render={({ field: { value, onChange } }) => ( t.value === value)?.label ?? value : "Select a timezone"} + label={ + value ? TIME_ZONES.find((t) => t.value === value)?.label ?? value : "Select a timezone" + } options={timeZoneOptions} onChange={onChange} optionsClassName="w-full" @@ -409,7 +413,11 @@ const ProfileSettingsPage: NextPageWithLayout = observer(() => { {({ open }) => ( <> - + Deactivate account @@ -426,8 +434,8 @@ const ProfileSettingsPage: NextPageWithLayout = observer(() => {
The danger zone of the profile page is a critical area that requires careful consideration and - attention. When deactivating an account, all of the data and resources within that account will be - permanently removed and cannot be recovered. + attention. When deactivating an account, all of the data and resources within that account + will be permanently removed and cannot be recovered.
- {storedValue && Object.keys(JSON.parse(storedValue)).length > 0 && ( + {!disabled && storedValue && Object.keys(JSON.parse(storedValue)).length > 0 && ( <>
Date: Tue, 20 Feb 2024 13:41:38 +0530 Subject: [PATCH 006/127] fix: kanban layout empty group toggle fix (#3708) --- .../issue-layouts/kanban/base-kanban-root.tsx | 2 +- .../issues/issue-layouts/kanban/default.tsx | 14 +++++++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/web/components/issues/issue-layouts/kanban/base-kanban-root.tsx b/web/components/issues/issue-layouts/kanban/base-kanban-root.tsx index 83f72d8ea05..8bf31a28f88 100644 --- a/web/components/issues/issue-layouts/kanban/base-kanban-root.tsx +++ b/web/components/issues/issue-layouts/kanban/base-kanban-root.tsx @@ -288,7 +288,7 @@ export const BaseKanBanRoot: React.FC = observer((props: IBas handleKanbanFilters={handleKanbanFilters} kanbanFilters={kanbanFilters} enableQuickIssueCreate={enableQuickAdd} - showEmptyGroup={userDisplayFilters?.show_empty_groups || true} + showEmptyGroup={userDisplayFilters?.show_empty_groups ?? true} quickAddCallback={issues?.quickAddIssue} viewId={viewId} disableIssueCreation={!enableIssueCreation || !isEditingAllowed || isCompletedCycle} diff --git a/web/components/issues/issue-layouts/kanban/default.tsx b/web/components/issues/issue-layouts/kanban/default.tsx index f11321944e2..b645afe30cb 100644 --- a/web/components/issues/issue-layouts/kanban/default.tsx +++ b/web/components/issues/issue-layouts/kanban/default.tsx @@ -48,6 +48,7 @@ export interface IGroupByKanBan { canEditProperties: (projectId: string | undefined) => boolean; scrollableContainerRef?: MutableRefObject; isDragStarted?: boolean; + showEmptyGroup?: boolean; } const GroupByKanBan: React.FC = observer((props) => { @@ -72,6 +73,7 @@ const GroupByKanBan: React.FC = observer((props) => { canEditProperties, scrollableContainerRef, isDragStarted, + showEmptyGroup = true, } = props; const member = useMember(); @@ -84,6 +86,10 @@ const GroupByKanBan: React.FC = observer((props) => { if (!list) return null; + const groupWithIssues = list.filter((_list) => (issueIds as TGroupedIssues)[_list.id]?.length > 0); + + const groupList = showEmptyGroup ? list : groupWithIssues; + const visibilityGroupBy = (_list: IGroupByColumn) => sub_group_by ? false : kanbanFilters?.group_by.includes(_list.id) ? true : false; @@ -91,9 +97,9 @@ const GroupByKanBan: React.FC = observer((props) => { return (
- {list && - list.length > 0 && - list.map((_list: IGroupByColumn) => { + {groupList && + groupList.length > 0 && + groupList.map((_list: IGroupByColumn) => { const groupByVisibilityToggle = visibilityGroupBy(_list); return ( @@ -196,6 +202,7 @@ export const KanBan: React.FC = observer((props) => { canEditProperties, scrollableContainerRef, isDragStarted, + showEmptyGroup, } = props; const issueKanBanView = useKanbanView(); @@ -222,6 +229,7 @@ export const KanBan: React.FC = observer((props) => { canEditProperties={canEditProperties} scrollableContainerRef={scrollableContainerRef} isDragStarted={isDragStarted} + showEmptyGroup={showEmptyGroup} /> ); }); From d16a0b61d00aeefa0cbc7efdf89a3a91936002e6 Mon Sep 17 00:00:00 2001 From: Anmol Singh Bhatia <121005188+anmolsinghbhatia@users.noreply.github.com> Date: Tue, 20 Feb 2024 13:42:04 +0530 Subject: [PATCH 007/127] chore: dashboard widget heading updated (#3709) --- web/components/dashboard/widgets/recent-projects.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/components/dashboard/widgets/recent-projects.tsx b/web/components/dashboard/widgets/recent-projects.tsx index eb7a5e4e552..79be923334b 100644 --- a/web/components/dashboard/widgets/recent-projects.tsx +++ b/web/components/dashboard/widgets/recent-projects.tsx @@ -96,7 +96,7 @@ export const RecentProjectsWidget: React.FC = observer((props) => { href={`/${workspaceSlug}/projects`} className="text-lg font-semibold text-custom-text-300 mx-7 hover:underline" > - Your projects + Recent projects
{canCreateProject && ( From 6bf9d84bea17b97921ba8b3309f6ee8eff4f0ae4 Mon Sep 17 00:00:00 2001 From: Anmol Singh Bhatia <121005188+anmolsinghbhatia@users.noreply.github.com> Date: Tue, 20 Feb 2024 15:03:58 +0530 Subject: [PATCH 008/127] chore : calendar layout improvement (#3705) * chore: current date indicator added in calendar layout * style: calendar layout ui improvement --- .../issues/issue-layouts/calendar/calendar.tsx | 2 +- .../issues/issue-layouts/calendar/day-tile.tsx | 15 ++++++++++++--- .../issues/issue-layouts/calendar/week-days.tsx | 2 +- .../issues/issue-layouts/calendar/week-header.tsx | 4 ++-- 4 files changed, 16 insertions(+), 7 deletions(-) diff --git a/web/components/issues/issue-layouts/calendar/calendar.tsx b/web/components/issues/issue-layouts/calendar/calendar.tsx index c48f23068b6..733b3a0974d 100644 --- a/web/components/issues/issue-layouts/calendar/calendar.tsx +++ b/web/components/issues/issue-layouts/calendar/calendar.tsx @@ -76,7 +76,7 @@ export const CalendarChart: React.FC = observer((props) => {
{layout === "month" && ( -
+
{allWeeksOfActiveMonth && Object.values(allWeeksOfActiveMonth).map((week: ICalendarWeek, weekIndex) => ( = observer((props) => { const issueIdList = groupedIssueIds ? groupedIssueIds[formattedDatePayload] : null; const totalIssues = issueIdList?.length ?? 0; + + const isToday = date.date.toDateString() === new Date().toDateString(); + return ( <>
{/* header */}
= observer((props) => { date.date.getDay() === 0 || date.date.getDay() === 6 ? "bg-custom-background-90" : "bg-custom-background-100" - }`} + } `} > {date.date.getDate() === 1 && MONTHS_LIST[date.date.getMonth() + 1].shortTitle + " "} - {date.date.getDate()} + {isToday ? ( + + {date.date.getDate()} + + ) : ( + <>{date.date.getDate()} + )}
{/* content */} diff --git a/web/components/issues/issue-layouts/calendar/week-days.tsx b/web/components/issues/issue-layouts/calendar/week-days.tsx index 5a640a56695..f65b6949163 100644 --- a/web/components/issues/issue-layouts/calendar/week-days.tsx +++ b/web/components/issues/issue-layouts/calendar/week-days.tsx @@ -50,7 +50,7 @@ export const CalendarWeekDays: React.FC = observer((props) => { return (
diff --git a/web/components/issues/issue-layouts/calendar/week-header.tsx b/web/components/issues/issue-layouts/calendar/week-header.tsx index 5e7b0fcb74e..ca6b0556892 100644 --- a/web/components/issues/issue-layouts/calendar/week-header.tsx +++ b/web/components/issues/issue-layouts/calendar/week-header.tsx @@ -13,7 +13,7 @@ export const CalendarWeekHeader: React.FC = observer((props) => { return (
@@ -24,7 +24,7 @@ export const CalendarWeekHeader: React.FC = observer((props) => { if (!showWeekends && (day.shortTitle === "Sat" || day.shortTitle === "Sun")) return null; return ( -
+
{day.shortTitle}
); From 952eb871df73a999a9055c4c577bb0261261c889 Mon Sep 17 00:00:00 2001 From: Anmol Singh Bhatia <121005188+anmolsinghbhatia@users.noreply.github.com> Date: Tue, 20 Feb 2024 20:11:59 +0530 Subject: [PATCH 009/127] [WEB-462] fix: inbox mutation (#3720) * fix: inbox issue status mutation * fix: sidebar inbox issue count fix * fix: inbox issue mutation fix --- web/components/project/sidebar-list-item.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/components/project/sidebar-list-item.tsx b/web/components/project/sidebar-list-item.tsx index b8e4b9edab2..f899a9b31b1 100644 --- a/web/components/project/sidebar-list-item.tsx +++ b/web/components/project/sidebar-list-item.tsx @@ -105,7 +105,7 @@ export const ProjectSidebarListItem: React.FC = observer((props) => { const actionSectionRef = useRef(null); - const inboxesMap = currentProjectDetails?.inbox_view ? getInboxesByProjectId(currentProjectDetails.id) : undefined; + const inboxesMap = project?.inbox_view ? getInboxesByProjectId(projectId) : undefined; const inboxDetails = inboxesMap && inboxesMap.length > 0 ? getInboxById(inboxesMap[0]) : undefined; const handleAddToFavorites = () => { From 95871b0049f77572239ea9a17967941a1f71f28a Mon Sep 17 00:00:00 2001 From: Lakhan Baheti <94619783+1akhanBaheti@users.noreply.github.com> Date: Tue, 20 Feb 2024 20:16:45 +0530 Subject: [PATCH 010/127] [WEB-472] fix: Page title for global view (#3723) * fix: Page title for global view * fix: global-view-id type --- .../workspace-views/[globalViewId].tsx | 25 ++++++++++++++++--- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/web/pages/[workspaceSlug]/workspace-views/[globalViewId].tsx b/web/pages/[workspaceSlug]/workspace-views/[globalViewId].tsx index 43a0ad49439..85e90748115 100644 --- a/web/pages/[workspaceSlug]/workspace-views/[globalViewId].tsx +++ b/web/pages/[workspaceSlug]/workspace-views/[globalViewId].tsx @@ -1,20 +1,37 @@ import { ReactElement } from "react"; +import { useRouter } from "next/router"; +import { observer } from "mobx-react"; // layouts import { AppLayout } from "layouts/app-layout"; +// hooks +import { useGlobalView, useWorkspace } from "hooks/store"; // components import { GlobalViewsHeader } from "components/workspace"; import { AllIssueLayoutRoot } from "components/issues"; import { GlobalIssuesHeader } from "components/headers"; +import { PageHead } from "components/core"; // types import { NextPageWithLayout } from "lib/types"; -import { observer } from "mobx-react"; -import { useWorkspace } from "hooks/store"; -import { PageHead } from "components/core"; +// constants +import { DEFAULT_GLOBAL_VIEWS_LIST } from "constants/workspace"; const GlobalViewIssuesPage: NextPageWithLayout = observer(() => { + // router + const router = useRouter(); + const { globalViewId } = router.query; + // store hooks const { currentWorkspace } = useWorkspace(); + const { getViewDetailsById } = useGlobalView(); // derived values - const pageTitle = currentWorkspace?.name ? `${currentWorkspace?.name} - Views` : undefined; + const globalViewDetails = globalViewId ? getViewDetailsById(globalViewId.toString()) : undefined; + const defaultView = DEFAULT_GLOBAL_VIEWS_LIST.find((view) => view.key === globalViewId); + const pageTitle = + currentWorkspace?.name && defaultView?.label + ? `${currentWorkspace?.name} - ${defaultView?.label}` + : currentWorkspace?.name && globalViewDetails?.name + ? `${currentWorkspace?.name} - ${globalViewDetails?.name}` + : undefined; + return ( <> From 3efb7fc0701e7dd0b2e2885fd063f59ca38328c3 Mon Sep 17 00:00:00 2001 From: Aaryan Khandelwal <65252264+aaryan610@users.noreply.github.com> Date: Wed, 21 Feb 2024 16:40:52 +0530 Subject: [PATCH 011/127] fix: layout change not working in cycles and modules (#3729) --- .../issues/issue-layouts/calendar/calendar.tsx | 2 +- .../calendar/dropdowns/options-dropdown.tsx | 16 +++++++++++----- .../issues/issue-layouts/calendar/week-days.tsx | 2 +- .../issue-layouts/calendar/week-header.tsx | 2 +- web/store/issue/issue_calendar_view.store.ts | 2 +- 5 files changed, 15 insertions(+), 9 deletions(-) diff --git a/web/components/issues/issue-layouts/calendar/calendar.tsx b/web/components/issues/issue-layouts/calendar/calendar.tsx index 733b3a0974d..c48f23068b6 100644 --- a/web/components/issues/issue-layouts/calendar/calendar.tsx +++ b/web/components/issues/issue-layouts/calendar/calendar.tsx @@ -76,7 +76,7 @@ export const CalendarChart: React.FC = observer((props) => {
{layout === "month" && ( -
+
{allWeeksOfActiveMonth && Object.values(allWeeksOfActiveMonth).map((week: ICalendarWeek, weekIndex) => ( = observer((prop const handleLayoutChange = (layout: TCalendarLayouts) => { if (!workspaceSlug || !projectId) return; - issuesFilterStore.updateFilters(workspaceSlug.toString(), projectId.toString(), EIssueFilterType.DISPLAY_FILTERS, { - calendar: { - ...issuesFilterStore.issueFilters?.displayFilters?.calendar, - layout, + issuesFilterStore.updateFilters( + workspaceSlug.toString(), + projectId.toString(), + EIssueFilterType.DISPLAY_FILTERS, + { + calendar: { + ...issuesFilterStore.issueFilters?.displayFilters?.calendar, + layout, + }, }, - }); + viewId + ); issueCalendarView.updateCalendarPayload( layout === "month" diff --git a/web/components/issues/issue-layouts/calendar/week-days.tsx b/web/components/issues/issue-layouts/calendar/week-days.tsx index f65b6949163..5a640a56695 100644 --- a/web/components/issues/issue-layouts/calendar/week-days.tsx +++ b/web/components/issues/issue-layouts/calendar/week-days.tsx @@ -50,7 +50,7 @@ export const CalendarWeekDays: React.FC = observer((props) => { return (
diff --git a/web/components/issues/issue-layouts/calendar/week-header.tsx b/web/components/issues/issue-layouts/calendar/week-header.tsx index ca6b0556892..f5ec41e96c3 100644 --- a/web/components/issues/issue-layouts/calendar/week-header.tsx +++ b/web/components/issues/issue-layouts/calendar/week-header.tsx @@ -13,7 +13,7 @@ export const CalendarWeekHeader: React.FC = observer((props) => { return (
diff --git a/web/store/issue/issue_calendar_view.store.ts b/web/store/issue/issue_calendar_view.store.ts index 61c2216a297..ac4a608098f 100644 --- a/web/store/issue/issue_calendar_view.store.ts +++ b/web/store/issue/issue_calendar_view.store.ts @@ -78,7 +78,7 @@ export class CalendarStore implements ICalendarStore { const { activeWeekDate } = this.calendarFilters; return this.calendarPayload[`y-${activeWeekDate.getFullYear()}`][`m-${activeWeekDate.getMonth()}`][ - `w-${this.activeWeekNumber}` + `w-${this.activeWeekNumber - 1}` ]; } From fb4f4260fa27c650845d97b89bddd7a366f26fdd Mon Sep 17 00:00:00 2001 From: guru_sainath Date: Wed, 21 Feb 2024 16:42:04 +0530 Subject: [PATCH 012/127] [WEB-479]: rendering issue title without overflow problem in issue peekoverview (#3728) * chore: rendering issue title without overflow problem * chore: optimised calssName * fix: className update --- web/components/issues/title-input.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/web/components/issues/title-input.tsx b/web/components/issues/title-input.tsx index 55dd80b87bb..dbd18aaaa6f 100644 --- a/web/components/issues/title-input.tsx +++ b/web/components/issues/title-input.tsx @@ -48,6 +48,8 @@ export const IssueTitleInput: FC = observer((props) => { [setIsSubmitting] ); + if (disabled) return
{title}
; + return (