forked from github/plane
Merge branch 'develop' of github.com:makeplane/plane into develop
This commit is contained in:
commit
a6a28d46c7
@ -4,3 +4,4 @@ export * from "./sidebar";
|
|||||||
export * from "./theme";
|
export * from "./theme";
|
||||||
export * from "./activity";
|
export * from "./activity";
|
||||||
export * from "./image-picker-popover";
|
export * from "./image-picker-popover";
|
||||||
|
export * from "./page-title";
|
||||||
|
18
web/components/core/page-title.tsx
Normal file
18
web/components/core/page-title.tsx
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import Head from "next/head";
|
||||||
|
|
||||||
|
type PageHeadTitleProps = {
|
||||||
|
title?: string;
|
||||||
|
description?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const PageHead: React.FC<PageHeadTitleProps> = (props) => {
|
||||||
|
const { title } = props;
|
||||||
|
|
||||||
|
if (!title) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Head>
|
||||||
|
<title>{title}</title>
|
||||||
|
</Head>
|
||||||
|
);
|
||||||
|
};
|
@ -96,7 +96,7 @@ export const RecentProjectsWidget: React.FC<WidgetProps> = observer((props) => {
|
|||||||
href={`/${workspaceSlug}/projects`}
|
href={`/${workspaceSlug}/projects`}
|
||||||
className="text-lg font-semibold text-custom-text-300 mx-7 hover:underline"
|
className="text-lg font-semibold text-custom-text-300 mx-7 hover:underline"
|
||||||
>
|
>
|
||||||
Your projects
|
Recent projects
|
||||||
</Link>
|
</Link>
|
||||||
<div className="space-y-8 mt-4 mx-7">
|
<div className="space-y-8 mt-4 mx-7">
|
||||||
{canCreateProject && (
|
{canCreateProject && (
|
||||||
|
@ -288,7 +288,7 @@ export const BaseKanBanRoot: React.FC<IBaseKanBanLayout> = observer((props: IBas
|
|||||||
handleKanbanFilters={handleKanbanFilters}
|
handleKanbanFilters={handleKanbanFilters}
|
||||||
kanbanFilters={kanbanFilters}
|
kanbanFilters={kanbanFilters}
|
||||||
enableQuickIssueCreate={enableQuickAdd}
|
enableQuickIssueCreate={enableQuickAdd}
|
||||||
showEmptyGroup={userDisplayFilters?.show_empty_groups || true}
|
showEmptyGroup={userDisplayFilters?.show_empty_groups ?? true}
|
||||||
quickAddCallback={issues?.quickAddIssue}
|
quickAddCallback={issues?.quickAddIssue}
|
||||||
viewId={viewId}
|
viewId={viewId}
|
||||||
disableIssueCreation={!enableIssueCreation || !isEditingAllowed || isCompletedCycle}
|
disableIssueCreation={!enableIssueCreation || !isEditingAllowed || isCompletedCycle}
|
||||||
|
@ -48,6 +48,7 @@ export interface IGroupByKanBan {
|
|||||||
canEditProperties: (projectId: string | undefined) => boolean;
|
canEditProperties: (projectId: string | undefined) => boolean;
|
||||||
scrollableContainerRef?: MutableRefObject<HTMLDivElement | null>;
|
scrollableContainerRef?: MutableRefObject<HTMLDivElement | null>;
|
||||||
isDragStarted?: boolean;
|
isDragStarted?: boolean;
|
||||||
|
showEmptyGroup?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const GroupByKanBan: React.FC<IGroupByKanBan> = observer((props) => {
|
const GroupByKanBan: React.FC<IGroupByKanBan> = observer((props) => {
|
||||||
@ -72,6 +73,7 @@ const GroupByKanBan: React.FC<IGroupByKanBan> = observer((props) => {
|
|||||||
canEditProperties,
|
canEditProperties,
|
||||||
scrollableContainerRef,
|
scrollableContainerRef,
|
||||||
isDragStarted,
|
isDragStarted,
|
||||||
|
showEmptyGroup = true,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const member = useMember();
|
const member = useMember();
|
||||||
@ -84,6 +86,10 @@ const GroupByKanBan: React.FC<IGroupByKanBan> = observer((props) => {
|
|||||||
|
|
||||||
if (!list) return null;
|
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) =>
|
const visibilityGroupBy = (_list: IGroupByColumn) =>
|
||||||
sub_group_by ? false : kanbanFilters?.group_by.includes(_list.id) ? true : false;
|
sub_group_by ? false : kanbanFilters?.group_by.includes(_list.id) ? true : false;
|
||||||
|
|
||||||
@ -91,9 +97,9 @@ const GroupByKanBan: React.FC<IGroupByKanBan> = observer((props) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`relative w-full flex gap-3 ${sub_group_by ? "h-full" : "h-full"}`}>
|
<div className={`relative w-full flex gap-3 ${sub_group_by ? "h-full" : "h-full"}`}>
|
||||||
{list &&
|
{groupList &&
|
||||||
list.length > 0 &&
|
groupList.length > 0 &&
|
||||||
list.map((_list: IGroupByColumn) => {
|
groupList.map((_list: IGroupByColumn) => {
|
||||||
const groupByVisibilityToggle = visibilityGroupBy(_list);
|
const groupByVisibilityToggle = visibilityGroupBy(_list);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -196,6 +202,7 @@ export const KanBan: React.FC<IKanBan> = observer((props) => {
|
|||||||
canEditProperties,
|
canEditProperties,
|
||||||
scrollableContainerRef,
|
scrollableContainerRef,
|
||||||
isDragStarted,
|
isDragStarted,
|
||||||
|
showEmptyGroup,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const issueKanBanView = useKanbanView();
|
const issueKanBanView = useKanbanView();
|
||||||
@ -222,6 +229,7 @@ export const KanBan: React.FC<IKanBan> = observer((props) => {
|
|||||||
canEditProperties={canEditProperties}
|
canEditProperties={canEditProperties}
|
||||||
scrollableContainerRef={scrollableContainerRef}
|
scrollableContainerRef={scrollableContainerRef}
|
||||||
isDragStarted={isDragStarted}
|
isDragStarted={isDragStarted}
|
||||||
|
showEmptyGroup={showEmptyGroup}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -6,6 +6,7 @@ import { useApplication, useUser } from "hooks/store";
|
|||||||
import useSignInRedirection from "hooks/use-sign-in-redirection";
|
import useSignInRedirection from "hooks/use-sign-in-redirection";
|
||||||
// components
|
// components
|
||||||
import { SignInRoot } from "components/account";
|
import { SignInRoot } from "components/account";
|
||||||
|
import { PageHead } from "components/core";
|
||||||
// ui
|
// ui
|
||||||
import { Spinner } from "@plane/ui";
|
import { Spinner } from "@plane/ui";
|
||||||
// images
|
// images
|
||||||
@ -34,6 +35,8 @@ export const SignInView = observer(() => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
|
<PageHead title="Sign In" />
|
||||||
<div className="h-full w-full bg-onboarding-gradient-100">
|
<div className="h-full w-full bg-onboarding-gradient-100">
|
||||||
<div className="flex items-center justify-between px-8 pb-4 sm:px-16 sm:py-5 lg:px-28">
|
<div className="flex items-center justify-between px-8 pb-4 sm:px-16 sm:py-5 lg:px-28">
|
||||||
<div className="flex items-center gap-x-2 py-10">
|
<div className="flex items-center gap-x-2 py-10">
|
||||||
@ -48,5 +51,6 @@ export const SignInView = observer(() => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -2,7 +2,7 @@ import { useRef, useState } from "react";
|
|||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { ChevronUp, PenSquare, Search } from "lucide-react";
|
import { ChevronUp, PenSquare, Search } from "lucide-react";
|
||||||
// hooks
|
// hooks
|
||||||
import { useApplication, useEventTracker, useUser } from "hooks/store";
|
import { useApplication, useEventTracker, useProject, useUser } from "hooks/store";
|
||||||
import useLocalStorage from "hooks/use-local-storage";
|
import useLocalStorage from "hooks/use-local-storage";
|
||||||
// components
|
// components
|
||||||
import { CreateUpdateDraftIssueModal } from "components/issues";
|
import { CreateUpdateDraftIssueModal } from "components/issues";
|
||||||
@ -16,6 +16,7 @@ export const WorkspaceSidebarQuickAction = observer(() => {
|
|||||||
|
|
||||||
const { theme: themeStore, commandPalette: commandPaletteStore } = useApplication();
|
const { theme: themeStore, commandPalette: commandPaletteStore } = useApplication();
|
||||||
const { setTrackElement } = useEventTracker();
|
const { setTrackElement } = useEventTracker();
|
||||||
|
const { joinedProjectIds } = useProject();
|
||||||
const {
|
const {
|
||||||
membership: { currentWorkspaceRole },
|
membership: { currentWorkspaceRole },
|
||||||
} = useUser();
|
} = useUser();
|
||||||
@ -31,6 +32,8 @@ export const WorkspaceSidebarQuickAction = observer(() => {
|
|||||||
|
|
||||||
const isAuthorizedUser = !!currentWorkspaceRole && currentWorkspaceRole >= EUserWorkspaceRoles.MEMBER;
|
const isAuthorizedUser = !!currentWorkspaceRole && currentWorkspaceRole >= EUserWorkspaceRoles.MEMBER;
|
||||||
|
|
||||||
|
const disabled = joinedProjectIds.length === 0;
|
||||||
|
|
||||||
const onMouseEnter = () => {
|
const onMouseEnter = () => {
|
||||||
//if renet before timout clear the timeout
|
//if renet before timout clear the timeout
|
||||||
timeoutRef?.current && clearTimeout(timeoutRef.current);
|
timeoutRef?.current && clearTimeout(timeoutRef.current);
|
||||||
@ -73,17 +76,18 @@ export const WorkspaceSidebarQuickAction = observer(() => {
|
|||||||
type="button"
|
type="button"
|
||||||
className={`relative flex flex-shrink-0 flex-grow items-center gap-2 rounded py-1.5 outline-none ${
|
className={`relative flex flex-shrink-0 flex-grow items-center gap-2 rounded py-1.5 outline-none ${
|
||||||
isSidebarCollapsed ? "justify-center" : ""
|
isSidebarCollapsed ? "justify-center" : ""
|
||||||
}`}
|
} ${disabled ? "cursor-not-allowed opacity-50" : ""}`}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setTrackElement("APP_SIDEBAR_QUICK_ACTIONS");
|
setTrackElement("APP_SIDEBAR_QUICK_ACTIONS");
|
||||||
commandPaletteStore.toggleCreateIssueModal(true, EIssuesStoreType.PROJECT);
|
commandPaletteStore.toggleCreateIssueModal(true, EIssuesStoreType.PROJECT);
|
||||||
}}
|
}}
|
||||||
|
disabled={disabled}
|
||||||
>
|
>
|
||||||
<PenSquare className="h-4 w-4 text-custom-sidebar-text-300" />
|
<PenSquare className="h-4 w-4 text-custom-sidebar-text-300" />
|
||||||
{!isSidebarCollapsed && <span className="text-sm font-medium">New Issue</span>}
|
{!isSidebarCollapsed && <span className="text-sm font-medium">New Issue</span>}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
{storedValue && Object.keys(JSON.parse(storedValue)).length > 0 && (
|
{!disabled && storedValue && Object.keys(JSON.parse(storedValue)).length > 0 && (
|
||||||
<>
|
<>
|
||||||
<div
|
<div
|
||||||
className={`h-8 w-0.5 bg-custom-sidebar-background-80 ${isSidebarCollapsed ? "hidden" : "block"}`}
|
className={`h-8 w-0.5 bg-custom-sidebar-background-80 ${isSidebarCollapsed ? "hidden" : "block"}`}
|
||||||
|
@ -2,7 +2,8 @@ import React from "react";
|
|||||||
|
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
|
// components
|
||||||
|
import { PageHead } from "components/core";
|
||||||
// layouts
|
// layouts
|
||||||
import DefaultLayout from "layouts/default-layout";
|
import DefaultLayout from "layouts/default-layout";
|
||||||
// ui
|
// ui
|
||||||
@ -14,6 +15,7 @@ import type { NextPage } from "next";
|
|||||||
|
|
||||||
const PageNotFound: NextPage = () => (
|
const PageNotFound: NextPage = () => (
|
||||||
<DefaultLayout>
|
<DefaultLayout>
|
||||||
|
<PageHead title="404 - Page Not Found" />
|
||||||
<div className="grid h-full place-items-center p-4">
|
<div className="grid h-full place-items-center p-4">
|
||||||
<div className="space-y-8 text-center">
|
<div className="space-y-8 text-center">
|
||||||
<div className="relative mx-auto h-60 w-60 lg:h-80 lg:w-80">
|
<div className="relative mx-auto h-60 w-60 lg:h-80 lg:w-80">
|
||||||
|
@ -1,13 +1,28 @@
|
|||||||
import { ReactElement } from "react";
|
import { ReactElement } from "react";
|
||||||
|
import { observer } from "mobx-react";
|
||||||
// components
|
// components
|
||||||
|
import { PageHead } from "components/core";
|
||||||
import { WorkspaceActiveCycleHeader } from "components/headers";
|
import { WorkspaceActiveCycleHeader } from "components/headers";
|
||||||
import { WorkspaceActiveCyclesUpgrade } from "components/workspace";
|
import { WorkspaceActiveCyclesUpgrade } from "components/workspace";
|
||||||
// layouts
|
// layouts
|
||||||
import { AppLayout } from "layouts/app-layout";
|
import { AppLayout } from "layouts/app-layout";
|
||||||
// types
|
// types
|
||||||
import { NextPageWithLayout } from "lib/types";
|
import { NextPageWithLayout } from "lib/types";
|
||||||
|
// hooks
|
||||||
|
import { useWorkspace } from "hooks/store";
|
||||||
|
|
||||||
const WorkspaceActiveCyclesPage: NextPageWithLayout = () => <WorkspaceActiveCyclesUpgrade />;
|
const WorkspaceActiveCyclesPage: NextPageWithLayout = observer(() => {
|
||||||
|
const { currentWorkspace } = useWorkspace();
|
||||||
|
// derived values
|
||||||
|
const pageTitle = currentWorkspace?.name ? `${currentWorkspace?.name} - Active Cycles` : undefined;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<PageHead title={pageTitle} />
|
||||||
|
<WorkspaceActiveCyclesUpgrade />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
WorkspaceActiveCyclesPage.getLayout = function getLayout(page: ReactElement) {
|
WorkspaceActiveCyclesPage.getLayout = function getLayout(page: ReactElement) {
|
||||||
return <AppLayout header={<WorkspaceActiveCycleHeader />}>{page}</AppLayout>;
|
return <AppLayout header={<WorkspaceActiveCycleHeader />}>{page}</AppLayout>;
|
||||||
|
@ -1,22 +1,23 @@
|
|||||||
import React, { Fragment, ReactElement } from "react";
|
import React, { Fragment, ReactElement } from "react";
|
||||||
|
import { useRouter } from "next/router";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { Tab } from "@headlessui/react";
|
import { Tab } from "@headlessui/react";
|
||||||
import { useTheme } from "next-themes";
|
import { useTheme } from "next-themes";
|
||||||
// hooks
|
// hooks
|
||||||
import { useApplication, useEventTracker, useProject, useUser } from "hooks/store";
|
import { useApplication, useEventTracker, useProject, useUser, useWorkspace } from "hooks/store";
|
||||||
// layouts
|
// layouts
|
||||||
import { AppLayout } from "layouts/app-layout";
|
import { AppLayout } from "layouts/app-layout";
|
||||||
// components
|
// components
|
||||||
|
import { PageHead } from "components/core";
|
||||||
import { CustomAnalytics, ScopeAndDemand } from "components/analytics";
|
import { CustomAnalytics, ScopeAndDemand } from "components/analytics";
|
||||||
import { WorkspaceAnalyticsHeader } from "components/headers";
|
import { WorkspaceAnalyticsHeader } from "components/headers";
|
||||||
import { EmptyState, getEmptyStateImagePath } from "components/empty-state";
|
import { EmptyState, getEmptyStateImagePath } from "components/empty-state";
|
||||||
// constants
|
// constants
|
||||||
import { ANALYTICS_TABS } from "constants/analytics";
|
import { ANALYTICS_TABS } from "constants/analytics";
|
||||||
import { EUserWorkspaceRoles } from "constants/workspace";
|
import { EUserWorkspaceRoles } from "constants/workspace";
|
||||||
|
import { WORKSPACE_EMPTY_STATE_DETAILS } from "constants/empty-state";
|
||||||
// type
|
// type
|
||||||
import { NextPageWithLayout } from "lib/types";
|
import { NextPageWithLayout } from "lib/types";
|
||||||
import { useRouter } from "next/router";
|
|
||||||
import { WORKSPACE_EMPTY_STATE_DETAILS } from "constants/empty-state";
|
|
||||||
|
|
||||||
const AnalyticsPage: NextPageWithLayout = observer(() => {
|
const AnalyticsPage: NextPageWithLayout = observer(() => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
@ -33,13 +34,16 @@ const AnalyticsPage: NextPageWithLayout = observer(() => {
|
|||||||
currentUser,
|
currentUser,
|
||||||
} = useUser();
|
} = useUser();
|
||||||
const { workspaceProjectIds } = useProject();
|
const { workspaceProjectIds } = useProject();
|
||||||
|
const { currentWorkspace } = useWorkspace();
|
||||||
|
// derived values
|
||||||
const isLightMode = resolvedTheme ? resolvedTheme === "light" : currentUser?.theme.theme === "light";
|
const isLightMode = resolvedTheme ? resolvedTheme === "light" : currentUser?.theme.theme === "light";
|
||||||
const EmptyStateImagePath = getEmptyStateImagePath("onboarding", "analytics", isLightMode);
|
const EmptyStateImagePath = getEmptyStateImagePath("onboarding", "analytics", isLightMode);
|
||||||
const isEditingAllowed = !!currentWorkspaceRole && currentWorkspaceRole >= EUserWorkspaceRoles.MEMBER;
|
const isEditingAllowed = !!currentWorkspaceRole && currentWorkspaceRole >= EUserWorkspaceRoles.MEMBER;
|
||||||
|
const pageTitle = currentWorkspace?.name ? `${currentWorkspace?.name} - Analytics` : undefined;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
<PageHead title={pageTitle} />
|
||||||
{workspaceProjectIds && workspaceProjectIds.length > 0 ? (
|
{workspaceProjectIds && workspaceProjectIds.length > 0 ? (
|
||||||
<div className="flex h-full flex-col overflow-hidden bg-custom-background-100">
|
<div className="flex h-full flex-col overflow-hidden bg-custom-background-100">
|
||||||
<Tab.Group as={Fragment} defaultIndex={analytics_tab === "custom" ? 1 : 0}>
|
<Tab.Group as={Fragment} defaultIndex={analytics_tab === "custom" ? 1 : 0}>
|
||||||
|
@ -1,13 +1,28 @@
|
|||||||
import { ReactElement } from "react";
|
import { ReactElement } from "react";
|
||||||
|
import { observer } from "mobx-react";
|
||||||
// layouts
|
// layouts
|
||||||
import { AppLayout } from "layouts/app-layout";
|
import { AppLayout } from "layouts/app-layout";
|
||||||
// components
|
// components
|
||||||
|
import { PageHead } from "components/core";
|
||||||
import { WorkspaceDashboardView } from "components/page-views";
|
import { WorkspaceDashboardView } from "components/page-views";
|
||||||
import { WorkspaceDashboardHeader } from "components/headers/workspace-dashboard";
|
import { WorkspaceDashboardHeader } from "components/headers/workspace-dashboard";
|
||||||
// types
|
// types
|
||||||
import { NextPageWithLayout } from "lib/types";
|
import { NextPageWithLayout } from "lib/types";
|
||||||
|
// hooks
|
||||||
|
import { useWorkspace } from "hooks/store";
|
||||||
|
|
||||||
const WorkspacePage: NextPageWithLayout = () => <WorkspaceDashboardView />;
|
const WorkspacePage: NextPageWithLayout = observer(() => {
|
||||||
|
const { currentWorkspace } = useWorkspace();
|
||||||
|
// derived values
|
||||||
|
const pageTitle = currentWorkspace?.name ? `${currentWorkspace?.name} - Dashboard` : undefined;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<PageHead title={pageTitle} />
|
||||||
|
<WorkspaceDashboardView />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
WorkspacePage.getLayout = function getLayout(page: ReactElement) {
|
WorkspacePage.getLayout = function getLayout(page: ReactElement) {
|
||||||
return <AppLayout header={<WorkspaceDashboardHeader />}>{page}</AppLayout>;
|
return <AppLayout header={<WorkspaceDashboardHeader />}>{page}</AppLayout>;
|
||||||
|
@ -4,11 +4,17 @@ import { AppLayout } from "layouts/app-layout";
|
|||||||
import { ProfileAuthWrapper } from "layouts/user-profile-layout";
|
import { ProfileAuthWrapper } from "layouts/user-profile-layout";
|
||||||
// components
|
// components
|
||||||
import { UserProfileHeader } from "components/headers";
|
import { UserProfileHeader } from "components/headers";
|
||||||
|
import { PageHead } from "components/core";
|
||||||
// types
|
// types
|
||||||
import { NextPageWithLayout } from "lib/types";
|
import { NextPageWithLayout } from "lib/types";
|
||||||
import { ProfileIssuesPage } from "components/profile/profile-issues";
|
import { ProfileIssuesPage } from "components/profile/profile-issues";
|
||||||
|
|
||||||
const ProfileAssignedIssuesPage: NextPageWithLayout = () => <ProfileIssuesPage type="assigned" />;
|
const ProfileAssignedIssuesPage: NextPageWithLayout = () => (
|
||||||
|
<>
|
||||||
|
<PageHead title="Profile - Assigned" />
|
||||||
|
<ProfileIssuesPage type="assigned" />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
ProfileAssignedIssuesPage.getLayout = function getLayout(page: ReactElement) {
|
ProfileAssignedIssuesPage.getLayout = function getLayout(page: ReactElement) {
|
||||||
return (
|
return (
|
||||||
|
@ -6,11 +6,17 @@ import { AppLayout } from "layouts/app-layout";
|
|||||||
import { ProfileAuthWrapper } from "layouts/user-profile-layout";
|
import { ProfileAuthWrapper } from "layouts/user-profile-layout";
|
||||||
// components
|
// components
|
||||||
import { UserProfileHeader } from "components/headers";
|
import { UserProfileHeader } from "components/headers";
|
||||||
|
import { PageHead } from "components/core";
|
||||||
// types
|
// types
|
||||||
import { NextPageWithLayout } from "lib/types";
|
import { NextPageWithLayout } from "lib/types";
|
||||||
import { ProfileIssuesPage } from "components/profile/profile-issues";
|
import { ProfileIssuesPage } from "components/profile/profile-issues";
|
||||||
|
|
||||||
const ProfileCreatedIssuesPage: NextPageWithLayout = () => <ProfileIssuesPage type="created" />;
|
const ProfileCreatedIssuesPage: NextPageWithLayout = () => (
|
||||||
|
<>
|
||||||
|
<PageHead title="Profile - Created" />
|
||||||
|
<ProfileIssuesPage type="created" />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
ProfileCreatedIssuesPage.getLayout = function getLayout(page: ReactElement) {
|
ProfileCreatedIssuesPage.getLayout = function getLayout(page: ReactElement) {
|
||||||
return (
|
return (
|
||||||
|
@ -8,6 +8,7 @@ import { AppLayout } from "layouts/app-layout";
|
|||||||
import { ProfileAuthWrapper } from "layouts/user-profile-layout";
|
import { ProfileAuthWrapper } from "layouts/user-profile-layout";
|
||||||
// components
|
// components
|
||||||
import { UserProfileHeader } from "components/headers";
|
import { UserProfileHeader } from "components/headers";
|
||||||
|
import { PageHead } from "components/core";
|
||||||
import {
|
import {
|
||||||
ProfileActivity,
|
ProfileActivity,
|
||||||
ProfilePriorityDistribution,
|
ProfilePriorityDistribution,
|
||||||
@ -42,6 +43,8 @@ const ProfileOverviewPage: NextPageWithLayout = () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
|
<PageHead title="Profile - Summary" />
|
||||||
<div className="h-full w-full space-y-7 overflow-y-auto px-5 py-5 md:px-9">
|
<div className="h-full w-full space-y-7 overflow-y-auto px-5 py-5 md:px-9">
|
||||||
<ProfileStats userProfile={userProfile} />
|
<ProfileStats userProfile={userProfile} />
|
||||||
<ProfileWorkload stateDistribution={stateDistribution} />
|
<ProfileWorkload stateDistribution={stateDistribution} />
|
||||||
@ -51,12 +54,13 @@ const ProfileOverviewPage: NextPageWithLayout = () => {
|
|||||||
</div>
|
</div>
|
||||||
<ProfileActivity />
|
<ProfileActivity />
|
||||||
</div>
|
</div>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
ProfileOverviewPage.getLayout = function getLayout(page: ReactElement) {
|
ProfileOverviewPage.getLayout = function getLayout(page: ReactElement) {
|
||||||
return (
|
return (
|
||||||
<AppLayout header={<UserProfileHeader type='Summary' />}>
|
<AppLayout header={<UserProfileHeader type="Summary" />}>
|
||||||
<ProfileAuthWrapper>{page}</ProfileAuthWrapper>
|
<ProfileAuthWrapper>{page}</ProfileAuthWrapper>
|
||||||
</AppLayout>
|
</AppLayout>
|
||||||
);
|
);
|
||||||
|
@ -6,11 +6,17 @@ import { AppLayout } from "layouts/app-layout";
|
|||||||
import { ProfileAuthWrapper } from "layouts/user-profile-layout";
|
import { ProfileAuthWrapper } from "layouts/user-profile-layout";
|
||||||
// components
|
// components
|
||||||
import { UserProfileHeader } from "components/headers";
|
import { UserProfileHeader } from "components/headers";
|
||||||
|
import { PageHead } from "components/core";
|
||||||
// types
|
// types
|
||||||
import { NextPageWithLayout } from "lib/types";
|
import { NextPageWithLayout } from "lib/types";
|
||||||
import { ProfileIssuesPage } from "components/profile/profile-issues";
|
import { ProfileIssuesPage } from "components/profile/profile-issues";
|
||||||
|
|
||||||
const ProfileSubscribedIssuesPage: NextPageWithLayout = () => <ProfileIssuesPage type="subscribed" />;
|
const ProfileSubscribedIssuesPage: NextPageWithLayout = () => (
|
||||||
|
<>
|
||||||
|
<PageHead title="Profile - Subscribed" />
|
||||||
|
<ProfileIssuesPage type="subscribed" />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
ProfileSubscribedIssuesPage.getLayout = function getLayout(page: ReactElement) {
|
ProfileSubscribedIssuesPage.getLayout = function getLayout(page: ReactElement) {
|
||||||
return (
|
return (
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { useState, ReactElement } from "react";
|
import { useState, ReactElement } from "react";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
|
import { observer } from "mobx-react";
|
||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
// hooks
|
// hooks
|
||||||
import useToast from "hooks/use-toast";
|
import useToast from "hooks/use-toast";
|
||||||
@ -9,6 +10,7 @@ import { AppLayout } from "layouts/app-layout";
|
|||||||
// components
|
// components
|
||||||
import { IssueDetailRoot } from "components/issues";
|
import { IssueDetailRoot } from "components/issues";
|
||||||
import { ProjectArchivedIssueDetailsHeader } from "components/headers";
|
import { ProjectArchivedIssueDetailsHeader } from "components/headers";
|
||||||
|
import { PageHead } from "components/core";
|
||||||
// ui
|
// ui
|
||||||
import { ArchiveIcon, Loader } from "@plane/ui";
|
import { ArchiveIcon, Loader } from "@plane/ui";
|
||||||
// icons
|
// icons
|
||||||
@ -18,7 +20,7 @@ import { NextPageWithLayout } from "lib/types";
|
|||||||
// constants
|
// constants
|
||||||
import { EIssuesStoreType } from "constants/issue";
|
import { EIssuesStoreType } from "constants/issue";
|
||||||
|
|
||||||
const ArchivedIssueDetailsPage: NextPageWithLayout = () => {
|
const ArchivedIssueDetailsPage: NextPageWithLayout = observer(() => {
|
||||||
// router
|
// router
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug, projectId, archivedIssueId } = router.query;
|
const { workspaceSlug, projectId, archivedIssueId } = router.query;
|
||||||
@ -45,6 +47,9 @@ const ArchivedIssueDetailsPage: NextPageWithLayout = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const issue = getIssueById(archivedIssueId?.toString() || "") || undefined;
|
const issue = getIssueById(archivedIssueId?.toString() || "") || undefined;
|
||||||
|
const project = (issue?.project_id && getProjectById(issue?.project_id)) || undefined;
|
||||||
|
const pageTitle = project && issue ? `${project?.identifier}-${issue?.sequence_id} ${issue?.name}` : undefined;
|
||||||
|
|
||||||
if (!issue) return <></>;
|
if (!issue) return <></>;
|
||||||
|
|
||||||
const handleUnArchive = async () => {
|
const handleUnArchive = async () => {
|
||||||
@ -79,6 +84,7 @@ const ArchivedIssueDetailsPage: NextPageWithLayout = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
<PageHead title={pageTitle} />
|
||||||
{issueLoader ? (
|
{issueLoader ? (
|
||||||
<Loader className="flex h-full gap-5 p-5">
|
<Loader className="flex h-full gap-5 p-5">
|
||||||
<div className="basis-2/3 space-y-2">
|
<div className="basis-2/3 space-y-2">
|
||||||
@ -126,7 +132,7 @@ const ArchivedIssueDetailsPage: NextPageWithLayout = () => {
|
|||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
});
|
||||||
|
|
||||||
ArchivedIssueDetailsPage.getLayout = function getLayout(page: ReactElement) {
|
ArchivedIssueDetailsPage.getLayout = function getLayout(page: ReactElement) {
|
||||||
return (
|
return (
|
||||||
|
@ -1,22 +1,34 @@
|
|||||||
import { ReactElement } from "react";
|
import { ReactElement } from "react";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
|
import { observer } from "mobx-react";
|
||||||
// layouts
|
// layouts
|
||||||
import { AppLayout } from "layouts/app-layout";
|
import { AppLayout } from "layouts/app-layout";
|
||||||
// contexts
|
// contexts
|
||||||
import { ArchivedIssueLayoutRoot } from "components/issues";
|
import { ArchivedIssueLayoutRoot } from "components/issues";
|
||||||
// ui
|
// ui
|
||||||
import { ArchiveIcon } from "@plane/ui";
|
import { ArchiveIcon } from "@plane/ui";
|
||||||
|
// components
|
||||||
import { ProjectArchivedIssuesHeader } from "components/headers";
|
import { ProjectArchivedIssuesHeader } from "components/headers";
|
||||||
|
import { PageHead } from "components/core";
|
||||||
// icons
|
// icons
|
||||||
import { X } from "lucide-react";
|
import { X } from "lucide-react";
|
||||||
// types
|
// types
|
||||||
import { NextPageWithLayout } from "lib/types";
|
import { NextPageWithLayout } from "lib/types";
|
||||||
|
// hooks
|
||||||
|
import { useProject } from "hooks/store";
|
||||||
|
|
||||||
const ProjectArchivedIssuesPage: NextPageWithLayout = () => {
|
const ProjectArchivedIssuesPage: NextPageWithLayout = observer(() => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug, projectId } = router.query;
|
const { workspaceSlug, projectId } = router.query;
|
||||||
|
// store hooks
|
||||||
|
const { getProjectById } = useProject();
|
||||||
|
// derived values
|
||||||
|
const project = projectId ? getProjectById(projectId.toString()) : undefined;
|
||||||
|
const pageTitle = project?.name && `${project?.name} - Archived Issues`;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
|
<PageHead title={pageTitle} />
|
||||||
<div className="flex h-full w-full flex-col">
|
<div className="flex h-full w-full flex-col">
|
||||||
<div className="ga-1 flex items-center border-b border-custom-border-200 px-4 py-2.5 shadow-sm">
|
<div className="ga-1 flex items-center border-b border-custom-border-200 px-4 py-2.5 shadow-sm">
|
||||||
<button
|
<button
|
||||||
@ -31,8 +43,9 @@ const ProjectArchivedIssuesPage: NextPageWithLayout = () => {
|
|||||||
</div>
|
</div>
|
||||||
<ArchivedIssueLayoutRoot />
|
<ArchivedIssueLayoutRoot />
|
||||||
</div>
|
</div>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
});
|
||||||
|
|
||||||
ProjectArchivedIssuesPage.getLayout = function getLayout(page: ReactElement) {
|
ProjectArchivedIssuesPage.getLayout = function getLayout(page: ReactElement) {
|
||||||
return (
|
return (
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
import { ReactElement } from "react";
|
import { ReactElement } from "react";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
|
import { observer } from "mobx-react";
|
||||||
// hooks
|
// hooks
|
||||||
import { useCycle } from "hooks/store";
|
import { useCycle, useProject } from "hooks/store";
|
||||||
import useLocalStorage from "hooks/use-local-storage";
|
import useLocalStorage from "hooks/use-local-storage";
|
||||||
// layouts
|
// layouts
|
||||||
import { AppLayout } from "layouts/app-layout";
|
import { AppLayout } from "layouts/app-layout";
|
||||||
// components
|
// components
|
||||||
|
import { PageHead } from "components/core";
|
||||||
import { CycleIssuesHeader } from "components/headers";
|
import { CycleIssuesHeader } from "components/headers";
|
||||||
import { CycleDetailsSidebar } from "components/cycles";
|
import { CycleDetailsSidebar } from "components/cycles";
|
||||||
import { CycleLayoutRoot } from "components/issues/issue-layouts";
|
import { CycleLayoutRoot } from "components/issues/issue-layouts";
|
||||||
@ -17,27 +19,36 @@ import emptyCycle from "public/empty-state/cycle.svg";
|
|||||||
// types
|
// types
|
||||||
import { NextPageWithLayout } from "lib/types";
|
import { NextPageWithLayout } from "lib/types";
|
||||||
|
|
||||||
const CycleDetailPage: NextPageWithLayout = () => {
|
const CycleDetailPage: NextPageWithLayout = observer(() => {
|
||||||
// router
|
// router
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug, projectId, cycleId } = router.query;
|
const { workspaceSlug, projectId, cycleId } = router.query;
|
||||||
// store hooks
|
// store hooks
|
||||||
const { fetchCycleDetails } = useCycle();
|
const { fetchCycleDetails, getCycleById } = useCycle();
|
||||||
|
const { getProjectById } = useProject();
|
||||||
|
// hooks
|
||||||
const { setValue, storedValue } = useLocalStorage("cycle_sidebar_collapsed", "false");
|
const { setValue, storedValue } = useLocalStorage("cycle_sidebar_collapsed", "false");
|
||||||
const isSidebarCollapsed = storedValue ? (storedValue === "true" ? true : false) : false;
|
// fetching cycle details
|
||||||
|
|
||||||
const { error } = useSWR(
|
const { error } = useSWR(
|
||||||
workspaceSlug && projectId && cycleId ? `CYCLE_DETAILS_${cycleId.toString()}` : null,
|
workspaceSlug && projectId && cycleId ? `CYCLE_DETAILS_${cycleId.toString()}` : null,
|
||||||
workspaceSlug && projectId && cycleId
|
workspaceSlug && projectId && cycleId
|
||||||
? () => fetchCycleDetails(workspaceSlug.toString(), projectId.toString(), cycleId.toString())
|
? () => fetchCycleDetails(workspaceSlug.toString(), projectId.toString(), cycleId.toString())
|
||||||
: null
|
: 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}`);
|
const toggleSidebar = () => setValue(`${!isSidebarCollapsed}`);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
<PageHead title={pageTitle} />
|
||||||
{error ? (
|
{error ? (
|
||||||
<EmptyState
|
<EmptyState
|
||||||
image={emptyCycle}
|
image={emptyCycle}
|
||||||
@ -70,7 +81,7 @@ const CycleDetailPage: NextPageWithLayout = () => {
|
|||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
});
|
||||||
|
|
||||||
CycleDetailPage.getLayout = function getLayout(page: ReactElement) {
|
CycleDetailPage.getLayout = function getLayout(page: ReactElement) {
|
||||||
return (
|
return (
|
||||||
|
@ -4,11 +4,12 @@ import { observer } from "mobx-react-lite";
|
|||||||
import { Tab } from "@headlessui/react";
|
import { Tab } from "@headlessui/react";
|
||||||
import { useTheme } from "next-themes";
|
import { useTheme } from "next-themes";
|
||||||
// hooks
|
// hooks
|
||||||
import { useEventTracker, useCycle, useUser } from "hooks/store";
|
import { useEventTracker, useCycle, useUser, useProject } from "hooks/store";
|
||||||
import useLocalStorage from "hooks/use-local-storage";
|
import useLocalStorage from "hooks/use-local-storage";
|
||||||
// layouts
|
// layouts
|
||||||
import { AppLayout } from "layouts/app-layout";
|
import { AppLayout } from "layouts/app-layout";
|
||||||
// components
|
// components
|
||||||
|
import { PageHead } from "components/core";
|
||||||
import { CyclesHeader } from "components/headers";
|
import { CyclesHeader } from "components/headers";
|
||||||
import { CyclesView, ActiveCycleDetails, CycleCreateUpdateModal } from "components/cycles";
|
import { CyclesView, ActiveCycleDetails, CycleCreateUpdateModal } from "components/cycles";
|
||||||
import { EmptyState, getEmptyStateImagePath } from "components/empty-state";
|
import { EmptyState, getEmptyStateImagePath } from "components/empty-state";
|
||||||
@ -34,12 +35,20 @@ const ProjectCyclesPage: NextPageWithLayout = observer(() => {
|
|||||||
currentUser,
|
currentUser,
|
||||||
} = useUser();
|
} = useUser();
|
||||||
const { currentProjectCycleIds, loader } = useCycle();
|
const { currentProjectCycleIds, loader } = useCycle();
|
||||||
|
const { getProjectById } = useProject();
|
||||||
// router
|
// router
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug, projectId, peekCycle } = router.query;
|
const { workspaceSlug, projectId, peekCycle } = router.query;
|
||||||
// local storage
|
// local storage
|
||||||
const { storedValue: cycleTab, setValue: setCycleTab } = useLocalStorage<TCycleView>("cycle_tab", "active");
|
const { storedValue: cycleTab, setValue: setCycleTab } = useLocalStorage<TCycleView>("cycle_tab", "active");
|
||||||
const { storedValue: cycleLayout, setValue: setCycleLayout } = useLocalStorage<TCycleLayout>("cycle_layout", "list");
|
const { storedValue: cycleLayout, setValue: setCycleLayout } = useLocalStorage<TCycleLayout>("cycle_layout", "list");
|
||||||
|
// derived values
|
||||||
|
const isLightMode = resolvedTheme ? resolvedTheme === "light" : currentUser?.theme.theme === "light";
|
||||||
|
const EmptyStateImagePath = getEmptyStateImagePath("onboarding", "cycles", isLightMode);
|
||||||
|
const totalCycles = currentProjectCycleIds?.length ?? 0;
|
||||||
|
const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserWorkspaceRoles.MEMBER;
|
||||||
|
const project = projectId ? getProjectById(projectId?.toString()) : undefined;
|
||||||
|
const pageTitle = project?.name ? `${project?.name} - Cycles` : undefined;
|
||||||
|
|
||||||
const handleCurrentLayout = useCallback(
|
const handleCurrentLayout = useCallback(
|
||||||
(_layout: TCycleLayout) => {
|
(_layout: TCycleLayout) => {
|
||||||
@ -56,13 +65,6 @@ const ProjectCyclesPage: NextPageWithLayout = observer(() => {
|
|||||||
[handleCurrentLayout, setCycleTab]
|
[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 (!workspaceSlug || !projectId) return null;
|
||||||
|
|
||||||
if (loader)
|
if (loader)
|
||||||
@ -75,6 +77,8 @@ const ProjectCyclesPage: NextPageWithLayout = observer(() => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
|
<PageHead title={pageTitle} />
|
||||||
<div className="w-full h-full">
|
<div className="w-full h-full">
|
||||||
<CycleCreateUpdateModal
|
<CycleCreateUpdateModal
|
||||||
workspaceSlug={workspaceSlug.toString()}
|
workspaceSlug={workspaceSlug.toString()}
|
||||||
@ -212,6 +216,7 @@ const ProjectCyclesPage: NextPageWithLayout = observer(() => {
|
|||||||
</Tab.Group>
|
</Tab.Group>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -5,15 +5,26 @@ import { X, PenSquare } from "lucide-react";
|
|||||||
import { AppLayout } from "layouts/app-layout";
|
import { AppLayout } from "layouts/app-layout";
|
||||||
// components
|
// components
|
||||||
import { DraftIssueLayoutRoot } from "components/issues/issue-layouts/roots/draft-issue-layout-root";
|
import { DraftIssueLayoutRoot } from "components/issues/issue-layouts/roots/draft-issue-layout-root";
|
||||||
|
import { PageHead } from "components/core";
|
||||||
import { ProjectDraftIssueHeader } from "components/headers";
|
import { ProjectDraftIssueHeader } from "components/headers";
|
||||||
// types
|
// types
|
||||||
import { NextPageWithLayout } from "lib/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 router = useRouter();
|
||||||
const { workspaceSlug, projectId } = router.query;
|
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 (
|
return (
|
||||||
|
<>
|
||||||
|
<PageHead title={pageTitle} />
|
||||||
<div className="flex h-full w-full flex-col">
|
<div className="flex h-full w-full flex-col">
|
||||||
<div className="ga-1 flex items-center border-b border-custom-border-200 px-4 py-2.5 shadow-sm">
|
<div className="ga-1 flex items-center border-b border-custom-border-200 px-4 py-2.5 shadow-sm">
|
||||||
<button
|
<button
|
||||||
@ -23,13 +34,14 @@ const ProjectDraftIssuesPage: NextPageWithLayout = () => {
|
|||||||
>
|
>
|
||||||
<PenSquare className="h-4 w-4" />
|
<PenSquare className="h-4 w-4" />
|
||||||
<span>Draft Issues</span>
|
<span>Draft Issues</span>
|
||||||
<X className="h-3 w-3" />
|
|
||||||
</button>
|
</button>
|
||||||
|
<X className="h-3 w-3" />
|
||||||
</div>
|
</div>
|
||||||
<DraftIssueLayoutRoot />
|
<DraftIssueLayoutRoot />
|
||||||
</div>
|
</div>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
});
|
||||||
|
|
||||||
ProjectDraftIssuesPage.getLayout = function getLayout(page: ReactElement) {
|
ProjectDraftIssuesPage.getLayout = function getLayout(page: ReactElement) {
|
||||||
return (
|
return (
|
||||||
|
@ -7,9 +7,9 @@ import { useProject, useInboxIssues } from "hooks/store";
|
|||||||
// layouts
|
// layouts
|
||||||
import { AppLayout } from "layouts/app-layout";
|
import { AppLayout } from "layouts/app-layout";
|
||||||
// components
|
// components
|
||||||
|
import { PageHead } from "components/core";
|
||||||
import { ProjectInboxHeader } from "components/headers";
|
import { ProjectInboxHeader } from "components/headers";
|
||||||
import { InboxSidebarRoot, InboxContentRoot } from "components/inbox";
|
import { InboxSidebarRoot, InboxContentRoot } from "components/inbox";
|
||||||
|
|
||||||
// types
|
// types
|
||||||
import { NextPageWithLayout } from "lib/types";
|
import { NextPageWithLayout } from "lib/types";
|
||||||
|
|
||||||
@ -22,7 +22,7 @@ const ProjectInboxPage: NextPageWithLayout = observer(() => {
|
|||||||
filters: { fetchInboxFilters },
|
filters: { fetchInboxFilters },
|
||||||
issues: { fetchInboxIssues },
|
issues: { fetchInboxIssues },
|
||||||
} = useInboxIssues();
|
} = useInboxIssues();
|
||||||
|
// fetching the Inbox filters and issues
|
||||||
useSWR(
|
useSWR(
|
||||||
workspaceSlug && projectId && currentProjectDetails && currentProjectDetails?.inbox_view
|
workspaceSlug && projectId && currentProjectDetails && currentProjectDetails?.inbox_view
|
||||||
? `INBOX_ISSUES_${workspaceSlug.toString()}_${projectId.toString()}`
|
? `INBOX_ISSUES_${workspaceSlug.toString()}_${projectId.toString()}`
|
||||||
@ -34,9 +34,14 @@ const ProjectInboxPage: NextPageWithLayout = observer(() => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
// derived values
|
||||||
|
const pageTitle = currentProjectDetails?.name ? `${currentProjectDetails?.name} - Inbox` : undefined;
|
||||||
|
|
||||||
if (!workspaceSlug || !projectId || !inboxId || !currentProjectDetails?.inbox_view) return <></>;
|
if (!workspaceSlug || !projectId || !inboxId || !currentProjectDetails?.inbox_view) return <></>;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
|
<PageHead title={pageTitle} />
|
||||||
<div className="relative flex h-full overflow-hidden">
|
<div className="relative flex h-full overflow-hidden">
|
||||||
<div className="flex-shrink-0 w-[340px] h-full border-r border-custom-border-300">
|
<div className="flex-shrink-0 w-[340px] h-full border-r border-custom-border-300">
|
||||||
<InboxSidebarRoot
|
<InboxSidebarRoot
|
||||||
@ -54,6 +59,7 @@ const ProjectInboxPage: NextPageWithLayout = observer(() => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -5,14 +5,15 @@ import { observer } from "mobx-react-lite";
|
|||||||
// layouts
|
// layouts
|
||||||
import { AppLayout } from "layouts/app-layout";
|
import { AppLayout } from "layouts/app-layout";
|
||||||
// components
|
// components
|
||||||
|
import { PageHead } from "components/core";
|
||||||
import { ProjectIssueDetailsHeader } from "components/headers";
|
import { ProjectIssueDetailsHeader } from "components/headers";
|
||||||
import { IssueDetailRoot } from "components/issues";
|
import { IssueDetailRoot } from "components/issues";
|
||||||
// ui
|
// ui
|
||||||
import { Loader } from "@plane/ui";
|
import { Loader } from "@plane/ui";
|
||||||
// types
|
// types
|
||||||
import { NextPageWithLayout } from "lib/types";
|
import { NextPageWithLayout } from "lib/types";
|
||||||
// fetch-keys
|
// store hooks
|
||||||
import { useApplication, useIssueDetail } from "hooks/store";
|
import { useApplication, useIssueDetail, useProject } from "hooks/store";
|
||||||
|
|
||||||
const IssueDetailsPage: NextPageWithLayout = observer(() => {
|
const IssueDetailsPage: NextPageWithLayout = observer(() => {
|
||||||
// router
|
// router
|
||||||
@ -23,17 +24,20 @@ const IssueDetailsPage: NextPageWithLayout = observer(() => {
|
|||||||
fetchIssue,
|
fetchIssue,
|
||||||
issue: { getIssueById },
|
issue: { getIssueById },
|
||||||
} = useIssueDetail();
|
} = useIssueDetail();
|
||||||
|
const { getProjectById } = useProject();
|
||||||
const { theme: themeStore } = useApplication();
|
const { theme: themeStore } = useApplication();
|
||||||
|
// fetching issue details
|
||||||
const { isLoading } = useSWR(
|
const { isLoading } = useSWR(
|
||||||
workspaceSlug && projectId && issueId ? `ISSUE_DETAIL_${workspaceSlug}_${projectId}_${issueId}` : null,
|
workspaceSlug && projectId && issueId ? `ISSUE_DETAIL_${workspaceSlug}_${projectId}_${issueId}` : null,
|
||||||
workspaceSlug && projectId && issueId
|
workspaceSlug && projectId && issueId
|
||||||
? () => fetchIssue(workspaceSlug.toString(), projectId.toString(), issueId.toString())
|
? () => fetchIssue(workspaceSlug.toString(), projectId.toString(), issueId.toString())
|
||||||
: null
|
: null
|
||||||
);
|
);
|
||||||
|
// derived values
|
||||||
const issue = getIssueById(issueId?.toString() || "") || undefined;
|
const issue = getIssueById(issueId?.toString() || "") || undefined;
|
||||||
|
const project = (issue?.project_id && getProjectById(issue?.project_id)) || undefined;
|
||||||
const issueLoader = !issue || isLoading ? true : false;
|
const issueLoader = !issue || isLoading ? true : false;
|
||||||
|
const pageTitle = project && issue ? `${project?.identifier}-${issue?.sequence_id} ${issue?.name}` : undefined;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleToggleIssueDetailSidebar = () => {
|
const handleToggleIssueDetailSidebar = () => {
|
||||||
@ -52,6 +56,7 @@ const IssueDetailsPage: NextPageWithLayout = observer(() => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
<PageHead title={pageTitle} />
|
||||||
{issueLoader ? (
|
{issueLoader ? (
|
||||||
<Loader className="flex h-full gap-5 p-5">
|
<Loader className="flex h-full gap-5 p-5">
|
||||||
<div className="basis-2/3 space-y-2">
|
<div className="basis-2/3 space-y-2">
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
import { ReactElement } from "react";
|
import { ReactElement } from "react";
|
||||||
|
import Head from "next/head";
|
||||||
|
import { useRouter } from "next/router";
|
||||||
|
import { observer } from "mobx-react";
|
||||||
// components
|
// components
|
||||||
import { ProjectLayoutRoot } from "components/issues";
|
import { ProjectLayoutRoot } from "components/issues";
|
||||||
import { ProjectIssuesHeader } from "components/headers";
|
import { ProjectIssuesHeader } from "components/headers";
|
||||||
@ -6,12 +9,36 @@ import { ProjectIssuesHeader } from "components/headers";
|
|||||||
import { NextPageWithLayout } from "lib/types";
|
import { NextPageWithLayout } from "lib/types";
|
||||||
// layouts
|
// layouts
|
||||||
import { AppLayout } from "layouts/app-layout";
|
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 (
|
||||||
|
<>
|
||||||
|
<PageHead title={pageTitle} />
|
||||||
|
<Head>
|
||||||
|
<title>{project?.name} - Issues</title>
|
||||||
|
</Head>
|
||||||
<div className="h-full w-full">
|
<div className="h-full w-full">
|
||||||
<ProjectLayoutRoot />
|
<ProjectLayoutRoot />
|
||||||
</div>
|
</div>
|
||||||
);
|
</>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
ProjectIssuesPage.getLayout = function getLayout(page: ReactElement) {
|
ProjectIssuesPage.getLayout = function getLayout(page: ReactElement) {
|
||||||
return (
|
return (
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import { ReactElement } from "react";
|
import { ReactElement } from "react";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
|
import { observer } from "mobx-react";
|
||||||
// hooks
|
// hooks
|
||||||
import { useModule } from "hooks/store";
|
import { useModule, useProject } from "hooks/store";
|
||||||
import useLocalStorage from "hooks/use-local-storage";
|
import useLocalStorage from "hooks/use-local-storage";
|
||||||
// layouts
|
// layouts
|
||||||
import { AppLayout } from "layouts/app-layout";
|
import { AppLayout } from "layouts/app-layout";
|
||||||
@ -10,37 +11,44 @@ import { AppLayout } from "layouts/app-layout";
|
|||||||
import { ModuleDetailsSidebar } from "components/modules";
|
import { ModuleDetailsSidebar } from "components/modules";
|
||||||
import { ModuleLayoutRoot } from "components/issues";
|
import { ModuleLayoutRoot } from "components/issues";
|
||||||
import { ModuleIssuesHeader } from "components/headers";
|
import { ModuleIssuesHeader } from "components/headers";
|
||||||
// ui
|
import { PageHead } from "components/core";
|
||||||
import { EmptyState } from "components/common";
|
import { EmptyState } from "components/common";
|
||||||
// assets
|
// assets
|
||||||
import emptyModule from "public/empty-state/module.svg";
|
import emptyModule from "public/empty-state/module.svg";
|
||||||
// types
|
// types
|
||||||
import { NextPageWithLayout } from "lib/types";
|
import { NextPageWithLayout } from "lib/types";
|
||||||
|
|
||||||
const ModuleIssuesPage: NextPageWithLayout = () => {
|
const ModuleIssuesPage: NextPageWithLayout = observer(() => {
|
||||||
// router
|
// router
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug, projectId, moduleId } = router.query;
|
const { workspaceSlug, projectId, moduleId } = router.query;
|
||||||
// store hooks
|
// store hooks
|
||||||
const { fetchModuleDetails } = useModule();
|
const { fetchModuleDetails, getModuleById } = useModule();
|
||||||
|
const { getProjectById } = useProject();
|
||||||
// local storage
|
// local storage
|
||||||
const { setValue, storedValue } = useLocalStorage("module_sidebar_collapsed", "false");
|
const { setValue, storedValue } = useLocalStorage("module_sidebar_collapsed", "false");
|
||||||
const isSidebarCollapsed = storedValue ? (storedValue === "true" ? true : false) : false;
|
const isSidebarCollapsed = storedValue ? (storedValue === "true" ? true : false) : false;
|
||||||
|
// fetching module details
|
||||||
const { error } = useSWR(
|
const { error } = useSWR(
|
||||||
workspaceSlug && projectId && moduleId ? `CURRENT_MODULE_DETAILS_${moduleId.toString()}` : null,
|
workspaceSlug && projectId && moduleId ? `CURRENT_MODULE_DETAILS_${moduleId.toString()}` : null,
|
||||||
workspaceSlug && projectId && moduleId
|
workspaceSlug && projectId && moduleId
|
||||||
? () => fetchModuleDetails(workspaceSlug.toString(), projectId.toString(), moduleId.toString())
|
? () => fetchModuleDetails(workspaceSlug.toString(), projectId.toString(), moduleId.toString())
|
||||||
: null
|
: 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 = () => {
|
const toggleSidebar = () => {
|
||||||
setValue(`${!isSidebarCollapsed}`);
|
setValue(`${!isSidebarCollapsed}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!workspaceSlug || !projectId || !moduleId) return <></>;
|
if (!workspaceSlug || !projectId || !moduleId) return <></>;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
<PageHead title={pageTitle} />
|
||||||
{error ? (
|
{error ? (
|
||||||
<EmptyState
|
<EmptyState
|
||||||
image={emptyModule}
|
image={emptyModule}
|
||||||
@ -71,7 +79,7 @@ const ModuleIssuesPage: NextPageWithLayout = () => {
|
|||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
});
|
||||||
|
|
||||||
ModuleIssuesPage.getLayout = function getLayout(page: ReactElement) {
|
ModuleIssuesPage.getLayout = function getLayout(page: ReactElement) {
|
||||||
return (
|
return (
|
||||||
|
@ -1,13 +1,33 @@
|
|||||||
import { ReactElement } from "react";
|
import { ReactElement } from "react";
|
||||||
|
import { useRouter } from "next/router";
|
||||||
// layouts
|
// layouts
|
||||||
import { AppLayout } from "layouts/app-layout";
|
import { AppLayout } from "layouts/app-layout";
|
||||||
// components
|
// components
|
||||||
|
import { PageHead } from "components/core";
|
||||||
import { ModulesListView } from "components/modules";
|
import { ModulesListView } from "components/modules";
|
||||||
import { ModulesListHeader } from "components/headers";
|
import { ModulesListHeader } from "components/headers";
|
||||||
// types
|
// types
|
||||||
import { NextPageWithLayout } from "lib/types";
|
import { NextPageWithLayout } from "lib/types";
|
||||||
|
// hooks
|
||||||
|
import { useProject } from "hooks/store";
|
||||||
|
import { observer } from "mobx-react";
|
||||||
|
|
||||||
const ProjectModulesPage: NextPageWithLayout = () => <ModulesListView />;
|
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 (
|
||||||
|
<>
|
||||||
|
<PageHead title={pageTitle} />
|
||||||
|
<ModulesListView />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
ProjectModulesPage.getLayout = function getLayout(page: ReactElement) {
|
ProjectModulesPage.getLayout = function getLayout(page: ReactElement) {
|
||||||
return (
|
return (
|
||||||
|
@ -14,7 +14,7 @@ import { FileService } from "services/file.service";
|
|||||||
// layouts
|
// layouts
|
||||||
import { AppLayout } from "layouts/app-layout";
|
import { AppLayout } from "layouts/app-layout";
|
||||||
// components
|
// components
|
||||||
import { GptAssistantPopover } from "components/core";
|
import { GptAssistantPopover, PageHead } from "components/core";
|
||||||
import { PageDetailsHeader } from "components/headers/page-details";
|
import { PageDetailsHeader } from "components/headers/page-details";
|
||||||
// ui
|
// ui
|
||||||
import { DocumentEditorWithRef, DocumentReadOnlyEditorWithRef } from "@plane/document-editor";
|
import { DocumentEditorWithRef, DocumentReadOnlyEditorWithRef } from "@plane/document-editor";
|
||||||
@ -256,6 +256,8 @@ const PageDetailsPage: NextPageWithLayout = observer(() => {
|
|||||||
currentProjectRole && [EUserProjectRoles.ADMIN, EUserProjectRoles.MEMBER].includes(currentProjectRole);
|
currentProjectRole && [EUserProjectRoles.ADMIN, EUserProjectRoles.MEMBER].includes(currentProjectRole);
|
||||||
|
|
||||||
return pageIdMobx ? (
|
return pageIdMobx ? (
|
||||||
|
<>
|
||||||
|
<PageHead title={pageTitle} />
|
||||||
<div className="flex h-full flex-col justify-between">
|
<div className="flex h-full flex-col justify-between">
|
||||||
<div className="h-full w-full overflow-hidden">
|
<div className="h-full w-full overflow-hidden">
|
||||||
{isPageReadOnly ? (
|
{isPageReadOnly ? (
|
||||||
@ -363,6 +365,7 @@ const PageDetailsPage: NextPageWithLayout = observer(() => {
|
|||||||
<IssuePeekOverview />
|
<IssuePeekOverview />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</>
|
||||||
) : (
|
) : (
|
||||||
<div className="grid h-full w-full place-items-center">
|
<div className="grid h-full w-full place-items-center">
|
||||||
<Spinner />
|
<Spinner />
|
||||||
|
@ -6,7 +6,7 @@ import useSWR from "swr";
|
|||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { useTheme } from "next-themes";
|
import { useTheme } from "next-themes";
|
||||||
// hooks
|
// hooks
|
||||||
import { useApplication, useEventTracker, useUser } from "hooks/store";
|
import { useApplication, useEventTracker, useUser, useProject } from "hooks/store";
|
||||||
import useLocalStorage from "hooks/use-local-storage";
|
import useLocalStorage from "hooks/use-local-storage";
|
||||||
import useUserAuth from "hooks/use-user-auth";
|
import useUserAuth from "hooks/use-user-auth";
|
||||||
import useSize from "hooks/use-window-size";
|
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 { useProjectPages } from "hooks/store/use-project-page";
|
||||||
import { EUserWorkspaceRoles } from "constants/workspace";
|
import { EUserWorkspaceRoles } from "constants/workspace";
|
||||||
import { PAGE_EMPTY_STATE_DETAILS } from "constants/empty-state";
|
import { PAGE_EMPTY_STATE_DETAILS } from "constants/empty-state";
|
||||||
|
import { PageHead } from "components/core";
|
||||||
|
|
||||||
const AllPagesList = dynamic<any>(() => import("components/pages").then((a) => a.AllPagesList), {
|
const AllPagesList = dynamic<any>(() => import("components/pages").then((a) => a.AllPagesList), {
|
||||||
ssr: false,
|
ssr: false,
|
||||||
@ -63,7 +64,7 @@ const ProjectPagesPage: NextPageWithLayout = observer(() => {
|
|||||||
commandPalette: { toggleCreatePageModal },
|
commandPalette: { toggleCreatePageModal },
|
||||||
} = useApplication();
|
} = useApplication();
|
||||||
const { setTrackElement } = useEventTracker();
|
const { setTrackElement } = useEventTracker();
|
||||||
|
const { getProjectById } = useProject();
|
||||||
const { fetchProjectPages, fetchArchivedProjectPages, loader, archivedPageLoader, projectPageIds, archivedPageIds } =
|
const { fetchProjectPages, fetchArchivedProjectPages, loader, archivedPageLoader, projectPageIds, archivedPageIds } =
|
||||||
useProjectPages();
|
useProjectPages();
|
||||||
// hooks
|
// hooks
|
||||||
@ -101,10 +102,12 @@ const ProjectPagesPage: NextPageWithLayout = observer(() => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// derived values
|
||||||
const isLightMode = resolvedTheme ? resolvedTheme === "light" : currentUser?.theme.theme === "light";
|
const isLightMode = resolvedTheme ? resolvedTheme === "light" : currentUser?.theme.theme === "light";
|
||||||
const EmptyStateImagePath = getEmptyStateImagePath("onboarding", "pages", isLightMode);
|
const EmptyStateImagePath = getEmptyStateImagePath("onboarding", "pages", isLightMode);
|
||||||
|
|
||||||
const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserWorkspaceRoles.MEMBER;
|
const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserWorkspaceRoles.MEMBER;
|
||||||
|
const project = projectId ? getProjectById(projectId.toString()) : undefined;
|
||||||
|
const pageTitle = project?.name ? `${project?.name} - Pages` : undefined;
|
||||||
|
|
||||||
const MobileTabList = () => (
|
const MobileTabList = () => (
|
||||||
<Tab.List as="div" className="flex items-center justify-between border-b border-custom-border-200 px-3 pt-3 mb-4">
|
<Tab.List as="div" className="flex items-center justify-between border-b border-custom-border-200 px-3 pt-3 mb-4">
|
||||||
@ -129,6 +132,7 @@ const ProjectPagesPage: NextPageWithLayout = observer(() => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
<PageHead title={pageTitle} />
|
||||||
{projectPageIds && archivedPageIds && projectPageIds.length + archivedPageIds.length > 0 ? (
|
{projectPageIds && archivedPageIds && projectPageIds.length + archivedPageIds.length > 0 ? (
|
||||||
<>
|
<>
|
||||||
{workspaceSlug && projectId && (
|
{workspaceSlug && projectId && (
|
||||||
|
@ -10,6 +10,7 @@ import { ProjectSettingLayout } from "layouts/settings-layout";
|
|||||||
import useToast from "hooks/use-toast";
|
import useToast from "hooks/use-toast";
|
||||||
// components
|
// components
|
||||||
import { AutoArchiveAutomation, AutoCloseAutomation } from "components/automation";
|
import { AutoArchiveAutomation, AutoCloseAutomation } from "components/automation";
|
||||||
|
import { PageHead } from "components/core";
|
||||||
import { ProjectSettingHeader } from "components/headers";
|
import { ProjectSettingHeader } from "components/headers";
|
||||||
// types
|
// types
|
||||||
import { NextPageWithLayout } from "lib/types";
|
import { NextPageWithLayout } from "lib/types";
|
||||||
@ -41,9 +42,13 @@ const AutomationSettingsPage: NextPageWithLayout = observer(() => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// derived values
|
||||||
const isAdmin = currentProjectRole === EUserProjectRoles.ADMIN;
|
const isAdmin = currentProjectRole === EUserProjectRoles.ADMIN;
|
||||||
|
const pageTitle = projectDetails?.name ? `${projectDetails?.name} - Automations` : undefined;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
|
<PageHead title={pageTitle} />
|
||||||
<section className={`w-full overflow-y-auto py-8 pr-9 ${isAdmin ? "" : "opacity-60"}`}>
|
<section className={`w-full overflow-y-auto py-8 pr-9 ${isAdmin ? "" : "opacity-60"}`}>
|
||||||
<div className="flex items-center border-b border-custom-border-100 py-3.5">
|
<div className="flex items-center border-b border-custom-border-100 py-3.5">
|
||||||
<h3 className="text-xl font-medium">Automations</h3>
|
<h3 className="text-xl font-medium">Automations</h3>
|
||||||
@ -51,6 +56,7 @@ const AutomationSettingsPage: NextPageWithLayout = observer(() => {
|
|||||||
<AutoArchiveAutomation handleChange={handleChange} />
|
<AutoArchiveAutomation handleChange={handleChange} />
|
||||||
<AutoCloseAutomation handleChange={handleChange} />
|
<AutoCloseAutomation handleChange={handleChange} />
|
||||||
</section>
|
</section>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
import { ReactElement } from "react";
|
import { ReactElement } from "react";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
// hooks
|
// hooks
|
||||||
import { useUser } from "hooks/store";
|
import { useUser, useProject } from "hooks/store";
|
||||||
// layouts
|
// layouts
|
||||||
import { AppLayout } from "layouts/app-layout";
|
import { AppLayout } from "layouts/app-layout";
|
||||||
import { ProjectSettingLayout } from "layouts/settings-layout";
|
import { ProjectSettingLayout } from "layouts/settings-layout";
|
||||||
// components
|
// components
|
||||||
|
import { PageHead } from "components/core";
|
||||||
import { ProjectSettingHeader } from "components/headers";
|
import { ProjectSettingHeader } from "components/headers";
|
||||||
import { EstimatesList } from "components/estimates";
|
import { EstimatesList } from "components/estimates";
|
||||||
// types
|
// types
|
||||||
@ -17,13 +18,18 @@ const EstimatesSettingsPage: NextPageWithLayout = observer(() => {
|
|||||||
const {
|
const {
|
||||||
membership: { currentProjectRole },
|
membership: { currentProjectRole },
|
||||||
} = useUser();
|
} = useUser();
|
||||||
|
const { currentProjectDetails } = useProject();
|
||||||
|
// derived values
|
||||||
const isAdmin = currentProjectRole === EUserProjectRoles.ADMIN;
|
const isAdmin = currentProjectRole === EUserProjectRoles.ADMIN;
|
||||||
|
const pageTitle = currentProjectDetails?.name ? `${currentProjectDetails?.name} - Estimates` : undefined;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
|
<PageHead title={pageTitle} />
|
||||||
<div className={`h-full w-full overflow-y-auto py-8 pr-9 ${isAdmin ? "" : "pointer-events-none opacity-60"}`}>
|
<div className={`h-full w-full overflow-y-auto py-8 pr-9 ${isAdmin ? "" : "pointer-events-none opacity-60"}`}>
|
||||||
<EstimatesList />
|
<EstimatesList />
|
||||||
</div>
|
</div>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,41 +1,48 @@
|
|||||||
import { ReactElement } from "react";
|
import { ReactElement } from "react";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
|
import { observer } from "mobx-react";
|
||||||
// hooks
|
// hooks
|
||||||
import { useUser } from "hooks/store";
|
import { useProject, useUser } from "hooks/store";
|
||||||
// layouts
|
// layouts
|
||||||
import { AppLayout } from "layouts/app-layout";
|
import { AppLayout } from "layouts/app-layout";
|
||||||
import { ProjectSettingLayout } from "layouts/settings-layout";
|
import { ProjectSettingLayout } from "layouts/settings-layout";
|
||||||
// components
|
// components
|
||||||
|
import { PageHead } from "components/core";
|
||||||
import { ProjectSettingHeader } from "components/headers";
|
import { ProjectSettingHeader } from "components/headers";
|
||||||
import { ProjectFeaturesList } from "components/project";
|
import { ProjectFeaturesList } from "components/project";
|
||||||
// types
|
// types
|
||||||
import { NextPageWithLayout } from "lib/types";
|
import { NextPageWithLayout } from "lib/types";
|
||||||
|
|
||||||
const FeaturesSettingsPage: NextPageWithLayout = () => {
|
const FeaturesSettingsPage: NextPageWithLayout = observer(() => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug, projectId } = router.query;
|
const { workspaceSlug, projectId } = router.query;
|
||||||
// store
|
// store
|
||||||
const {
|
const {
|
||||||
membership: { fetchUserProjectInfo },
|
membership: { fetchUserProjectInfo },
|
||||||
} = useUser();
|
} = useUser();
|
||||||
|
const { currentProjectDetails } = useProject();
|
||||||
|
// fetch the project details
|
||||||
const { data: memberDetails } = useSWR(
|
const { data: memberDetails } = useSWR(
|
||||||
workspaceSlug && projectId ? `PROJECT_MEMBERS_ME_${workspaceSlug}_${projectId}` : null,
|
workspaceSlug && projectId ? `PROJECT_MEMBERS_ME_${workspaceSlug}_${projectId}` : null,
|
||||||
workspaceSlug && projectId ? () => fetchUserProjectInfo(workspaceSlug.toString(), projectId.toString()) : null
|
workspaceSlug && projectId ? () => fetchUserProjectInfo(workspaceSlug.toString(), projectId.toString()) : null
|
||||||
);
|
);
|
||||||
|
// derived values
|
||||||
const isAdmin = memberDetails?.role === 20;
|
const isAdmin = memberDetails?.role === 20;
|
||||||
|
const pageTitle = currentProjectDetails?.name ? `${currentProjectDetails?.name} - Features` : undefined;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
|
<PageHead title={pageTitle} />
|
||||||
<section className={`w-full overflow-y-auto py-8 pr-9 ${isAdmin ? "" : "opacity-60"}`}>
|
<section className={`w-full overflow-y-auto py-8 pr-9 ${isAdmin ? "" : "opacity-60"}`}>
|
||||||
<div className="flex items-center border-b border-custom-border-100 py-3.5">
|
<div className="flex items-center border-b border-custom-border-100 py-3.5">
|
||||||
<h3 className="text-xl font-medium">Features</h3>
|
<h3 className="text-xl font-medium">Features</h3>
|
||||||
</div>
|
</div>
|
||||||
<ProjectFeaturesList />
|
<ProjectFeaturesList />
|
||||||
</section>
|
</section>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
});
|
||||||
|
|
||||||
FeaturesSettingsPage.getLayout = function getLayout(page: ReactElement) {
|
FeaturesSettingsPage.getLayout = function getLayout(page: ReactElement) {
|
||||||
return (
|
return (
|
||||||
|
@ -8,6 +8,7 @@ import { useProject } from "hooks/store";
|
|||||||
import { AppLayout } from "layouts/app-layout";
|
import { AppLayout } from "layouts/app-layout";
|
||||||
import { ProjectSettingLayout } from "layouts/settings-layout";
|
import { ProjectSettingLayout } from "layouts/settings-layout";
|
||||||
// components
|
// components
|
||||||
|
import { PageHead } from "components/core";
|
||||||
import { ProjectSettingHeader } from "components/headers";
|
import { ProjectSettingHeader } from "components/headers";
|
||||||
import {
|
import {
|
||||||
DeleteProjectModal,
|
DeleteProjectModal,
|
||||||
@ -32,14 +33,15 @@ const GeneralSettingsPage: NextPageWithLayout = observer(() => {
|
|||||||
workspaceSlug && projectId ? `PROJECT_DETAILS_${projectId}` : null,
|
workspaceSlug && projectId ? `PROJECT_DETAILS_${projectId}` : null,
|
||||||
workspaceSlug && projectId ? () => fetchProjectDetails(workspaceSlug.toString(), projectId.toString()) : 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 currentNetwork = NETWORK_CHOICES.find((n) => n.key === projectDetails?.network);
|
||||||
// const selectedNetwork = NETWORK_CHOICES.find((n) => n.key === watch("network"));
|
// const selectedNetwork = NETWORK_CHOICES.find((n) => n.key === watch("network"));
|
||||||
|
|
||||||
const isAdmin = currentProjectDetails?.member_role === 20;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
<PageHead title={pageTitle} />
|
||||||
{currentProjectDetails && (
|
{currentProjectDetails && (
|
||||||
<DeleteProjectModal
|
<DeleteProjectModal
|
||||||
project={currentProjectDetails}
|
project={currentProjectDetails}
|
||||||
|
@ -2,6 +2,7 @@ import { ReactElement } from "react";
|
|||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
import { useTheme } from "next-themes";
|
import { useTheme } from "next-themes";
|
||||||
|
import { observer } from "mobx-react";
|
||||||
// hooks
|
// hooks
|
||||||
import { useUser } from "hooks/store";
|
import { useUser } from "hooks/store";
|
||||||
// layouts
|
// layouts
|
||||||
@ -11,6 +12,7 @@ import { ProjectSettingLayout } from "layouts/settings-layout";
|
|||||||
import { IntegrationService } from "services/integrations";
|
import { IntegrationService } from "services/integrations";
|
||||||
import { ProjectService } from "services/project";
|
import { ProjectService } from "services/project";
|
||||||
// components
|
// components
|
||||||
|
import { PageHead } from "components/core";
|
||||||
import { IntegrationCard } from "components/project";
|
import { IntegrationCard } from "components/project";
|
||||||
import { ProjectSettingHeader } from "components/headers";
|
import { ProjectSettingHeader } from "components/headers";
|
||||||
import { EmptyState, getEmptyStateImagePath } from "components/empty-state";
|
import { EmptyState, getEmptyStateImagePath } from "components/empty-state";
|
||||||
@ -27,31 +29,33 @@ import { PROJECT_SETTINGS_EMPTY_STATE_DETAILS } from "constants/empty-state";
|
|||||||
const integrationService = new IntegrationService();
|
const integrationService = new IntegrationService();
|
||||||
const projectService = new ProjectService();
|
const projectService = new ProjectService();
|
||||||
|
|
||||||
const ProjectIntegrationsPage: NextPageWithLayout = () => {
|
const ProjectIntegrationsPage: NextPageWithLayout = observer(() => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug, projectId } = router.query;
|
const { workspaceSlug, projectId } = router.query;
|
||||||
// theme
|
// theme
|
||||||
const { resolvedTheme } = useTheme();
|
const { resolvedTheme } = useTheme();
|
||||||
// store hooks
|
// store hooks
|
||||||
const { currentUser } = useUser();
|
const { currentUser } = useUser();
|
||||||
|
// fetch project details
|
||||||
const { data: projectDetails } = useSWR<IProject>(
|
const { data: projectDetails } = useSWR<IProject>(
|
||||||
workspaceSlug && projectId ? PROJECT_DETAILS(projectId as string) : null,
|
workspaceSlug && projectId ? PROJECT_DETAILS(projectId as string) : null,
|
||||||
workspaceSlug && projectId ? () => projectService.getProject(workspaceSlug as string, projectId as string) : null
|
workspaceSlug && projectId ? () => projectService.getProject(workspaceSlug as string, projectId as string) : null
|
||||||
);
|
);
|
||||||
|
// fetch Integrations list
|
||||||
const { data: workspaceIntegrations } = useSWR(
|
const { data: workspaceIntegrations } = useSWR(
|
||||||
workspaceSlug ? WORKSPACE_INTEGRATIONS(workspaceSlug as string) : null,
|
workspaceSlug ? WORKSPACE_INTEGRATIONS(workspaceSlug as string) : null,
|
||||||
() => (workspaceSlug ? integrationService.getWorkspaceIntegrationsList(workspaceSlug as string) : null)
|
() => (workspaceSlug ? integrationService.getWorkspaceIntegrationsList(workspaceSlug as string) : null)
|
||||||
);
|
);
|
||||||
|
// derived values
|
||||||
const emptyStateDetail = PROJECT_SETTINGS_EMPTY_STATE_DETAILS["integrations"];
|
const emptyStateDetail = PROJECT_SETTINGS_EMPTY_STATE_DETAILS["integrations"];
|
||||||
const isLightMode = resolvedTheme ? resolvedTheme === "light" : currentUser?.theme.theme === "light";
|
const isLightMode = resolvedTheme ? resolvedTheme === "light" : currentUser?.theme.theme === "light";
|
||||||
const emptyStateImage = getEmptyStateImagePath("project-settings", "integrations", isLightMode);
|
const emptyStateImage = getEmptyStateImagePath("project-settings", "integrations", isLightMode);
|
||||||
|
|
||||||
const isAdmin = projectDetails?.member_role === 20;
|
const isAdmin = projectDetails?.member_role === 20;
|
||||||
|
const pageTitle = projectDetails?.name ? `${projectDetails?.name} - Integrations` : undefined;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
|
<PageHead title={pageTitle} />
|
||||||
<div className={`h-full w-full gap-10 overflow-y-auto py-8 pr-9 ${isAdmin ? "" : "opacity-60"}`}>
|
<div className={`h-full w-full gap-10 overflow-y-auto py-8 pr-9 ${isAdmin ? "" : "opacity-60"}`}>
|
||||||
<div className="flex items-center border-b border-custom-border-100 py-3.5">
|
<div className="flex items-center border-b border-custom-border-100 py-3.5">
|
||||||
<h3 className="text-xl font-medium">Integrations</h3>
|
<h3 className="text-xl font-medium">Integrations</h3>
|
||||||
@ -82,8 +86,9 @@ const ProjectIntegrationsPage: NextPageWithLayout = () => {
|
|||||||
<IntegrationsSettingsLoader />
|
<IntegrationsSettingsLoader />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
});
|
||||||
|
|
||||||
ProjectIntegrationsPage.getLayout = function getLayout(page: ReactElement) {
|
ProjectIntegrationsPage.getLayout = function getLayout(page: ReactElement) {
|
||||||
return (
|
return (
|
||||||
|
@ -1,18 +1,30 @@
|
|||||||
import { ReactElement } from "react";
|
import { ReactElement } from "react";
|
||||||
|
import { observer } from "mobx-react";
|
||||||
// layouts
|
// layouts
|
||||||
import { AppLayout } from "layouts/app-layout";
|
import { AppLayout } from "layouts/app-layout";
|
||||||
import { ProjectSettingLayout } from "layouts/settings-layout";
|
import { ProjectSettingLayout } from "layouts/settings-layout";
|
||||||
// components
|
// components
|
||||||
|
import { PageHead } from "components/core";
|
||||||
import { ProjectSettingsLabelList } from "components/labels";
|
import { ProjectSettingsLabelList } from "components/labels";
|
||||||
import { ProjectSettingHeader } from "components/headers";
|
import { ProjectSettingHeader } from "components/headers";
|
||||||
// types
|
// types
|
||||||
import { NextPageWithLayout } from "lib/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 (
|
||||||
|
<>
|
||||||
|
<PageHead title={pageTitle} />
|
||||||
<div className="h-full w-full gap-10 overflow-y-auto py-8 pr-9">
|
<div className="h-full w-full gap-10 overflow-y-auto py-8 pr-9">
|
||||||
<ProjectSettingsLabelList />
|
<ProjectSettingsLabelList />
|
||||||
</div>
|
</div>
|
||||||
);
|
</>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
LabelsSettingsPage.getLayout = function getLayout(page: ReactElement) {
|
LabelsSettingsPage.getLayout = function getLayout(page: ReactElement) {
|
||||||
return (
|
return (
|
||||||
|
@ -1,19 +1,33 @@
|
|||||||
import { ReactElement } from "react";
|
import { ReactElement } from "react";
|
||||||
|
import { observer } from "mobx-react";
|
||||||
// layouts
|
// layouts
|
||||||
import { AppLayout } from "layouts/app-layout";
|
import { AppLayout } from "layouts/app-layout";
|
||||||
import { ProjectSettingLayout } from "layouts/settings-layout";
|
import { ProjectSettingLayout } from "layouts/settings-layout";
|
||||||
// components
|
// components
|
||||||
|
import { PageHead } from "components/core";
|
||||||
import { ProjectSettingHeader } from "components/headers";
|
import { ProjectSettingHeader } from "components/headers";
|
||||||
import { ProjectMemberList, ProjectSettingsMemberDefaults } from "components/project";
|
import { ProjectMemberList, ProjectSettingsMemberDefaults } from "components/project";
|
||||||
// types
|
// types
|
||||||
import { NextPageWithLayout } from "lib/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 (
|
||||||
|
<>
|
||||||
|
<PageHead title={pageTitle} />
|
||||||
<section className={`w-full overflow-y-auto py-8 pr-9`}>
|
<section className={`w-full overflow-y-auto py-8 pr-9`}>
|
||||||
<ProjectSettingsMemberDefaults />
|
<ProjectSettingsMemberDefaults />
|
||||||
<ProjectMemberList />
|
<ProjectMemberList />
|
||||||
</section>
|
</section>
|
||||||
);
|
</>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
MembersSettingsPage.getLayout = function getLayout(page: ReactElement) {
|
MembersSettingsPage.getLayout = function getLayout(page: ReactElement) {
|
||||||
return (
|
return (
|
||||||
|
@ -1,13 +1,15 @@
|
|||||||
import { ReactElement } from "react";
|
import { ReactElement } from "react";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
|
import { observer } from "mobx-react";
|
||||||
// hooks
|
// hooks
|
||||||
import { useProjectView } from "hooks/store";
|
import { useProject, useProjectView } from "hooks/store";
|
||||||
// layouts
|
// layouts
|
||||||
import { AppLayout } from "layouts/app-layout";
|
import { AppLayout } from "layouts/app-layout";
|
||||||
// components
|
// components
|
||||||
import { ProjectViewLayoutRoot } from "components/issues";
|
import { ProjectViewLayoutRoot } from "components/issues";
|
||||||
import { ProjectViewIssuesHeader } from "components/headers";
|
import { ProjectViewIssuesHeader } from "components/headers";
|
||||||
|
import { PageHead } from "components/core";
|
||||||
// ui
|
// ui
|
||||||
import { EmptyState } from "components/common";
|
import { EmptyState } from "components/common";
|
||||||
// assets
|
// assets
|
||||||
@ -15,12 +17,17 @@ import emptyView from "public/empty-state/view.svg";
|
|||||||
// types
|
// types
|
||||||
import { NextPageWithLayout } from "lib/types";
|
import { NextPageWithLayout } from "lib/types";
|
||||||
|
|
||||||
const ProjectViewIssuesPage: NextPageWithLayout = () => {
|
const ProjectViewIssuesPage: NextPageWithLayout = observer(() => {
|
||||||
// router
|
// router
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug, projectId, viewId } = router.query;
|
const { workspaceSlug, projectId, viewId } = router.query;
|
||||||
// store hooks
|
// 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(
|
const { error } = useSWR(
|
||||||
workspaceSlug && projectId && viewId ? `VIEW_DETAILS_${viewId.toString()}` : null,
|
workspaceSlug && projectId && viewId ? `VIEW_DETAILS_${viewId.toString()}` : null,
|
||||||
@ -42,11 +49,14 @@ const ProjectViewIssuesPage: NextPageWithLayout = () => {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
|
<>
|
||||||
|
<PageHead title={pageTitle} />
|
||||||
<ProjectViewLayoutRoot />
|
<ProjectViewLayoutRoot />
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
});
|
||||||
|
|
||||||
ProjectViewIssuesPage.getLayout = function getLayout(page: ReactElement) {
|
ProjectViewIssuesPage.getLayout = function getLayout(page: ReactElement) {
|
||||||
return (
|
return (
|
||||||
|
@ -1,13 +1,34 @@
|
|||||||
import { ReactElement } from "react";
|
import { ReactElement } from "react";
|
||||||
|
import { useRouter } from "next/router";
|
||||||
|
import { observer } from "mobx-react";
|
||||||
// components
|
// components
|
||||||
import { ProjectViewsHeader } from "components/headers";
|
import { ProjectViewsHeader } from "components/headers";
|
||||||
import { ProjectViewsList } from "components/views";
|
import { ProjectViewsList } from "components/views";
|
||||||
|
import { PageHead } from "components/core";
|
||||||
|
// hooks
|
||||||
|
import { useProject } from "hooks/store";
|
||||||
// layouts
|
// layouts
|
||||||
import { AppLayout } from "layouts/app-layout";
|
import { AppLayout } from "layouts/app-layout";
|
||||||
// types
|
// types
|
||||||
import { NextPageWithLayout } from "lib/types";
|
import { NextPageWithLayout } from "lib/types";
|
||||||
|
|
||||||
const ProjectViewsPage: NextPageWithLayout = () => <ProjectViewsList />;
|
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 (
|
||||||
|
<>
|
||||||
|
<PageHead title={pageTitle} />
|
||||||
|
<ProjectViewsList />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
ProjectViewsPage.getLayout = function getLayout(page: ReactElement) {
|
ProjectViewsPage.getLayout = function getLayout(page: ReactElement) {
|
||||||
return (
|
return (
|
||||||
|
@ -1,13 +1,28 @@
|
|||||||
import { ReactElement } from "react";
|
import { ReactElement } from "react";
|
||||||
|
import { observer } from "mobx-react";
|
||||||
// components
|
// components
|
||||||
|
import { PageHead } from "components/core";
|
||||||
import { ProjectCardList } from "components/project";
|
import { ProjectCardList } from "components/project";
|
||||||
import { ProjectsHeader } from "components/headers";
|
import { ProjectsHeader } from "components/headers";
|
||||||
// layouts
|
// layouts
|
||||||
import { AppLayout } from "layouts/app-layout";
|
import { AppLayout } from "layouts/app-layout";
|
||||||
// type
|
// type
|
||||||
import { NextPageWithLayout } from "lib/types";
|
import { NextPageWithLayout } from "lib/types";
|
||||||
|
import { useWorkspace } from "hooks/store";
|
||||||
|
|
||||||
const ProjectsPage: NextPageWithLayout = () => <ProjectCardList />;
|
const ProjectsPage: NextPageWithLayout = observer(() => {
|
||||||
|
// store
|
||||||
|
const { currentWorkspace } = useWorkspace();
|
||||||
|
// derived values
|
||||||
|
const pageTitle = currentWorkspace?.name ? `${currentWorkspace?.name} - Projects` : undefined;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<PageHead title={pageTitle} />
|
||||||
|
<ProjectCardList />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
ProjectsPage.getLayout = function getLayout(page: ReactElement) {
|
ProjectsPage.getLayout = function getLayout(page: ReactElement) {
|
||||||
return <AppLayout header={<ProjectsHeader />}>{page}</AppLayout>;
|
return <AppLayout header={<ProjectsHeader />}>{page}</AppLayout>;
|
||||||
|
@ -4,7 +4,7 @@ import { observer } from "mobx-react-lite";
|
|||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
import { useTheme } from "next-themes";
|
import { useTheme } from "next-themes";
|
||||||
// store hooks
|
// store hooks
|
||||||
import { useUser } from "hooks/store";
|
import { useUser, useWorkspace } from "hooks/store";
|
||||||
// layouts
|
// layouts
|
||||||
import { AppLayout } from "layouts/app-layout";
|
import { AppLayout } from "layouts/app-layout";
|
||||||
import { WorkspaceSettingLayout } from "layouts/settings-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 { API_TOKENS_LIST } from "constants/fetch-keys";
|
||||||
import { EUserWorkspaceRoles } from "constants/workspace";
|
import { EUserWorkspaceRoles } from "constants/workspace";
|
||||||
import { WORKSPACE_SETTINGS_EMPTY_STATE_DETAILS } from "constants/empty-state";
|
import { WORKSPACE_SETTINGS_EMPTY_STATE_DETAILS } from "constants/empty-state";
|
||||||
|
import { PageHead } from "components/core";
|
||||||
|
|
||||||
const apiTokenService = new APITokenService();
|
const apiTokenService = new APITokenService();
|
||||||
|
|
||||||
@ -39,6 +40,7 @@ const ApiTokensPage: NextPageWithLayout = observer(() => {
|
|||||||
membership: { currentWorkspaceRole },
|
membership: { currentWorkspaceRole },
|
||||||
currentUser,
|
currentUser,
|
||||||
} = useUser();
|
} = useUser();
|
||||||
|
const { currentWorkspace } = useWorkspace();
|
||||||
|
|
||||||
const isAdmin = currentWorkspaceRole === EUserWorkspaceRoles.ADMIN;
|
const isAdmin = currentWorkspaceRole === EUserWorkspaceRoles.ADMIN;
|
||||||
|
|
||||||
@ -49,12 +51,16 @@ const ApiTokensPage: NextPageWithLayout = observer(() => {
|
|||||||
const emptyStateDetail = WORKSPACE_SETTINGS_EMPTY_STATE_DETAILS["api-tokens"];
|
const emptyStateDetail = WORKSPACE_SETTINGS_EMPTY_STATE_DETAILS["api-tokens"];
|
||||||
const isLightMode = resolvedTheme ? resolvedTheme === "light" : currentUser?.theme.theme === "light";
|
const isLightMode = resolvedTheme ? resolvedTheme === "light" : currentUser?.theme.theme === "light";
|
||||||
const emptyStateImage = getEmptyStateImagePath("workspace-settings", "api-tokens", isLightMode);
|
const emptyStateImage = getEmptyStateImagePath("workspace-settings", "api-tokens", isLightMode);
|
||||||
|
const pageTitle = currentWorkspace?.name ? `${currentWorkspace.name} - API Tokens` : undefined;
|
||||||
|
|
||||||
if (!isAdmin)
|
if (!isAdmin)
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
|
<PageHead title={pageTitle} />
|
||||||
<div className="mt-10 flex h-full w-full justify-center p-4">
|
<div className="mt-10 flex h-full w-full justify-center p-4">
|
||||||
<p className="text-sm text-custom-text-300">You are not authorized to access this page.</p>
|
<p className="text-sm text-custom-text-300">You are not authorized to access this page.</p>
|
||||||
</div>
|
</div>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!tokens) {
|
if (!tokens) {
|
||||||
@ -63,6 +69,7 @@ const ApiTokensPage: NextPageWithLayout = observer(() => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
<PageHead title={pageTitle} />
|
||||||
<CreateApiTokenModal isOpen={isCreateTokenModalOpen} onClose={() => setIsCreateTokenModalOpen(false)} />
|
<CreateApiTokenModal isOpen={isCreateTokenModalOpen} onClose={() => setIsCreateTokenModalOpen(false)} />
|
||||||
<section className="h-full w-full overflow-y-auto py-8 pr-9">
|
<section className="h-full w-full overflow-y-auto py-8 pr-9">
|
||||||
{tokens.length > 0 ? (
|
{tokens.length > 0 ? (
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
// hooks
|
// hooks
|
||||||
import { useUser } from "hooks/store";
|
import { useUser, useWorkspace } from "hooks/store";
|
||||||
// layouts
|
// layouts
|
||||||
import { AppLayout } from "layouts/app-layout";
|
import { AppLayout } from "layouts/app-layout";
|
||||||
import { WorkspaceSettingLayout } from "layouts/settings-layout";
|
import { WorkspaceSettingLayout } from "layouts/settings-layout";
|
||||||
// component
|
// component
|
||||||
import { WorkspaceSettingHeader } from "components/headers";
|
import { WorkspaceSettingHeader } from "components/headers";
|
||||||
|
import { PageHead } from "components/core";
|
||||||
// ui
|
// ui
|
||||||
import { Button } from "@plane/ui";
|
import { Button } from "@plane/ui";
|
||||||
// types
|
// types
|
||||||
@ -18,17 +19,24 @@ const BillingSettingsPage: NextPageWithLayout = observer(() => {
|
|||||||
const {
|
const {
|
||||||
membership: { currentWorkspaceRole },
|
membership: { currentWorkspaceRole },
|
||||||
} = useUser();
|
} = useUser();
|
||||||
|
const { currentWorkspace } = useWorkspace();
|
||||||
|
// derived values
|
||||||
const isAdmin = currentWorkspaceRole === EUserWorkspaceRoles.ADMIN;
|
const isAdmin = currentWorkspaceRole === EUserWorkspaceRoles.ADMIN;
|
||||||
|
const pageTitle = currentWorkspace?.name ? `${currentWorkspace.name} - Billing & Plans` : undefined;
|
||||||
|
|
||||||
if (!isAdmin)
|
if (!isAdmin)
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
|
<PageHead title={pageTitle} />
|
||||||
<div className="mt-10 flex h-full w-full justify-center p-4">
|
<div className="mt-10 flex h-full w-full justify-center p-4">
|
||||||
<p className="text-sm text-custom-text-300">You are not authorized to access this page.</p>
|
<p className="text-sm text-custom-text-300">You are not authorized to access this page.</p>
|
||||||
</div>
|
</div>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
|
<PageHead title={pageTitle} />
|
||||||
<section className="w-full overflow-y-auto py-8 pr-9">
|
<section className="w-full overflow-y-auto py-8 pr-9">
|
||||||
<div>
|
<div>
|
||||||
<div className="flex items-center border-b border-custom-border-100 py-3.5">
|
<div className="flex items-center border-b border-custom-border-100 py-3.5">
|
||||||
@ -45,6 +53,7 @@ const BillingSettingsPage: NextPageWithLayout = observer(() => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
// hooks
|
// hooks
|
||||||
import { useUser } from "hooks/store";
|
import { useUser, useWorkspace } from "hooks/store";
|
||||||
// layout
|
// layout
|
||||||
import { AppLayout } from "layouts/app-layout";
|
import { AppLayout } from "layouts/app-layout";
|
||||||
import { WorkspaceSettingLayout } from "layouts/settings-layout";
|
import { WorkspaceSettingLayout } from "layouts/settings-layout";
|
||||||
// components
|
// components
|
||||||
import { WorkspaceSettingHeader } from "components/headers";
|
import { WorkspaceSettingHeader } from "components/headers";
|
||||||
import ExportGuide from "components/exporter/guide";
|
import ExportGuide from "components/exporter/guide";
|
||||||
|
import { PageHead } from "components/core";
|
||||||
// types
|
// types
|
||||||
import { NextPageWithLayout } from "lib/types";
|
import { NextPageWithLayout } from "lib/types";
|
||||||
// constants
|
// constants
|
||||||
@ -17,24 +18,33 @@ const ExportsPage: NextPageWithLayout = observer(() => {
|
|||||||
const {
|
const {
|
||||||
membership: { currentWorkspaceRole },
|
membership: { currentWorkspaceRole },
|
||||||
} = useUser();
|
} = useUser();
|
||||||
|
const { currentWorkspace } = useWorkspace();
|
||||||
|
|
||||||
|
// derived values
|
||||||
const hasPageAccess =
|
const hasPageAccess =
|
||||||
currentWorkspaceRole && [EUserWorkspaceRoles.ADMIN, EUserWorkspaceRoles.MEMBER].includes(currentWorkspaceRole);
|
currentWorkspaceRole && [EUserWorkspaceRoles.ADMIN, EUserWorkspaceRoles.MEMBER].includes(currentWorkspaceRole);
|
||||||
|
const pageTitle = currentWorkspace?.name ? `${currentWorkspace.name} - Exports` : undefined;
|
||||||
|
|
||||||
if (!hasPageAccess)
|
if (!hasPageAccess)
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
|
<PageHead title={pageTitle} />
|
||||||
<div className="mt-10 flex h-full w-full justify-center p-4">
|
<div className="mt-10 flex h-full w-full justify-center p-4">
|
||||||
<p className="text-sm text-custom-text-300">You are not authorized to access this page.</p>
|
<p className="text-sm text-custom-text-300">You are not authorized to access this page.</p>
|
||||||
</div>
|
</div>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
|
<PageHead title={pageTitle} />
|
||||||
<div className="w-full overflow-y-auto py-8 pr-9">
|
<div className="w-full overflow-y-auto py-8 pr-9">
|
||||||
<div className="flex items-center border-b border-custom-border-100 py-3.5">
|
<div className="flex items-center border-b border-custom-border-100 py-3.5">
|
||||||
<h3 className="text-xl font-medium">Exports</h3>
|
<h3 className="text-xl font-medium">Exports</h3>
|
||||||
</div>
|
</div>
|
||||||
<ExportGuide />
|
<ExportGuide />
|
||||||
</div>
|
</div>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
// hooks
|
// hooks
|
||||||
import { useUser } from "hooks/store";
|
import { useUser, useWorkspace } from "hooks/store";
|
||||||
// layouts
|
// layouts
|
||||||
import { WorkspaceSettingLayout } from "layouts/settings-layout";
|
import { WorkspaceSettingLayout } from "layouts/settings-layout";
|
||||||
import { AppLayout } from "layouts/app-layout";
|
import { AppLayout } from "layouts/app-layout";
|
||||||
// components
|
// components
|
||||||
import IntegrationGuide from "components/integration/guide";
|
import IntegrationGuide from "components/integration/guide";
|
||||||
import { WorkspaceSettingHeader } from "components/headers";
|
import { WorkspaceSettingHeader } from "components/headers";
|
||||||
|
import { PageHead } from "components/core";
|
||||||
// types
|
// types
|
||||||
import { NextPageWithLayout } from "lib/types";
|
import { NextPageWithLayout } from "lib/types";
|
||||||
// constants
|
// constants
|
||||||
@ -17,23 +18,32 @@ const ImportsPage: NextPageWithLayout = observer(() => {
|
|||||||
const {
|
const {
|
||||||
membership: { currentWorkspaceRole },
|
membership: { currentWorkspaceRole },
|
||||||
} = useUser();
|
} = useUser();
|
||||||
|
const { currentWorkspace } = useWorkspace();
|
||||||
|
|
||||||
|
// derived values
|
||||||
const isAdmin = currentWorkspaceRole === EUserWorkspaceRoles.ADMIN;
|
const isAdmin = currentWorkspaceRole === EUserWorkspaceRoles.ADMIN;
|
||||||
|
const pageTitle = currentWorkspace?.name ? `${currentWorkspace.name} - Imports` : undefined;
|
||||||
|
|
||||||
if (!isAdmin)
|
if (!isAdmin)
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
|
<PageHead title={pageTitle} />
|
||||||
<div className="mt-10 flex h-full w-full justify-center p-4">
|
<div className="mt-10 flex h-full w-full justify-center p-4">
|
||||||
<p className="text-sm text-custom-text-300">You are not authorized to access this page.</p>
|
<p className="text-sm text-custom-text-300">You are not authorized to access this page.</p>
|
||||||
</div>
|
</div>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
|
<PageHead title={pageTitle} />
|
||||||
<section className="w-full overflow-y-auto py-8 pr-9">
|
<section className="w-full overflow-y-auto py-8 pr-9">
|
||||||
<div className="flex items-center border-b border-custom-border-100 py-3.5">
|
<div className="flex items-center border-b border-custom-border-100 py-3.5">
|
||||||
<h3 className="text-xl font-medium">Imports</h3>
|
<h3 className="text-xl font-medium">Imports</h3>
|
||||||
</div>
|
</div>
|
||||||
<IntegrationGuide />
|
<IntegrationGuide />
|
||||||
</section>
|
</section>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,14 +1,30 @@
|
|||||||
import { ReactElement } from "react";
|
import { ReactElement } from "react";
|
||||||
|
import { observer } from "mobx-react";
|
||||||
// layouts
|
// layouts
|
||||||
import { AppLayout } from "layouts/app-layout";
|
import { AppLayout } from "layouts/app-layout";
|
||||||
import { WorkspaceSettingLayout } from "layouts/settings-layout";
|
import { WorkspaceSettingLayout } from "layouts/settings-layout";
|
||||||
|
// hooks
|
||||||
|
import { useWorkspace } from "hooks/store";
|
||||||
// components
|
// components
|
||||||
import { WorkspaceSettingHeader } from "components/headers";
|
import { WorkspaceSettingHeader } from "components/headers";
|
||||||
import { WorkspaceDetails } from "components/workspace";
|
import { WorkspaceDetails } from "components/workspace";
|
||||||
|
import { PageHead } from "components/core";
|
||||||
// types
|
// types
|
||||||
import { NextPageWithLayout } from "lib/types";
|
import { NextPageWithLayout } from "lib/types";
|
||||||
|
|
||||||
const WorkspaceSettingsPage: NextPageWithLayout = () => <WorkspaceDetails />;
|
const WorkspaceSettingsPage: NextPageWithLayout = observer(() => {
|
||||||
|
// store hooks
|
||||||
|
const { currentWorkspace } = useWorkspace();
|
||||||
|
// derived values
|
||||||
|
const pageTitle = currentWorkspace?.name ? `${currentWorkspace.name} - General Settings` : undefined;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<PageHead title={pageTitle} />
|
||||||
|
<WorkspaceDetails />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
WorkspaceSettingsPage.getLayout = function getLayout(page: ReactElement) {
|
WorkspaceSettingsPage.getLayout = function getLayout(page: ReactElement) {
|
||||||
return (
|
return (
|
||||||
|
@ -3,7 +3,7 @@ import { useRouter } from "next/router";
|
|||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
// hooks
|
// hooks
|
||||||
import { useUser } from "hooks/store";
|
import { useUser, useWorkspace } from "hooks/store";
|
||||||
// services
|
// services
|
||||||
import { IntegrationService } from "services/integrations";
|
import { IntegrationService } from "services/integrations";
|
||||||
// layouts
|
// layouts
|
||||||
@ -12,6 +12,7 @@ import { WorkspaceSettingLayout } from "layouts/settings-layout";
|
|||||||
// components
|
// components
|
||||||
import { SingleIntegrationCard } from "components/integration";
|
import { SingleIntegrationCard } from "components/integration";
|
||||||
import { WorkspaceSettingHeader } from "components/headers";
|
import { WorkspaceSettingHeader } from "components/headers";
|
||||||
|
import { PageHead } from "components/core";
|
||||||
// ui
|
// ui
|
||||||
import { IntegrationAndImportExportBanner, IntegrationsSettingsLoader } from "components/ui";
|
import { IntegrationAndImportExportBanner, IntegrationsSettingsLoader } from "components/ui";
|
||||||
// types
|
// types
|
||||||
@ -31,14 +32,20 @@ const WorkspaceIntegrationsPage: NextPageWithLayout = observer(() => {
|
|||||||
const {
|
const {
|
||||||
membership: { currentWorkspaceRole },
|
membership: { currentWorkspaceRole },
|
||||||
} = useUser();
|
} = useUser();
|
||||||
|
const { currentWorkspace } = useWorkspace();
|
||||||
|
|
||||||
|
// derived values
|
||||||
const isAdmin = currentWorkspaceRole === EUserWorkspaceRoles.ADMIN;
|
const isAdmin = currentWorkspaceRole === EUserWorkspaceRoles.ADMIN;
|
||||||
|
const pageTitle = currentWorkspace?.name ? `${currentWorkspace.name} - Integrations` : undefined;
|
||||||
|
|
||||||
if (!isAdmin)
|
if (!isAdmin)
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
|
<PageHead title={pageTitle} />
|
||||||
<div className="mt-10 flex h-full w-full justify-center p-4">
|
<div className="mt-10 flex h-full w-full justify-center p-4">
|
||||||
<p className="text-sm text-custom-text-300">You are not authorized to access this page.</p>
|
<p className="text-sm text-custom-text-300">You are not authorized to access this page.</p>
|
||||||
</div>
|
</div>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
|
|
||||||
const { data: appIntegrations } = useSWR(workspaceSlug && isAdmin ? APP_INTEGRATIONS : null, () =>
|
const { data: appIntegrations } = useSWR(workspaceSlug && isAdmin ? APP_INTEGRATIONS : null, () =>
|
||||||
@ -46,16 +53,21 @@ const WorkspaceIntegrationsPage: NextPageWithLayout = observer(() => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
|
<PageHead title={pageTitle} />
|
||||||
<section className="w-full overflow-y-auto py-8 pr-9">
|
<section className="w-full overflow-y-auto py-8 pr-9">
|
||||||
<IntegrationAndImportExportBanner bannerName="Integrations" />
|
<IntegrationAndImportExportBanner bannerName="Integrations" />
|
||||||
<div>
|
<div>
|
||||||
{appIntegrations ? (
|
{appIntegrations ? (
|
||||||
appIntegrations.map((integration) => <SingleIntegrationCard key={integration.id} integration={integration} />)
|
appIntegrations.map((integration) => (
|
||||||
|
<SingleIntegrationCard key={integration.id} integration={integration} />
|
||||||
|
))
|
||||||
) : (
|
) : (
|
||||||
<IntegrationsSettingsLoader />
|
<IntegrationsSettingsLoader />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ import { useRouter } from "next/router";
|
|||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { Search } from "lucide-react";
|
import { Search } from "lucide-react";
|
||||||
// hooks
|
// hooks
|
||||||
import { useEventTracker, useMember, useUser } from "hooks/store";
|
import { useEventTracker, useMember, useUser, useWorkspace } from "hooks/store";
|
||||||
import useToast from "hooks/use-toast";
|
import useToast from "hooks/use-toast";
|
||||||
// layouts
|
// layouts
|
||||||
import { AppLayout } from "layouts/app-layout";
|
import { AppLayout } from "layouts/app-layout";
|
||||||
@ -11,6 +11,7 @@ import { WorkspaceSettingLayout } from "layouts/settings-layout";
|
|||||||
// components
|
// components
|
||||||
import { WorkspaceSettingHeader } from "components/headers";
|
import { WorkspaceSettingHeader } from "components/headers";
|
||||||
import { SendWorkspaceInvitationModal, WorkspaceMembersList } from "components/workspace";
|
import { SendWorkspaceInvitationModal, WorkspaceMembersList } from "components/workspace";
|
||||||
|
import { PageHead } from "components/core";
|
||||||
// ui
|
// ui
|
||||||
import { Button } from "@plane/ui";
|
import { Button } from "@plane/ui";
|
||||||
// types
|
// types
|
||||||
@ -37,6 +38,7 @@ const WorkspaceMembersSettingsPage: NextPageWithLayout = observer(() => {
|
|||||||
const {
|
const {
|
||||||
workspace: { inviteMembersToWorkspace },
|
workspace: { inviteMembersToWorkspace },
|
||||||
} = useMember();
|
} = useMember();
|
||||||
|
const { currentWorkspace } = useWorkspace();
|
||||||
// toast alert
|
// toast alert
|
||||||
const { setToastAlert } = useToast();
|
const { setToastAlert } = useToast();
|
||||||
|
|
||||||
@ -83,11 +85,14 @@ const WorkspaceMembersSettingsPage: NextPageWithLayout = observer(() => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// derived values
|
||||||
const hasAddMemberPermission =
|
const hasAddMemberPermission =
|
||||||
currentWorkspaceRole && [EUserWorkspaceRoles.ADMIN, EUserWorkspaceRoles.MEMBER].includes(currentWorkspaceRole);
|
currentWorkspaceRole && [EUserWorkspaceRoles.ADMIN, EUserWorkspaceRoles.MEMBER].includes(currentWorkspaceRole);
|
||||||
|
const pageTitle = currentWorkspace?.name ? `${currentWorkspace.name} - Members` : undefined;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
<PageHead title={pageTitle} />
|
||||||
<SendWorkspaceInvitationModal
|
<SendWorkspaceInvitationModal
|
||||||
isOpen={inviteModal}
|
isOpen={inviteModal}
|
||||||
onClose={() => setInviteModal(false)}
|
onClose={() => setInviteModal(false)}
|
||||||
|
@ -3,7 +3,7 @@ import { useRouter } from "next/router";
|
|||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
// hooks
|
// hooks
|
||||||
import { useUser, useWebhook } from "hooks/store";
|
import { useUser, useWebhook, useWorkspace } from "hooks/store";
|
||||||
// layouts
|
// layouts
|
||||||
import { AppLayout } from "layouts/app-layout";
|
import { AppLayout } from "layouts/app-layout";
|
||||||
import { WorkspaceSettingLayout } from "layouts/settings-layout";
|
import { WorkspaceSettingLayout } from "layouts/settings-layout";
|
||||||
@ -12,6 +12,7 @@ import useToast from "hooks/use-toast";
|
|||||||
// components
|
// components
|
||||||
import { WorkspaceSettingHeader } from "components/headers";
|
import { WorkspaceSettingHeader } from "components/headers";
|
||||||
import { DeleteWebhookModal, WebhookDeleteSection, WebhookForm } from "components/web-hooks";
|
import { DeleteWebhookModal, WebhookDeleteSection, WebhookForm } from "components/web-hooks";
|
||||||
|
import { PageHead } from "components/core";
|
||||||
// ui
|
// ui
|
||||||
import { Spinner } from "@plane/ui";
|
import { Spinner } from "@plane/ui";
|
||||||
// types
|
// types
|
||||||
@ -29,6 +30,7 @@ const WebhookDetailsPage: NextPageWithLayout = observer(() => {
|
|||||||
membership: { currentWorkspaceRole },
|
membership: { currentWorkspaceRole },
|
||||||
} = useUser();
|
} = useUser();
|
||||||
const { currentWebhook, fetchWebhookById, updateWebhook } = useWebhook();
|
const { currentWebhook, fetchWebhookById, updateWebhook } = useWebhook();
|
||||||
|
const { currentWorkspace } = useWorkspace();
|
||||||
// toast
|
// toast
|
||||||
const { setToastAlert } = useToast();
|
const { setToastAlert } = useToast();
|
||||||
|
|
||||||
@ -38,6 +40,7 @@ const WebhookDetailsPage: NextPageWithLayout = observer(() => {
|
|||||||
// }, [clearSecretKey, isCreated]);
|
// }, [clearSecretKey, isCreated]);
|
||||||
|
|
||||||
const isAdmin = currentWorkspaceRole === 20;
|
const isAdmin = currentWorkspaceRole === 20;
|
||||||
|
const pageTitle = currentWorkspace?.name ? `${currentWorkspace.name} - Webhook` : undefined;
|
||||||
|
|
||||||
useSWR(
|
useSWR(
|
||||||
workspaceSlug && webhookId && isAdmin ? `WEBHOOK_DETAILS_${workspaceSlug}_${webhookId}` : null,
|
workspaceSlug && webhookId && isAdmin ? `WEBHOOK_DETAILS_${workspaceSlug}_${webhookId}` : null,
|
||||||
@ -76,9 +79,12 @@ const WebhookDetailsPage: NextPageWithLayout = observer(() => {
|
|||||||
|
|
||||||
if (!isAdmin)
|
if (!isAdmin)
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
|
<PageHead title={pageTitle} />
|
||||||
<div className="mt-10 flex h-full w-full justify-center p-4">
|
<div className="mt-10 flex h-full w-full justify-center p-4">
|
||||||
<p className="text-sm text-custom-text-300">You are not authorized to access this page.</p>
|
<p className="text-sm text-custom-text-300">You are not authorized to access this page.</p>
|
||||||
</div>
|
</div>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!currentWebhook)
|
if (!currentWebhook)
|
||||||
@ -90,6 +96,7 @@ const WebhookDetailsPage: NextPageWithLayout = observer(() => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
<PageHead title={pageTitle} />
|
||||||
<DeleteWebhookModal isOpen={deleteWebhookModal} onClose={() => setDeleteWebhookModal(false)} />
|
<DeleteWebhookModal isOpen={deleteWebhookModal} onClose={() => setDeleteWebhookModal(false)} />
|
||||||
<div className="w-full space-y-8 overflow-y-auto py-8 pr-9">
|
<div className="w-full space-y-8 overflow-y-auto py-8 pr-9">
|
||||||
<WebhookForm onSubmit={async (data) => await handleUpdateWebhook(data)} data={currentWebhook} />
|
<WebhookForm onSubmit={async (data) => await handleUpdateWebhook(data)} data={currentWebhook} />
|
||||||
|
@ -19,6 +19,7 @@ import { WebhookSettingsLoader } from "components/ui";
|
|||||||
import { NextPageWithLayout } from "lib/types";
|
import { NextPageWithLayout } from "lib/types";
|
||||||
// constants
|
// constants
|
||||||
import { WORKSPACE_SETTINGS_EMPTY_STATE_DETAILS } from "constants/empty-state";
|
import { WORKSPACE_SETTINGS_EMPTY_STATE_DETAILS } from "constants/empty-state";
|
||||||
|
import { PageHead } from "components/core";
|
||||||
|
|
||||||
const WebhooksListPage: NextPageWithLayout = observer(() => {
|
const WebhooksListPage: NextPageWithLayout = observer(() => {
|
||||||
// states
|
// states
|
||||||
@ -47,6 +48,7 @@ const WebhooksListPage: NextPageWithLayout = observer(() => {
|
|||||||
|
|
||||||
const isLightMode = resolvedTheme ? resolvedTheme === "light" : currentUser?.theme.theme === "light";
|
const isLightMode = resolvedTheme ? resolvedTheme === "light" : currentUser?.theme.theme === "light";
|
||||||
const emptyStateImage = getEmptyStateImagePath("workspace-settings", "webhooks", isLightMode);
|
const emptyStateImage = getEmptyStateImagePath("workspace-settings", "webhooks", isLightMode);
|
||||||
|
const pageTitle = currentWorkspace?.name ? `${currentWorkspace.name} - Webhooks` : undefined;
|
||||||
|
|
||||||
// clear secret key when modal is closed.
|
// clear secret key when modal is closed.
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -55,14 +57,19 @@ const WebhooksListPage: NextPageWithLayout = observer(() => {
|
|||||||
|
|
||||||
if (!isAdmin)
|
if (!isAdmin)
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
|
<PageHead title={pageTitle} />
|
||||||
<div className="mt-10 flex h-full w-full justify-center p-4">
|
<div className="mt-10 flex h-full w-full justify-center p-4">
|
||||||
<p className="text-sm text-custom-text-300">You are not authorized to access this page.</p>
|
<p className="text-sm text-custom-text-300">You are not authorized to access this page.</p>
|
||||||
</div>
|
</div>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!webhooks) return <WebhookSettingsLoader />;
|
if (!webhooks) return <WebhookSettingsLoader />;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
|
<PageHead title={pageTitle} />
|
||||||
<div className="h-full w-full overflow-hidden py-8 pr-9">
|
<div className="h-full w-full overflow-hidden py-8 pr-9">
|
||||||
<CreateWebhookModal
|
<CreateWebhookModal
|
||||||
createWebhook={createWebhook}
|
createWebhook={createWebhook}
|
||||||
@ -102,6 +109,7 @@ const WebhooksListPage: NextPageWithLayout = observer(() => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -7,15 +7,26 @@ import { AllIssueLayoutRoot } from "components/issues";
|
|||||||
import { GlobalIssuesHeader } from "components/headers";
|
import { GlobalIssuesHeader } from "components/headers";
|
||||||
// types
|
// types
|
||||||
import { NextPageWithLayout } from "lib/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 (
|
||||||
|
<>
|
||||||
|
<PageHead title={pageTitle} />
|
||||||
<div className="h-full overflow-hidden bg-custom-background-100">
|
<div className="h-full overflow-hidden bg-custom-background-100">
|
||||||
<div className="flex h-full w-full flex-col border-b border-custom-border-300">
|
<div className="flex h-full w-full flex-col border-b border-custom-border-300">
|
||||||
<GlobalViewsHeader />
|
<GlobalViewsHeader />
|
||||||
<AllIssueLayoutRoot />
|
<AllIssueLayoutRoot />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
</>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
GlobalViewIssuesPage.getLayout = function getLayout(page: ReactElement) {
|
GlobalViewIssuesPage.getLayout = function getLayout(page: ReactElement) {
|
||||||
return <AppLayout header={<GlobalIssuesHeader activeLayout="spreadsheet" />}>{page}</AppLayout>;
|
return <AppLayout header={<GlobalIssuesHeader activeLayout="spreadsheet" />}>{page}</AppLayout>;
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
import React, { useState, ReactElement } from "react";
|
import React, { useState, ReactElement } from "react";
|
||||||
|
import { observer } from "mobx-react";
|
||||||
// layouts
|
// layouts
|
||||||
import { AppLayout } from "layouts/app-layout";
|
import { AppLayout } from "layouts/app-layout";
|
||||||
// components
|
// components
|
||||||
|
import { PageHead } from "components/core";
|
||||||
import { GlobalDefaultViewListItem, GlobalViewsList } from "components/workspace";
|
import { GlobalDefaultViewListItem, GlobalViewsList } from "components/workspace";
|
||||||
import { GlobalIssuesHeader } from "components/headers";
|
import { GlobalIssuesHeader } from "components/headers";
|
||||||
// ui
|
// ui
|
||||||
@ -12,11 +14,19 @@ import { Search } from "lucide-react";
|
|||||||
import { NextPageWithLayout } from "lib/types";
|
import { NextPageWithLayout } from "lib/types";
|
||||||
// constants
|
// constants
|
||||||
import { DEFAULT_GLOBAL_VIEWS_LIST } from "constants/workspace";
|
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("");
|
const [query, setQuery] = useState("");
|
||||||
|
// store
|
||||||
|
const { currentWorkspace } = useWorkspace();
|
||||||
|
// derived values
|
||||||
|
const pageTitle = currentWorkspace?.name ? `${currentWorkspace?.name} - All Views` : undefined;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
|
<PageHead title={pageTitle} />
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<div className="flex h-full w-full flex-col overflow-hidden">
|
<div className="flex h-full w-full flex-col overflow-hidden">
|
||||||
<div className="flex w-full items-center gap-2.5 border-b border-custom-border-200 px-5 py-3">
|
<div className="flex w-full items-center gap-2.5 border-b border-custom-border-200 px-5 py-3">
|
||||||
@ -35,8 +45,9 @@ const WorkspaceViewsPage: NextPageWithLayout = () => {
|
|||||||
))}
|
))}
|
||||||
<GlobalViewsList searchQuery={query} />
|
<GlobalViewsList searchQuery={query} />
|
||||||
</div>
|
</div>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
});
|
||||||
|
|
||||||
WorkspaceViewsPage.getLayout = function getLayout(page: ReactElement) {
|
WorkspaceViewsPage.getLayout = function getLayout(page: ReactElement) {
|
||||||
return <AppLayout header={<GlobalIssuesHeader activeLayout="list" />}>{page}</AppLayout>;
|
return <AppLayout header={<GlobalIssuesHeader activeLayout="list" />}>{page}</AppLayout>;
|
||||||
|
@ -12,6 +12,7 @@ import { useEventTracker } from "hooks/store";
|
|||||||
import DefaultLayout from "layouts/default-layout";
|
import DefaultLayout from "layouts/default-layout";
|
||||||
// components
|
// components
|
||||||
import { LatestFeatureBlock } from "components/common";
|
import { LatestFeatureBlock } from "components/common";
|
||||||
|
import { PageHead } from "components/core";
|
||||||
// ui
|
// ui
|
||||||
import { Button, Input } from "@plane/ui";
|
import { Button, Input } from "@plane/ui";
|
||||||
// images
|
// images
|
||||||
@ -85,6 +86,8 @@ const ForgotPasswordPage: NextPageWithLayout = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
|
<PageHead title="Forgot Password" />
|
||||||
<div className="h-full w-full bg-onboarding-gradient-100">
|
<div className="h-full w-full bg-onboarding-gradient-100">
|
||||||
<div className="flex items-center justify-between px-8 pb-4 sm:px-16 sm:py-5 lg:px-28 ">
|
<div className="flex items-center justify-between px-8 pb-4 sm:px-16 sm:py-5 lg:px-28 ">
|
||||||
<div className="flex items-center gap-x-2 py-10">
|
<div className="flex items-center gap-x-2 py-10">
|
||||||
@ -138,6 +141,7 @@ const ForgotPasswordPage: NextPageWithLayout = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@ import { useEventTracker } from "hooks/store";
|
|||||||
import DefaultLayout from "layouts/default-layout";
|
import DefaultLayout from "layouts/default-layout";
|
||||||
// components
|
// components
|
||||||
import { LatestFeatureBlock } from "components/common";
|
import { LatestFeatureBlock } from "components/common";
|
||||||
|
import { PageHead } from "components/core";
|
||||||
// ui
|
// ui
|
||||||
import { Button, Input } from "@plane/ui";
|
import { Button, Input } from "@plane/ui";
|
||||||
// images
|
// images
|
||||||
@ -90,6 +91,8 @@ const ResetPasswordPage: NextPageWithLayout = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
|
<PageHead title="Reset Password" />
|
||||||
<div className="h-full w-full bg-onboarding-gradient-100">
|
<div className="h-full w-full bg-onboarding-gradient-100">
|
||||||
<div className="flex items-center justify-between px-8 pb-4 sm:px-16 sm:py-5 lg:px-28 ">
|
<div className="flex items-center justify-between px-8 pb-4 sm:px-16 sm:py-5 lg:px-28 ">
|
||||||
<div className="flex items-center gap-x-2 py-10">
|
<div className="flex items-center gap-x-2 py-10">
|
||||||
@ -174,6 +177,7 @@ const ResetPasswordPage: NextPageWithLayout = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ import { useApplication, useUser } from "hooks/store";
|
|||||||
import DefaultLayout from "layouts/default-layout";
|
import DefaultLayout from "layouts/default-layout";
|
||||||
// components
|
// components
|
||||||
import { SignUpRoot } from "components/account";
|
import { SignUpRoot } from "components/account";
|
||||||
|
import { PageHead } from "components/core";
|
||||||
// ui
|
// ui
|
||||||
import { Spinner } from "@plane/ui";
|
import { Spinner } from "@plane/ui";
|
||||||
// assets
|
// assets
|
||||||
@ -29,6 +30,8 @@ const SignUpPage: NextPageWithLayout = observer(() => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
|
<PageHead title="Sign Up" />
|
||||||
<div className="h-full w-full bg-onboarding-gradient-100">
|
<div className="h-full w-full bg-onboarding-gradient-100">
|
||||||
<div className="flex items-center justify-between px-8 pb-4 sm:px-16 sm:py-5 lg:px-28">
|
<div className="flex items-center justify-between px-8 pb-4 sm:px-16 sm:py-5 lg:px-28">
|
||||||
<div className="flex items-center gap-x-2 py-10">
|
<div className="flex items-center gap-x-2 py-10">
|
||||||
@ -43,6 +46,7 @@ const SignUpPage: NextPageWithLayout = observer(() => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@ import DefaultLayout from "layouts/default-layout";
|
|||||||
import { UserAuthWrapper } from "layouts/auth-layout";
|
import { UserAuthWrapper } from "layouts/auth-layout";
|
||||||
// components
|
// components
|
||||||
import { CreateWorkspaceForm } from "components/workspace";
|
import { CreateWorkspaceForm } from "components/workspace";
|
||||||
|
import { PageHead } from "components/core";
|
||||||
// images
|
// images
|
||||||
import BlackHorizontalLogo from "public/plane-logos/black-horizontal-with-blue-logo.svg";
|
import BlackHorizontalLogo from "public/plane-logos/black-horizontal-with-blue-logo.svg";
|
||||||
import WhiteHorizontalLogo from "public/plane-logos/white-horizontal-with-blue-logo.svg";
|
import WhiteHorizontalLogo from "public/plane-logos/white-horizontal-with-blue-logo.svg";
|
||||||
@ -37,6 +38,8 @@ const CreateWorkspacePage: NextPageWithLayout = observer(() => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
|
<PageHead title="Create Workspace" />
|
||||||
<div className="flex h-full flex-col gap-y-2 overflow-hidden sm:flex-row sm:gap-y-0">
|
<div className="flex h-full flex-col gap-y-2 overflow-hidden sm:flex-row sm:gap-y-0">
|
||||||
<div className="relative h-1/6 flex-shrink-0 sm:w-2/12 md:w-3/12 lg:w-1/5">
|
<div className="relative h-1/6 flex-shrink-0 sm:w-2/12 md:w-3/12 lg:w-1/5">
|
||||||
<div className="absolute left-0 top-1/2 h-[0.5px] w-full -translate-y-1/2 border-b-[0.5px] border-custom-border-200 sm:left-1/2 sm:top-0 sm:h-screen sm:w-[0.5px] sm:-translate-x-1/2 sm:translate-y-0 sm:border-r-[0.5px] md:left-1/3" />
|
<div className="absolute left-0 top-1/2 h-[0.5px] w-full -translate-y-1/2 border-b-[0.5px] border-custom-border-200 sm:left-1/2 sm:top-0 sm:h-screen sm:w-[0.5px] sm:-translate-x-1/2 sm:translate-y-0 sm:border-r-[0.5px] md:left-1/3" />
|
||||||
@ -69,6 +72,7 @@ const CreateWorkspacePage: NextPageWithLayout = observer(() => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@ import { Loader } from "@plane/ui";
|
|||||||
import { Lightbulb } from "lucide-react";
|
import { Lightbulb } from "lucide-react";
|
||||||
// components
|
// components
|
||||||
import { InstanceAIForm } from "components/instance";
|
import { InstanceAIForm } from "components/instance";
|
||||||
|
import { PageHead } from "components/core";
|
||||||
|
|
||||||
const InstanceAdminAIPage: NextPageWithLayout = observer(() => {
|
const InstanceAdminAIPage: NextPageWithLayout = observer(() => {
|
||||||
// store
|
// store
|
||||||
@ -23,6 +24,8 @@ const InstanceAdminAIPage: NextPageWithLayout = observer(() => {
|
|||||||
useSWR("INSTANCE_CONFIGURATIONS", () => fetchInstanceConfigurations());
|
useSWR("INSTANCE_CONFIGURATIONS", () => fetchInstanceConfigurations());
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
|
<PageHead title="God Mode - AI" />
|
||||||
<div className="flex flex-col gap-8">
|
<div className="flex flex-col gap-8">
|
||||||
<div className="mb-2 border-b border-custom-border-100 pb-3">
|
<div className="mb-2 border-b border-custom-border-100 pb-3">
|
||||||
<div className="pb-1 text-xl font-medium text-custom-text-100">AI features for all your workspaces</div>
|
<div className="pb-1 text-xl font-medium text-custom-text-100">AI features for all your workspaces</div>
|
||||||
@ -54,6 +57,7 @@ const InstanceAdminAIPage: NextPageWithLayout = observer(() => {
|
|||||||
</Loader>
|
</Loader>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@ import useToast from "hooks/use-toast";
|
|||||||
import { Loader, ToggleSwitch } from "@plane/ui";
|
import { Loader, ToggleSwitch } from "@plane/ui";
|
||||||
// components
|
// components
|
||||||
import { InstanceGithubConfigForm, InstanceGoogleConfigForm } from "components/instance";
|
import { InstanceGithubConfigForm, InstanceGoogleConfigForm } from "components/instance";
|
||||||
|
import { PageHead } from "components/core";
|
||||||
|
|
||||||
const InstanceAdminAuthorizationPage: NextPageWithLayout = observer(() => {
|
const InstanceAdminAuthorizationPage: NextPageWithLayout = observer(() => {
|
||||||
// store
|
// store
|
||||||
@ -64,6 +65,8 @@ const InstanceAdminAuthorizationPage: NextPageWithLayout = observer(() => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
|
<PageHead title="God Mode - SSO and OAuth" />
|
||||||
<div className="flex flex-col gap-8">
|
<div className="flex flex-col gap-8">
|
||||||
<div className="mb-2 border-b border-custom-border-100 pb-3">
|
<div className="mb-2 border-b border-custom-border-100 pb-3">
|
||||||
<div className="pb-1 text-xl font-medium text-custom-text-100">Single sign-on and OAuth</div>
|
<div className="pb-1 text-xl font-medium text-custom-text-100">Single sign-on and OAuth</div>
|
||||||
@ -176,6 +179,7 @@ const InstanceAdminAuthorizationPage: NextPageWithLayout = observer(() => {
|
|||||||
</Loader>
|
</Loader>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@ import { useApplication } from "hooks/store";
|
|||||||
import { Loader } from "@plane/ui";
|
import { Loader } from "@plane/ui";
|
||||||
// components
|
// components
|
||||||
import { InstanceEmailForm } from "components/instance";
|
import { InstanceEmailForm } from "components/instance";
|
||||||
|
import { PageHead } from "components/core";
|
||||||
|
|
||||||
const InstanceAdminEmailPage: NextPageWithLayout = observer(() => {
|
const InstanceAdminEmailPage: NextPageWithLayout = observer(() => {
|
||||||
// store
|
// store
|
||||||
@ -21,6 +22,8 @@ const InstanceAdminEmailPage: NextPageWithLayout = observer(() => {
|
|||||||
useSWR("INSTANCE_CONFIGURATIONS", () => fetchInstanceConfigurations());
|
useSWR("INSTANCE_CONFIGURATIONS", () => fetchInstanceConfigurations());
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
|
<PageHead title="God Mode - Email" />
|
||||||
<div className="flex flex-col gap-8">
|
<div className="flex flex-col gap-8">
|
||||||
<div className="mb-2 border-b border-custom-border-100 pb-3">
|
<div className="mb-2 border-b border-custom-border-100 pb-3">
|
||||||
<div className="pb-1 text-xl font-medium text-custom-text-100">Secure emails from your own instance</div>
|
<div className="pb-1 text-xl font-medium text-custom-text-100">Secure emails from your own instance</div>
|
||||||
@ -44,6 +47,7 @@ const InstanceAdminEmailPage: NextPageWithLayout = observer(() => {
|
|||||||
</Loader>
|
</Loader>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@ import { useApplication } from "hooks/store";
|
|||||||
import { Loader } from "@plane/ui";
|
import { Loader } from "@plane/ui";
|
||||||
// components
|
// components
|
||||||
import { InstanceImageConfigForm } from "components/instance";
|
import { InstanceImageConfigForm } from "components/instance";
|
||||||
|
import { PageHead } from "components/core";
|
||||||
|
|
||||||
const InstanceAdminImagePage: NextPageWithLayout = observer(() => {
|
const InstanceAdminImagePage: NextPageWithLayout = observer(() => {
|
||||||
// store
|
// store
|
||||||
@ -21,6 +22,8 @@ const InstanceAdminImagePage: NextPageWithLayout = observer(() => {
|
|||||||
useSWR("INSTANCE_CONFIGURATIONS", () => fetchInstanceConfigurations());
|
useSWR("INSTANCE_CONFIGURATIONS", () => fetchInstanceConfigurations());
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
|
<PageHead title="God Mode - Images" />
|
||||||
<div className="flex flex-col gap-8">
|
<div className="flex flex-col gap-8">
|
||||||
<div className="mb-2 border-b border-custom-border-100 pb-3">
|
<div className="mb-2 border-b border-custom-border-100 pb-3">
|
||||||
<div className="pb-1 text-xl font-medium text-custom-text-100">Third-party image libraries</div>
|
<div className="pb-1 text-xl font-medium text-custom-text-100">Third-party image libraries</div>
|
||||||
@ -40,6 +43,7 @@ const InstanceAdminImagePage: NextPageWithLayout = observer(() => {
|
|||||||
</Loader>
|
</Loader>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@ import { useApplication } from "hooks/store";
|
|||||||
import { Loader } from "@plane/ui";
|
import { Loader } from "@plane/ui";
|
||||||
// components
|
// components
|
||||||
import { InstanceGeneralForm } from "components/instance";
|
import { InstanceGeneralForm } from "components/instance";
|
||||||
|
import { PageHead } from "components/core";
|
||||||
|
|
||||||
const InstanceAdminPage: NextPageWithLayout = observer(() => {
|
const InstanceAdminPage: NextPageWithLayout = observer(() => {
|
||||||
// store hooks
|
// store hooks
|
||||||
@ -22,6 +23,8 @@ const InstanceAdminPage: NextPageWithLayout = observer(() => {
|
|||||||
useSWR("INSTANCE_ADMINS", () => fetchInstanceAdmins());
|
useSWR("INSTANCE_ADMINS", () => fetchInstanceAdmins());
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
|
<PageHead title="God Mode - General Settings" />
|
||||||
<div className="flex h-full w-full flex-col gap-8">
|
<div className="flex h-full w-full flex-col gap-8">
|
||||||
<div className="mb-2 border-b border-custom-border-100 pb-3">
|
<div className="mb-2 border-b border-custom-border-100 pb-3">
|
||||||
<div className="pb-1 text-xl font-medium text-custom-text-100">ID your instance easily</div>
|
<div className="pb-1 text-xl font-medium text-custom-text-100">ID your instance easily</div>
|
||||||
@ -42,6 +45,7 @@ const InstanceAdminPage: NextPageWithLayout = observer(() => {
|
|||||||
</Loader>
|
</Loader>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ import { ROLE } from "constants/workspace";
|
|||||||
import { MEMBER_ACCEPTED } from "constants/event-tracker";
|
import { MEMBER_ACCEPTED } from "constants/event-tracker";
|
||||||
// components
|
// components
|
||||||
import { EmptyState } from "components/common";
|
import { EmptyState } from "components/common";
|
||||||
|
import { PageHead } from "components/core";
|
||||||
// services
|
// services
|
||||||
const workspaceService = new WorkspaceService();
|
const workspaceService = new WorkspaceService();
|
||||||
const userService = new UserService();
|
const userService = new UserService();
|
||||||
@ -126,6 +126,8 @@ const UserInvitationsPage: NextPageWithLayout = observer(() => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
|
<PageHead title="Invitations" />
|
||||||
<div className="flex h-full flex-col gap-y-2 overflow-hidden sm:flex-row sm:gap-y-0">
|
<div className="flex h-full flex-col gap-y-2 overflow-hidden sm:flex-row sm:gap-y-0">
|
||||||
<div className="relative h-1/6 flex-shrink-0 sm:w-2/12 md:w-3/12 lg:w-1/5">
|
<div className="relative h-1/6 flex-shrink-0 sm:w-2/12 md:w-3/12 lg:w-1/5">
|
||||||
<div className="absolute left-0 top-1/2 h-[0.5px] w-full -translate-y-1/2 border-b-[0.5px] border-custom-border-200 sm:left-1/2 sm:top-0 sm:h-screen sm:w-[0.5px] sm:-translate-x-1/2 sm:translate-y-0 sm:border-r-[0.5px] md:left-1/3" />
|
<div className="absolute left-0 top-1/2 h-[0.5px] w-full -translate-y-1/2 border-b-[0.5px] border-custom-border-200 sm:left-1/2 sm:top-0 sm:h-screen sm:w-[0.5px] sm:-translate-x-1/2 sm:translate-y-0 sm:border-r-[0.5px] md:left-1/3" />
|
||||||
@ -228,6 +230,7 @@ const UserInvitationsPage: NextPageWithLayout = observer(() => {
|
|||||||
)
|
)
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@ import DefaultLayout from "layouts/default-layout";
|
|||||||
import { UserAuthWrapper } from "layouts/auth-layout";
|
import { UserAuthWrapper } from "layouts/auth-layout";
|
||||||
// components
|
// components
|
||||||
import { InviteMembers, JoinWorkspaces, UserDetails, SwitchOrDeleteAccountModal } from "components/onboarding";
|
import { InviteMembers, JoinWorkspaces, UserDetails, SwitchOrDeleteAccountModal } from "components/onboarding";
|
||||||
|
import { PageHead } from "components/core";
|
||||||
// ui
|
// ui
|
||||||
import { Avatar, Spinner } from "@plane/ui";
|
import { Avatar, Spinner } from "@plane/ui";
|
||||||
// images
|
// images
|
||||||
@ -142,6 +143,7 @@ const OnboardingPage: NextPageWithLayout = observer(() => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
<PageHead title="Onboarding" />
|
||||||
<SwitchOrDeleteAccountModal isOpen={showDeleteAccountModal} onClose={() => setShowDeleteAccountModal(false)} />
|
<SwitchOrDeleteAccountModal isOpen={showDeleteAccountModal} onClose={() => setShowDeleteAccountModal(false)} />
|
||||||
{user && step !== null ? (
|
{user && step !== null ? (
|
||||||
<div className={`fixed flex h-full w-full flex-col bg-onboarding-gradient-100`}>
|
<div className={`fixed flex h-full w-full flex-col bg-onboarding-gradient-100`}>
|
||||||
|
@ -9,7 +9,7 @@ import { UserService } from "services/user.service";
|
|||||||
// layouts
|
// layouts
|
||||||
import { ProfileSettingsLayout } from "layouts/settings-layout";
|
import { ProfileSettingsLayout } from "layouts/settings-layout";
|
||||||
// components
|
// components
|
||||||
import { ActivityIcon, ActivityMessage, IssueLink } from "components/core";
|
import { ActivityIcon, ActivityMessage, IssueLink, PageHead } from "components/core";
|
||||||
import { RichReadOnlyEditor } from "@plane/rich-text-editor";
|
import { RichReadOnlyEditor } from "@plane/rich-text-editor";
|
||||||
// icons
|
// icons
|
||||||
import { History, MessageSquare } from "lucide-react";
|
import { History, MessageSquare } from "lucide-react";
|
||||||
@ -32,6 +32,8 @@ const ProfileActivityPage: NextPageWithLayout = observer(() => {
|
|||||||
const { theme: themeStore } = useApplication();
|
const { theme: themeStore } = useApplication();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
|
<PageHead title="Profile - Activity" />
|
||||||
<section className="mx-auto mt-5 md:mt-16 flex h-full w-full flex-col overflow-hidden px-8 pb-8 lg:w-3/5">
|
<section className="mx-auto mt-5 md:mt-16 flex h-full w-full flex-col overflow-hidden px-8 pb-8 lg:w-3/5">
|
||||||
<div className="flex items-center border-b border-custom-border-100 gap-4 pb-3.5">
|
<div className="flex items-center border-b border-custom-border-100 gap-4 pb-3.5">
|
||||||
<SidebarHamburgerToggle onClick={() => themeStore.toggleSidebar()} />
|
<SidebarHamburgerToggle onClick={() => themeStore.toggleSidebar()} />
|
||||||
@ -185,6 +187,7 @@ const ProfileActivityPage: NextPageWithLayout = observer(() => {
|
|||||||
<ActivitySettingsLoader />
|
<ActivitySettingsLoader />
|
||||||
)}
|
)}
|
||||||
</section>
|
</section>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -6,6 +6,8 @@ import { Controller, useForm } from "react-hook-form";
|
|||||||
import { useApplication, useUser } from "hooks/store";
|
import { useApplication, useUser } from "hooks/store";
|
||||||
// services
|
// services
|
||||||
import { UserService } from "services/user.service";
|
import { UserService } from "services/user.service";
|
||||||
|
// components
|
||||||
|
import { PageHead } from "components/core";
|
||||||
// hooks
|
// hooks
|
||||||
import useToast from "hooks/use-toast";
|
import useToast from "hooks/use-toast";
|
||||||
// layout
|
// layout
|
||||||
@ -88,6 +90,8 @@ const ChangePasswordPage: NextPageWithLayout = observer(() => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
|
<PageHead title="Profile - Change Password" />
|
||||||
<div className="flex flex-col h-full">
|
<div className="flex flex-col h-full">
|
||||||
<div className="block md:hidden flex-shrink-0 border-b border-custom-border-200 p-4">
|
<div className="block md:hidden flex-shrink-0 border-b border-custom-border-200 p-4">
|
||||||
<SidebarHamburgerToggle onClick={() => themeStore.toggleSidebar()} />
|
<SidebarHamburgerToggle onClick={() => themeStore.toggleSidebar()} />
|
||||||
@ -164,7 +168,9 @@ const ChangePasswordPage: NextPageWithLayout = observer(() => {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
{errors.confirm_password && <span className="text-xs text-red-500">{errors.confirm_password.message}</span>}
|
{errors.confirm_password && (
|
||||||
|
<span className="text-xs text-red-500">{errors.confirm_password.message}</span>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -175,6 +181,7 @@ const ChangePasswordPage: NextPageWithLayout = observer(() => {
|
|||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ import useToast from "hooks/use-toast";
|
|||||||
// layouts
|
// layouts
|
||||||
import { ProfileSettingsLayout } from "layouts/settings-layout";
|
import { ProfileSettingsLayout } from "layouts/settings-layout";
|
||||||
// components
|
// components
|
||||||
import { ImagePickerPopover, UserImageUploadModal } from "components/core";
|
import { ImagePickerPopover, UserImageUploadModal, PageHead } from "components/core";
|
||||||
import { DeactivateAccountModal } from "components/account";
|
import { DeactivateAccountModal } from "components/account";
|
||||||
// ui
|
// ui
|
||||||
import { Button, CustomSelect, CustomSearchSelect, Input, Spinner } from "@plane/ui";
|
import { Button, CustomSelect, CustomSearchSelect, Input, Spinner } from "@plane/ui";
|
||||||
@ -57,7 +57,7 @@ const ProfileSettingsPage: NextPageWithLayout = observer(() => {
|
|||||||
// store hooks
|
// store hooks
|
||||||
const { currentUser: myProfile, updateCurrentUser, currentUserLoader } = useUser();
|
const { currentUser: myProfile, updateCurrentUser, currentUserLoader } = useUser();
|
||||||
// custom hooks
|
// custom hooks
|
||||||
const { } = useUserAuth({ user: myProfile, isLoading: currentUserLoader });
|
const {} = useUserAuth({ user: myProfile, isLoading: currentUserLoader });
|
||||||
const { theme: themeStore } = useApplication();
|
const { theme: themeStore } = useApplication();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -138,6 +138,7 @@ const ProfileSettingsPage: NextPageWithLayout = observer(() => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
<PageHead title="Profile - General Settings" />
|
||||||
<div className="flex flex-col h-full">
|
<div className="flex flex-col h-full">
|
||||||
<div className="block md:hidden flex-shrink-0 border-b border-custom-border-200 p-4">
|
<div className="block md:hidden flex-shrink-0 border-b border-custom-border-200 p-4">
|
||||||
<SidebarHamburgerToggle onClick={() => themeStore.toggleSidebar()} />
|
<SidebarHamburgerToggle onClick={() => themeStore.toggleSidebar()} />
|
||||||
@ -296,7 +297,8 @@ const ProfileSettingsPage: NextPageWithLayout = observer(() => {
|
|||||||
ref={ref}
|
ref={ref}
|
||||||
hasError={Boolean(errors.email)}
|
hasError={Boolean(errors.email)}
|
||||||
placeholder="Enter your 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
|
disabled
|
||||||
/>
|
/>
|
||||||
@ -385,7 +387,9 @@ const ProfileSettingsPage: NextPageWithLayout = observer(() => {
|
|||||||
render={({ field: { value, onChange } }) => (
|
render={({ field: { value, onChange } }) => (
|
||||||
<CustomSearchSelect
|
<CustomSearchSelect
|
||||||
value={value}
|
value={value}
|
||||||
label={value ? TIME_ZONES.find((t) => t.value === value)?.label ?? value : "Select a timezone"}
|
label={
|
||||||
|
value ? TIME_ZONES.find((t) => t.value === value)?.label ?? value : "Select a timezone"
|
||||||
|
}
|
||||||
options={timeZoneOptions}
|
options={timeZoneOptions}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
optionsClassName="w-full"
|
optionsClassName="w-full"
|
||||||
@ -409,7 +413,11 @@ const ProfileSettingsPage: NextPageWithLayout = observer(() => {
|
|||||||
<Disclosure as="div" className="border-t border-custom-border-100 px-8">
|
<Disclosure as="div" className="border-t border-custom-border-100 px-8">
|
||||||
{({ open }) => (
|
{({ open }) => (
|
||||||
<>
|
<>
|
||||||
<Disclosure.Button as="button" type="button" className="flex w-full items-center justify-between py-4">
|
<Disclosure.Button
|
||||||
|
as="button"
|
||||||
|
type="button"
|
||||||
|
className="flex w-full items-center justify-between py-4"
|
||||||
|
>
|
||||||
<span className="text-lg tracking-tight">Deactivate account</span>
|
<span className="text-lg tracking-tight">Deactivate account</span>
|
||||||
<ChevronDown className={`h-5 w-5 transition-all ${open ? "rotate-180" : ""}`} />
|
<ChevronDown className={`h-5 w-5 transition-all ${open ? "rotate-180" : ""}`} />
|
||||||
</Disclosure.Button>
|
</Disclosure.Button>
|
||||||
@ -426,8 +434,8 @@ const ProfileSettingsPage: NextPageWithLayout = observer(() => {
|
|||||||
<div className="flex flex-col gap-8">
|
<div className="flex flex-col gap-8">
|
||||||
<span className="text-sm tracking-tight">
|
<span className="text-sm tracking-tight">
|
||||||
The danger zone of the profile page is a critical area that requires careful consideration and
|
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
|
attention. When deactivating an account, all of the data and resources within that account
|
||||||
permanently removed and cannot be recovered.
|
will be permanently removed and cannot be recovered.
|
||||||
</span>
|
</span>
|
||||||
<div>
|
<div>
|
||||||
<Button variant="danger" onClick={() => setDeactivateAccountModal(true)}>
|
<Button variant="danger" onClick={() => setDeactivateAccountModal(true)}>
|
||||||
|
@ -6,6 +6,7 @@ import { ProfilePreferenceSettingsLayout } from "layouts/settings-layout/profile
|
|||||||
import { EmailSettingsLoader } from "components/ui";
|
import { EmailSettingsLoader } from "components/ui";
|
||||||
// components
|
// components
|
||||||
import { EmailNotificationForm } from "components/profile/preferences";
|
import { EmailNotificationForm } from "components/profile/preferences";
|
||||||
|
import { PageHead } from "components/core";
|
||||||
// services
|
// services
|
||||||
import { UserService } from "services/user.service";
|
import { UserService } from "services/user.service";
|
||||||
// type
|
// type
|
||||||
@ -25,9 +26,12 @@ const ProfilePreferencesThemePage: NextPageWithLayout = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
|
<PageHead title="Profile - Email Preference" />
|
||||||
<div className="mx-auto mt-8 h-full w-full overflow-y-auto px-6 lg:px-20 pb-8">
|
<div className="mx-auto mt-8 h-full w-full overflow-y-auto px-6 lg:px-20 pb-8">
|
||||||
<EmailNotificationForm data={data} />
|
<EmailNotificationForm data={data} />
|
||||||
</div>
|
</div>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ import useToast from "hooks/use-toast";
|
|||||||
// layouts
|
// layouts
|
||||||
import { ProfilePreferenceSettingsLayout } from "layouts/settings-layout/profile/preferences";
|
import { ProfilePreferenceSettingsLayout } from "layouts/settings-layout/profile/preferences";
|
||||||
// components
|
// components
|
||||||
import { CustomThemeSelector, ThemeSwitch } from "components/core";
|
import { CustomThemeSelector, ThemeSwitch, PageHead } from "components/core";
|
||||||
// ui
|
// ui
|
||||||
import { Spinner } from "@plane/ui";
|
import { Spinner } from "@plane/ui";
|
||||||
// constants
|
// constants
|
||||||
@ -47,6 +47,7 @@ const ProfilePreferencesThemePage: NextPageWithLayout = observer(() => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
<PageHead title="Profile - Theme Prefrence" />
|
||||||
{currentUser ? (
|
{currentUser ? (
|
||||||
<div className="mx-auto mt-10 md:mt-14 h-full w-full overflow-y-auto px-6 lg:px-20 pb-8">
|
<div className="mx-auto mt-10 md:mt-14 h-full w-full overflow-y-auto px-6 lg:px-20 pb-8">
|
||||||
<div className="flex items-center border-b border-custom-border-100 pb-3.5">
|
<div className="flex items-center border-b border-custom-border-100 pb-3.5">
|
||||||
|
Loading…
Reference in New Issue
Block a user