Compare commits

..
76 changed files with 554 additions and 689 deletions
-2
View File
@@ -17,7 +17,6 @@ from .views import urlpatterns as view_urls
from .webhook import urlpatterns as webhook_urls
from .workspace import urlpatterns as workspace_urls
from .timezone import urlpatterns as timezone_urls
from .exporter import urlpatterns as exporter_urls
urlpatterns = [
*analytic_urls,
@@ -39,5 +38,4 @@ urlpatterns = [
*api_urls,
*webhook_urls,
*timezone_urls,
*exporter_urls,
]
-12
View File
@@ -1,12 +0,0 @@
from django.urls import path
from plane.app.views import ExportIssuesEndpoint
urlpatterns = [
path(
"workspaces/<str:slug>/export-issues/",
ExportIssuesEndpoint.as_view(),
name="export-issues",
),
]
+7
View File
@@ -7,6 +7,7 @@ from plane.app.views import (
IssueLinkViewSet,
IssueAttachmentEndpoint,
CommentReactionViewSet,
ExportIssuesEndpoint,
IssueActivityEndpoint,
IssueArchiveViewSet,
IssueCommentViewSet,
@@ -140,6 +141,12 @@ urlpatterns = [
IssueAttachmentV2Endpoint.as_view(),
name="project-issue-attachments",
),
## Export Issues
path(
"workspaces/<str:slug>/export-issues/",
ExportIssuesEndpoint.as_view(),
name="export-issues",
),
## End Issues
## Issue Activity
path(
@@ -1154,7 +1154,10 @@ def create_comment_reaction_activity(
.values_list("id", "comment__id")
.first()
)
comment = IssueComment.objects.get(pk=comment_id, project_id=project_id)
comment = IssueComment.objects.filter(pk=comment_id, project_id=project_id).first()
if comment is None:
return
if comment is not None and comment_reaction_id is not None and comment_id is not None:
issue_activities.append(
IssueActivity(
+2 -2
View File
@@ -300,14 +300,14 @@ DATA_UPLOAD_MAX_MEMORY_SIZE = int(os.environ.get("FILE_SIZE_LIMIT", 5242880))
SESSION_COOKIE_SECURE = secure_origins
SESSION_COOKIE_HTTPONLY = True
SESSION_ENGINE = "plane.db.models.session"
SESSION_COOKIE_AGE = int(os.environ.get("SESSION_COOKIE_AGE", 604800))
SESSION_COOKIE_AGE = os.environ.get("SESSION_COOKIE_AGE", 604800)
SESSION_COOKIE_NAME = os.environ.get("SESSION_COOKIE_NAME", "session-id")
SESSION_COOKIE_DOMAIN = os.environ.get("COOKIE_DOMAIN", None)
SESSION_SAVE_EVERY_REQUEST = os.environ.get("SESSION_SAVE_EVERY_REQUEST", "0") == "1"
# Admin Cookie
ADMIN_SESSION_COOKIE_NAME = "admin-session-id"
ADMIN_SESSION_COOKIE_AGE = int(os.environ.get("ADMIN_SESSION_COOKIE_AGE", 3600))
ADMIN_SESSION_COOKIE_AGE = os.environ.get("ADMIN_SESSION_COOKIE_AGE", 3600)
# CSRF cookies
CSRF_COOKIE_SECURE = secure_origins
@@ -169,12 +169,17 @@ class IssueExportSchema(ExportSchema):
def prepare_cycle_start_date(self, i):
cycles_dict = self.context.get("cycles_dict") or {}
last_cycle = cycles_dict.get(i.id)
return last_cycle.cycle.start_date if last_cycle else None
if last_cycle and last_cycle.cycle.start_date:
return self._format_date(last_cycle.cycle.start_date)
return ""
def prepare_cycle_end_date(self, i):
cycles_dict = self.context.get("cycles_dict") or {}
last_cycle = cycles_dict.get(i.id)
return last_cycle.cycle.end_date if last_cycle else None
if last_cycle and last_cycle.cycle.end_date:
return self._format_date(last_cycle.cycle.end_date)
return ""
def prepare_parent(self, i):
if not i.parent:
@@ -8,7 +8,7 @@ import { useWorkspace } from "@/hooks/store/use-workspace";
// plane web components
import { WorkspaceActiveCyclesRoot } from "@/plane-web/components/active-cycles";
function WorkspaceActiveCyclesPage() {
const WorkspaceActiveCyclesPage = observer(() => {
const { currentWorkspace } = useWorkspace();
// derived values
const pageTitle = currentWorkspace?.name ? `${currentWorkspace?.name} - Active Cycles` : undefined;
@@ -19,6 +19,6 @@ function WorkspaceActiveCyclesPage() {
<WorkspaceActiveCyclesRoot />
</>
);
}
});
export default observer(WorkspaceActiveCyclesPage);
export default WorkspaceActiveCyclesPage;
@@ -22,14 +22,16 @@ import { useUserPermissions } from "@/hooks/store/user";
import { useResolvedAssetPath } from "@/hooks/use-resolved-asset-path";
import { getAnalyticsTabs } from "@/plane-web/components/analytics/tabs";
type AnalyticsPageProps = {
type Props = {
params: {
tabId: string;
workspaceSlug: string;
};
};
function AnalyticsPage({ params }: AnalyticsPageProps) {
const AnalyticsPage = observer((props: Props) => {
// props
const { params } = props;
const { tabId } = params;
// hooks
@@ -116,6 +118,6 @@ function AnalyticsPage({ params }: AnalyticsPageProps) {
)}
</>
);
}
});
export default observer(AnalyticsPage);
export default AnalyticsPage;
@@ -2,6 +2,7 @@
import React, { useEffect } from "react";
import { observer } from "mobx-react";
import { useParams } from "next/navigation";
import { useTheme } from "next-themes";
import useSWR from "swr";
// plane imports
@@ -23,17 +24,10 @@ import { ProjectAuthWrapper } from "@/plane-web/layouts/project-wrapper";
import emptyIssueDark from "@/public/empty-state/search/issues-dark.webp";
import emptyIssueLight from "@/public/empty-state/search/issues-light.webp";
type IssueDetailsPageProps = {
params: {
workspaceSlug: string;
workItem: string;
};
};
function IssueDetailsPage({ params }: IssueDetailsPageProps) {
const { workspaceSlug, workItem } = params;
const IssueDetailsPage = observer(() => {
// router
const router = useAppRouter();
const { workspaceSlug, workItem } = useParams();
// hooks
const { resolvedTheme } = useTheme();
// store hooks
@@ -45,23 +39,27 @@ function IssueDetailsPage({ params }: IssueDetailsPageProps) {
const { getProjectById } = useProject();
const { toggleIssueDetailSidebar, issueDetailSidebarCollapsed } = useAppTheme();
const [projectIdentifier, sequence_id] = workItem.split("-");
const projectIdentifier = workItem?.toString().split("-")[0];
const sequence_id = workItem?.toString().split("-")[1];
// fetching issue details
const { data, isLoading, error } = useSWR(`ISSUE_DETAIL_${workspaceSlug}_${projectIdentifier}_${sequence_id}`, () =>
fetchIssueWithIdentifier(workspaceSlug, projectIdentifier, sequence_id)
const { data, isLoading, error } = useSWR(
workspaceSlug && workItem ? `ISSUE_DETAIL_${workspaceSlug}_${projectIdentifier}_${sequence_id}` : null,
workspaceSlug && workItem
? () => fetchIssueWithIdentifier(workspaceSlug.toString(), projectIdentifier, sequence_id)
: null
);
const issueId = data?.id;
const projectId = data?.project_id;
// derived values
const issue = getIssueById(issueId || "") || undefined;
const issue = getIssueById(issueId?.toString() || "") || undefined;
const project = (issue?.project_id && getProjectById(issue?.project_id)) || undefined;
const issueLoader = !issue || isLoading;
const pageTitle = project && issue ? `${project?.identifier}-${issue?.sequence_id} ${issue?.name}` : undefined;
useWorkItemProperties(
projectId,
workspaceSlug,
workspaceSlug.toString(),
issueId,
issue?.is_epic ? EIssueServiceType.EPICS : EIssueServiceType.ISSUES
);
@@ -115,13 +113,14 @@ function IssueDetailsPage({ params }: IssueDetailsPageProps) {
</div>
</Loader>
) : (
workspaceSlug &&
projectId &&
issueId && (
<ProjectAuthWrapper workspaceSlug={workspaceSlug} projectId={projectId}>
<ProjectAuthWrapper workspaceSlug={workspaceSlug?.toString()} projectId={projectId?.toString()}>
<IssueDetailRoot
workspaceSlug={workspaceSlug}
projectId={projectId}
issueId={issueId}
workspaceSlug={workspaceSlug.toString()}
projectId={projectId.toString()}
issueId={issueId.toString()}
is_archived={!!issue?.archived_at}
/>
</ProjectAuthWrapper>
@@ -129,6 +128,6 @@ function IssueDetailsPage({ params }: IssueDetailsPageProps) {
)}
</>
);
}
});
export default observer(IssueDetailsPage);
export default IssueDetailsPage;
@@ -1,19 +1,19 @@
"use client";
import { useParams } from "next/navigation";
// components
import { PageHead } from "@/components/core/page-title";
import { WorkspaceDraftIssuesRoot } from "@/components/issues/workspace-draft";
type WorkspaceDraftPageProps = {
params: {
workspaceSlug: string;
};
};
function WorkspaceDraftPage({ params }: WorkspaceDraftPageProps) {
const { workspaceSlug } = params;
const WorkspaceDraftPage = () => {
// router
const { workspaceSlug: routeWorkspaceSlug } = useParams();
const pageTitle = "Workspace Draft";
// derived values
const workspaceSlug = (routeWorkspaceSlug as string) || undefined;
if (!workspaceSlug) return null;
return (
<>
<PageHead title={pageTitle} />
@@ -22,6 +22,6 @@ function WorkspaceDraftPage({ params }: WorkspaceDraftPageProps) {
</div>
</>
);
}
};
export default WorkspaceDraftPage;
@@ -1,6 +1,7 @@
"use client";
import { observer } from "mobx-react";
import { useParams } from "next/navigation";
// plane imports
import { useTranslation } from "@plane/i18n";
// components
@@ -9,14 +10,8 @@ import { NotificationsRoot } from "@/components/workspace-notifications";
// hooks
import { useWorkspace } from "@/hooks/store/use-workspace";
type WorkspaceDashboardPageProps = {
params: {
workspaceSlug: string;
};
};
function WorkspaceDashboardPage({ params }: WorkspaceDashboardPageProps) {
const { workspaceSlug } = params;
const WorkspaceDashboardPage = observer(() => {
const { workspaceSlug } = useParams();
// plane hooks
const { t } = useTranslation();
// hooks
@@ -29,9 +24,9 @@ function WorkspaceDashboardPage({ params }: WorkspaceDashboardPageProps) {
return (
<>
<PageHead title={pageTitle} />
<NotificationsRoot workspaceSlug={workspaceSlug} />
<NotificationsRoot workspaceSlug={workspaceSlug?.toString()} />
</>
);
}
});
export default observer(WorkspaceDashboardPage);
export default WorkspaceDashboardPage;
@@ -12,7 +12,7 @@ import { useWorkspace } from "@/hooks/store/use-workspace";
// local components
import { WorkspaceDashboardHeader } from "./header";
function WorkspaceDashboardPage() {
const WorkspaceDashboardPage = observer(() => {
const { currentWorkspace } = useWorkspace();
const { t } = useTranslation();
// derived values
@@ -27,6 +27,6 @@ function WorkspaceDashboardPage() {
</ContentWrapper>
</>
);
}
});
export default observer(WorkspaceDashboardPage);
export default WorkspaceDashboardPage;
@@ -1,6 +1,7 @@
"use client";
import React from "react";
import { useParams } from "next/navigation";
// components
import { PageHead } from "@/components/core/page-title";
import { ProfileIssuesPage } from "@/components/profile/profile-issues";
@@ -11,22 +12,10 @@ const ProfilePageHeader = {
subscribed: "Profile - Subscribed",
};
function isValidProfileViewId(profileViewId: string): profileViewId is keyof typeof ProfilePageHeader {
return profileViewId in ProfilePageHeader;
}
const ProfileIssuesTypePage = () => {
const { profileViewId } = useParams() as { profileViewId: "assigned" | "subscribed" | "created" | undefined };
type ProfileIssuesTypePageProps = {
params: {
workspaceSlug: string;
userId: string;
profileViewId: string;
};
};
function ProfileIssuesTypePage({ params }: ProfileIssuesTypePageProps) {
const { profileViewId } = params;
if (!isValidProfileViewId(profileViewId)) return null;
if (!profileViewId) return null;
const header = ProfilePageHeader[profileViewId];
@@ -36,6 +25,6 @@ function ProfileIssuesTypePage({ params }: ProfileIssuesTypePageProps) {
<ProfileIssuesPage type={profileViewId} />
</>
);
}
};
export default ProfileIssuesTypePage;
@@ -15,7 +15,7 @@ import { useUserPermissions } from "@/hooks/store/user";
const PER_PAGE = 100;
function ProfileActivityPage() {
const ProfileActivityPage = observer(() => {
// states
const [pageCount, setPageCount] = useState(1);
const [totalPages, setTotalPages] = useState(0);
@@ -69,6 +69,6 @@ function ProfileActivityPage() {
</div>
</>
);
}
});
export default observer(ProfileActivityPage);
export default ProfileActivityPage;
@@ -1,7 +1,7 @@
"use client";
import { observer } from "mobx-react";
import { usePathname } from "next/navigation";
import { useParams, usePathname } from "next/navigation";
import useSWR from "swr";
// components
import { PROFILE_VIEWER_TAB, PROFILE_ADMINS_TAB, EUserPermissions, EUserPermissionsLevel } from "@plane/constants";
@@ -22,16 +22,14 @@ import { ProfileNavbar } from "./navbar";
const userService = new UserService();
type UseProfileLayoutProps = {
type Props = {
children: React.ReactNode;
params: {
workspaceSlug: string;
userId: string;
};
};
function UseProfileLayout({ children, params }: UseProfileLayoutProps) {
const { workspaceSlug, userId } = params;
const UseProfileLayout: React.FC<Props> = observer((props) => {
const { children } = props;
// router
const { workspaceSlug, userId } = useParams();
const pathname = usePathname();
// store hooks
const { allowPermissions } = useUserPermissions();
@@ -45,8 +43,11 @@ function UseProfileLayout({ children, params }: UseProfileLayoutProps) {
const windowSize = useSize();
const isSmallerScreen = windowSize[0] >= 768;
const { data: userProjectsData } = useSWR(USER_PROFILE_PROJECT_SEGREGATION(workspaceSlug, userId), () =>
userService.getUserProfileProjectsSegregation(workspaceSlug, userId)
const { data: userProjectsData } = useSWR(
workspaceSlug && userId ? USER_PROFILE_PROJECT_SEGREGATION(workspaceSlug.toString(), userId.toString()) : null,
workspaceSlug && userId
? () => userService.getUserProfileProjectsSegregation(workspaceSlug.toString(), userId.toString())
: null
);
// derived values
const isAuthorizedPath =
@@ -92,6 +93,6 @@ function UseProfileLayout({ children, params }: UseProfileLayoutProps) {
</div>
</>
);
}
});
export default observer(UseProfileLayout);
export default UseProfileLayout;
@@ -1,5 +1,6 @@
"use client";
import { useParams } from "next/navigation";
import useSWR from "swr";
// plane imports
import { GROUP_CHOICES } from "@plane/constants";
@@ -19,19 +20,13 @@ import { USER_PROFILE_DATA } from "@/constants/fetch-keys";
import { UserService } from "@/services/user.service";
const userService = new UserService();
type ProfileOverviewPageProps = {
params: {
workspaceSlug: string;
userId: string;
};
};
export default function ProfileOverviewPage({ params }: ProfileOverviewPageProps) {
const { workspaceSlug, userId } = params;
export default function ProfileOverviewPage() {
const { workspaceSlug, userId } = useParams();
const { t } = useTranslation();
const { data: userProfile } = useSWR(USER_PROFILE_DATA(workspaceSlug, userId), () =>
userService.getUserProfileData(workspaceSlug, userId)
const { data: userProfile } = useSWR(
workspaceSlug && userId ? USER_PROFILE_DATA(workspaceSlug.toString(), userId.toString()) : null,
workspaceSlug && userId ? () => userService.getUserProfileData(workspaceSlug.toString(), userId.toString()) : null
);
const stateDistribution: IUserStateDistribution[] = Object.keys(GROUP_CHOICES).map((key) => {
@@ -1,6 +1,7 @@
"use client";
import { observer } from "mobx-react";
import { useParams } from "next/navigation";
// components
import { PageHead } from "@/components/core/page-title";
import { ArchivedCycleLayoutRoot } from "@/components/cycles/archived-cycles";
@@ -8,19 +9,13 @@ import { ArchivedCyclesHeader } from "@/components/cycles/archived-cycles/header
// hooks
import { useProject } from "@/hooks/store/use-project";
type ProjectArchivedCyclesPageProps = {
params: {
workspaceSlug: string;
projectId: string;
};
};
function ProjectArchivedCyclesPage({ params }: ProjectArchivedCyclesPageProps) {
const { projectId } = params;
const ProjectArchivedCyclesPage = observer(() => {
// router
const { projectId } = useParams();
// store hooks
const { getProjectById } = useProject();
// derived values
const project = getProjectById(projectId);
const project = projectId ? getProjectById(projectId.toString()) : undefined;
const pageTitle = project?.name && `${project?.name} - Archived cycles`;
return (
@@ -32,6 +27,6 @@ function ProjectArchivedCyclesPage({ params }: ProjectArchivedCyclesPageProps) {
</div>
</>
);
}
});
export default observer(ProjectArchivedCyclesPage);
export default ProjectArchivedCyclesPage;
@@ -1,6 +1,7 @@
"use client";
import { observer } from "mobx-react";
import { useParams } from "next/navigation";
import useSWR from "swr";
// ui
import { Loader } from "@plane/ui";
@@ -12,16 +13,10 @@ import { IssueDetailRoot } from "@/components/issues/issue-detail";
import { useIssueDetail } from "@/hooks/store/use-issue-detail";
import { useProject } from "@/hooks/store/use-project";
type ArchivedIssueDetailsPageProps = {
params: {
workspaceSlug: string;
projectId: string;
archivedIssueId: string;
};
};
function ArchivedIssueDetailsPage({ params }: ArchivedIssueDetailsPageProps) {
const { workspaceSlug, projectId, archivedIssueId } = params;
const ArchivedIssueDetailsPage = observer(() => {
// router
const { workspaceSlug, projectId, archivedIssueId } = useParams();
// states
// hooks
const {
fetchIssue,
@@ -30,13 +25,18 @@ function ArchivedIssueDetailsPage({ params }: ArchivedIssueDetailsPageProps) {
const { getProjectById } = useProject();
const { isLoading } = useSWR(`ARCHIVED_ISSUE_DETAIL_${workspaceSlug}_${projectId}_${archivedIssueId}`, () =>
fetchIssue(workspaceSlug, projectId, archivedIssueId)
const { isLoading } = useSWR(
workspaceSlug && projectId && archivedIssueId
? `ARCHIVED_ISSUE_DETAIL_${workspaceSlug}_${projectId}_${archivedIssueId}`
: null,
workspaceSlug && projectId && archivedIssueId
? () => fetchIssue(workspaceSlug.toString(), projectId.toString(), archivedIssueId.toString())
: null
);
// derived values
const issue = getIssueById(archivedIssueId);
const project = issue ? getProjectById(issue.project_id ?? "") : undefined;
const issue = archivedIssueId ? getIssueById(archivedIssueId.toString()) : undefined;
const project = issue ? getProjectById(issue?.project_id ?? "") : undefined;
const pageTitle = project && issue ? `${project?.identifier}-${issue?.sequence_id} ${issue?.name}` : undefined;
if (!issue) return <></>;
@@ -64,17 +64,19 @@ function ArchivedIssueDetailsPage({ params }: ArchivedIssueDetailsPageProps) {
) : (
<div className="flex h-full overflow-hidden">
<div className="h-full w-full space-y-3 divide-y-2 divide-custom-border-200 overflow-y-auto">
<IssueDetailRoot
workspaceSlug={workspaceSlug}
projectId={projectId}
issueId={archivedIssueId}
is_archived
/>
{workspaceSlug && projectId && archivedIssueId && (
<IssueDetailRoot
workspaceSlug={workspaceSlug.toString()}
projectId={projectId.toString()}
issueId={archivedIssueId.toString()}
is_archived
/>
)}
</div>
</div>
)}
</>
);
}
});
export default observer(ArchivedIssueDetailsPage);
export default ArchivedIssueDetailsPage;
@@ -1,6 +1,7 @@
"use client";
import { observer } from "mobx-react";
import { useParams } from "next/navigation";
// components
import { PageHead } from "@/components/core/page-title";
import { ArchivedIssuesHeader } from "@/components/issues/archived-issues-header";
@@ -8,19 +9,13 @@ import { ArchivedIssueLayoutRoot } from "@/components/issues/issue-layouts/roots
// hooks
import { useProject } from "@/hooks/store/use-project";
type ProjectArchivedIssuesPageProps = {
params: {
workspaceSlug: string;
projectId: string;
};
};
function ProjectArchivedIssuesPage({ params }: ProjectArchivedIssuesPageProps) {
const { projectId } = params;
const ProjectArchivedIssuesPage = observer(() => {
// router
const { projectId } = useParams();
// store hooks
const { getProjectById } = useProject();
// derived values
const project = getProjectById(projectId);
const project = projectId ? getProjectById(projectId.toString()) : undefined;
const pageTitle = project?.name && `${project?.name} - Archived work items`;
return (
@@ -32,6 +27,6 @@ function ProjectArchivedIssuesPage({ params }: ProjectArchivedIssuesPageProps) {
</div>
</>
);
}
});
export default observer(ProjectArchivedIssuesPage);
export default ProjectArchivedIssuesPage;
@@ -1,25 +1,20 @@
"use client";
import { observer } from "mobx-react";
import { useParams } from "next/navigation";
// components
import { PageHead } from "@/components/core/page-title";
import { ArchivedModuleLayoutRoot, ArchivedModulesHeader } from "@/components/modules";
// hooks
import { useProject } from "@/hooks/store/use-project";
type ProjectArchivedModulesPageProps = {
params: {
workspaceSlug: string;
projectId: string;
};
};
function ProjectArchivedModulesPage({ params }: ProjectArchivedModulesPageProps) {
const { projectId } = params;
const ProjectArchivedModulesPage = observer(() => {
// router
const { projectId } = useParams();
// store hooks
const { getProjectById } = useProject();
// derived values
const project = getProjectById(projectId);
const project = projectId ? getProjectById(projectId.toString()) : undefined;
const pageTitle = project?.name && `${project?.name} - Archived modules`;
return (
@@ -31,6 +26,6 @@ function ProjectArchivedModulesPage({ params }: ProjectArchivedModulesPageProps)
</div>
</>
);
}
});
export default observer(ProjectArchivedModulesPage);
export default ProjectArchivedModulesPage;
@@ -1,6 +1,7 @@
"use client";
import { observer } from "mobx-react";
import { useParams } from "next/navigation";
// plane imports
import { cn } from "@plane/utils";
// components
@@ -17,18 +18,10 @@ import useLocalStorage from "@/hooks/use-local-storage";
// assets
import emptyCycle from "@/public/empty-state/cycle.svg";
type CycleDetailPageProps = {
params: {
workspaceSlug: string;
projectId: string;
cycleId: string;
};
};
function CycleDetailPage({ params }: CycleDetailPageProps) {
const { workspaceSlug, projectId, cycleId } = params;
const CycleDetailPage = observer(() => {
// router
const router = useAppRouter();
const { workspaceSlug, projectId, cycleId } = useParams();
// store hooks
const { getCycleById, loader } = useCycle();
const { getProjectById } = useProject();
@@ -37,14 +30,14 @@ function CycleDetailPage({ params }: CycleDetailPageProps) {
const { setValue, storedValue } = useLocalStorage("cycle_sidebar_collapsed", false);
useCyclesDetails({
workspaceSlug,
projectId,
cycleId,
workspaceSlug: workspaceSlug?.toString(),
projectId: projectId.toString(),
cycleId: cycleId.toString(),
});
// derived values
const isSidebarCollapsed = storedValue ? (storedValue === true ? true : false) : false;
const cycle = getCycleById(cycleId);
const project = getProjectById(projectId);
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;
/**
@@ -85,9 +78,9 @@ function CycleDetailPage({ params }: CycleDetailPageProps) {
>
<CycleDetailsSidebar
handleClose={toggleSidebar}
cycleId={cycleId}
projectId={projectId}
workspaceSlug={workspaceSlug}
cycleId={cycleId.toString()}
projectId={projectId.toString()}
workspaceSlug={workspaceSlug.toString()}
/>
</div>
)}
@@ -96,6 +89,6 @@ function CycleDetailPage({ params }: CycleDetailPageProps) {
)}
</>
);
}
});
export default observer(CycleDetailPage);
export default CycleDetailPage;
@@ -2,6 +2,7 @@
import { useState } from "react";
import { observer } from "mobx-react";
import { useParams } from "next/navigation";
// plane imports
import { EUserPermissionsLevel, CYCLE_TRACKER_ELEMENTS } from "@plane/constants";
import { useTranslation } from "@plane/i18n";
@@ -25,15 +26,7 @@ import { useUserPermissions } from "@/hooks/store/user";
import { useAppRouter } from "@/hooks/use-app-router";
import { useResolvedAssetPath } from "@/hooks/use-resolved-asset-path";
type ProjectCyclesPageProps = {
params: {
workspaceSlug: string;
projectId: string;
};
};
function ProjectCyclesPage({ params }: ProjectCyclesPageProps) {
const { workspaceSlug, projectId } = params;
const ProjectCyclesPage = observer(() => {
// states
const [createModal, setCreateModal] = useState(false);
// store hooks
@@ -41,6 +34,7 @@ function ProjectCyclesPage({ params }: ProjectCyclesPageProps) {
const { getProjectById, currentProjectDetails } = useProject();
// router
const router = useAppRouter();
const { workspaceSlug, projectId } = useParams();
// plane hooks
const { t } = useTranslation();
// cycle filters hook
@@ -48,7 +42,7 @@ function ProjectCyclesPage({ params }: ProjectCyclesPageProps) {
const { allowPermissions } = useUserPermissions();
// derived values
const totalCycles = currentProjectCycleIds?.length ?? 0;
const project = getProjectById(projectId);
const project = projectId ? getProjectById(projectId?.toString()) : undefined;
const pageTitle = project?.name ? `${project?.name} - ${t("common.cycles", { count: 2 })}` : undefined;
const hasAdminLevelPermission = allowPermissions([EUserProjectRoles.ADMIN], EUserPermissionsLevel.PROJECT);
const hasMemberLevelPermission = allowPermissions(
@@ -58,14 +52,17 @@ function ProjectCyclesPage({ params }: ProjectCyclesPageProps) {
const resolvedPath = useResolvedAssetPath({ basePath: "/empty-state/disabled-feature/cycles" });
const handleRemoveFilter = (key: keyof TCycleFilters, value: string | null) => {
if (!projectId) return;
let newValues = currentProjectFilters?.[key] ?? [];
if (!value) newValues = [];
else newValues = newValues.filter((val) => val !== value);
updateFilters(projectId, { [key]: newValues });
updateFilters(projectId.toString(), { [key]: newValues });
};
if (!workspaceSlug || !projectId) return <></>;
// No access to cycle
if (currentProjectDetails?.cycle_view === false)
return (
@@ -92,8 +89,8 @@ function ProjectCyclesPage({ params }: ProjectCyclesPageProps) {
<PageHead title={pageTitle} />
<div className="w-full h-full">
<CycleCreateUpdateModal
workspaceSlug={workspaceSlug}
projectId={projectId}
workspaceSlug={workspaceSlug.toString()}
projectId={projectId.toString()}
isOpen={createModal}
handleClose={() => setCreateModal(false)}
/>
@@ -123,18 +120,18 @@ function ProjectCyclesPage({ params }: ProjectCyclesPageProps) {
<Header variant={EHeaderVariant.TERNARY}>
<CycleAppliedFiltersList
appliedFilters={currentProjectFilters ?? {}}
handleClearAllFilters={() => clearAllFilters(projectId)}
handleClearAllFilters={() => clearAllFilters(projectId.toString())}
handleRemoveFilter={handleRemoveFilter}
/>
</Header>
)}
<CyclesView workspaceSlug={workspaceSlug} projectId={projectId} />
<CyclesView workspaceSlug={workspaceSlug.toString()} projectId={projectId.toString()} />
</>
)}
</div>
</>
);
}
});
export default observer(ProjectCyclesPage);
export default ProjectCyclesPage;
@@ -1,6 +1,6 @@
"use client";
import { observer } from "mobx-react";
import { useSearchParams } from "next/navigation";
import { useParams, useSearchParams } from "next/navigation";
// plane imports
import { EUserPermissionsLevel } from "@plane/constants";
import { useTranslation } from "@plane/i18n";
@@ -15,17 +15,10 @@ import { useUserPermissions } from "@/hooks/store/user";
import { useAppRouter } from "@/hooks/use-app-router";
import { useResolvedAssetPath } from "@/hooks/use-resolved-asset-path";
type ProjectInboxPageProps = {
params: {
workspaceSlug: string;
projectId: string;
};
};
function ProjectInboxPage({ params }: ProjectInboxPageProps) {
const { workspaceSlug, projectId } = params;
const ProjectInboxPage = observer(() => {
/// router
const router = useAppRouter();
const { workspaceSlug, projectId } = useParams();
const searchParams = useSearchParams();
const navigationTab = searchParams.get("currentTab");
const inboxIssueId = searchParams.get("inboxIssueId");
@@ -79,15 +72,15 @@ function ProjectInboxPage({ params }: ProjectInboxPageProps) {
<PageHead title={pageTitle} />
<div className="w-full h-full overflow-hidden">
<InboxIssueRoot
workspaceSlug={workspaceSlug}
projectId={projectId}
inboxIssueId={inboxIssueId || undefined}
workspaceSlug={workspaceSlug.toString()}
projectId={projectId.toString()}
inboxIssueId={inboxIssueId?.toString() || undefined}
inboxAccessible={currentProjectDetails?.inbox_view || false}
navigationTab={currentNavigationTab}
/>
</div>
</div>
);
}
});
export default observer(ProjectInboxPage);
export default ProjectInboxPage;
@@ -2,6 +2,7 @@
import { useEffect } from "react";
import { observer } from "mobx-react";
import { useParams } from "next/navigation";
import { useTheme } from "next-themes";
import useSWR from "swr";
import { useTranslation } from "@plane/i18n";
@@ -18,22 +19,17 @@ import { IssueService } from "@/services/issue/issue.service";
const issueService = new IssueService();
type IssueDetailsPageProps = {
params: {
workspaceSlug: string;
projectId: string;
issueId: string;
};
};
function IssueDetailsPage({ params }: IssueDetailsPageProps) {
const { workspaceSlug, projectId, issueId } = params;
const IssueDetailsPage = observer(() => {
const router = useAppRouter();
const { t } = useTranslation();
const { workspaceSlug, projectId, issueId } = useParams();
const { resolvedTheme } = useTheme();
const { data, isLoading, error } = useSWR(`ISSUE_DETAIL_META_${workspaceSlug}_${projectId}_${issueId}`, () =>
issueService.getIssueMetaFromURL(workspaceSlug, projectId, issueId)
const { data, isLoading, error } = useSWR(
workspaceSlug && projectId && issueId ? `ISSUE_DETAIL_META_${workspaceSlug}_${projectId}_${issueId}` : null,
workspaceSlug && projectId && issueId
? () => issueService.getIssueMetaFromURL(workspaceSlug.toString(), projectId.toString(), issueId.toString())
: null
);
useEffect(() => {
@@ -63,6 +59,6 @@ function IssueDetailsPage({ params }: IssueDetailsPageProps) {
)}
</div>
);
}
});
export default observer(IssueDetailsPage);
export default IssueDetailsPage;
@@ -2,6 +2,7 @@
import { observer } from "mobx-react";
import Head from "next/head";
import { useParams } from "next/navigation";
// i18n
import { useTranslation } from "@plane/i18n";
// components
@@ -10,22 +11,19 @@ import { ProjectLayoutRoot } from "@/components/issues/issue-layouts/roots/proje
// hooks
import { useProject } from "@/hooks/store/use-project";
type ProjectIssuesPageProps = {
params: {
workspaceSlug: string;
projectId: string;
};
};
function ProjectIssuesPage({ params }: ProjectIssuesPageProps) {
const { projectId } = params;
const ProjectIssuesPage = observer(() => {
const { projectId } = useParams();
// i18n
const { t } = useTranslation();
// store
const { getProjectById } = useProject();
if (!projectId) {
return <></>;
}
// derived values
const project = getProjectById(projectId);
const project = getProjectById(projectId.toString());
const pageTitle = project?.name ? `${project?.name} - ${t("issue.label", { count: 2 })}` : undefined; // Count is for pluralization
return (
@@ -41,6 +39,6 @@ function ProjectIssuesPage({ params }: ProjectIssuesPageProps) {
</div>
</>
);
}
});
export default observer(ProjectIssuesPage);
export default ProjectIssuesPage;
@@ -1,6 +1,7 @@
"use client";
import { observer } from "mobx-react";
import { useParams } from "next/navigation";
import useSWR from "swr";
// components
import { cn } from "@plane/utils";
@@ -17,18 +18,10 @@ import useLocalStorage from "@/hooks/use-local-storage";
// assets
import emptyModule from "@/public/empty-state/module.svg";
type ModuleIssuesPageProps = {
params: {
workspaceSlug: string;
projectId: string;
moduleId: string;
};
};
function ModuleIssuesPage({ params }: ModuleIssuesPageProps) {
const { workspaceSlug, projectId, moduleId } = params;
const ModuleIssuesPage = observer(() => {
// router
const router = useAppRouter();
const { workspaceSlug, projectId, moduleId } = useParams();
// store hooks
const { fetchModuleDetails, getModuleById } = useModule();
const { getProjectById } = useProject();
@@ -37,18 +30,22 @@ function ModuleIssuesPage({ params }: ModuleIssuesPageProps) {
const { setValue, storedValue } = useLocalStorage("module_sidebar_collapsed", "false");
const isSidebarCollapsed = storedValue ? (storedValue === "true" ? true : false) : false;
// fetching module details
const { error } = useSWR(`CURRENT_MODULE_DETAILS_${moduleId}`, () =>
fetchModuleDetails(workspaceSlug, projectId, moduleId)
const { error } = useSWR(
workspaceSlug && projectId && moduleId ? `CURRENT_MODULE_DETAILS_${moduleId.toString()}` : null,
workspaceSlug && projectId && moduleId
? () => fetchModuleDetails(workspaceSlug.toString(), projectId.toString(), moduleId.toString())
: null
);
// derived values
const projectModule = getModuleById(moduleId);
const project = getProjectById(projectId);
const projectModule = moduleId ? getModuleById(moduleId.toString()) : undefined;
const project = projectId ? getProjectById(projectId.toString()) : undefined;
const pageTitle = project?.name && projectModule?.name ? `${project?.name} - ${projectModule?.name}` : undefined;
const toggleSidebar = () => {
setValue(`${!isSidebarCollapsed}`);
};
if (!workspaceSlug || !projectId || !moduleId) return <></>;
// const activeLayout = issuesFilter?.issueFilters?.displayFilters?.layout;
@@ -80,13 +77,13 @@ function ModuleIssuesPage({ params }: ModuleIssuesPageProps) {
"0px 1px 4px 0px rgba(0, 0, 0, 0.06), 0px 2px 4px 0px rgba(16, 24, 40, 0.06), 0px 1px 8px -1px rgba(16, 24, 40, 0.06)",
}}
>
<ModuleAnalyticsSidebar moduleId={moduleId} handleClose={toggleSidebar} />
<ModuleAnalyticsSidebar moduleId={moduleId.toString()} handleClose={toggleSidebar} />
</div>
)}
</div>
)}
</>
);
}
});
export default observer(ModuleIssuesPage);
export default ModuleIssuesPage;
@@ -2,6 +2,7 @@
import { useCallback } from "react";
import { observer } from "mobx-react";
import { useParams } from "next/navigation";
// types
import { EUserPermissionsLevel } from "@plane/constants";
import { useTranslation } from "@plane/i18n";
@@ -20,17 +21,10 @@ import { useUserPermissions } from "@/hooks/store/user";
import { useAppRouter } from "@/hooks/use-app-router";
import { useResolvedAssetPath } from "@/hooks/use-resolved-asset-path";
type ProjectModulesPageProps = {
params: {
workspaceSlug: string;
projectId: string;
};
};
function ProjectModulesPage({ params }: ProjectModulesPageProps) {
const { workspaceSlug, projectId } = params;
const ProjectModulesPage = observer(() => {
// router
const router = useAppRouter();
const { workspaceSlug, projectId } = useParams();
// plane hooks
const { t } = useTranslation();
// store
@@ -39,23 +33,25 @@ function ProjectModulesPage({ params }: ProjectModulesPageProps) {
useModuleFilter();
const { allowPermissions } = useUserPermissions();
// derived values
const project = getProjectById(projectId);
const project = projectId ? getProjectById(projectId.toString()) : undefined;
const pageTitle = project?.name ? `${project?.name} - Modules` : undefined;
const canPerformEmptyStateActions = allowPermissions([EUserProjectRoles.ADMIN], EUserPermissionsLevel.PROJECT);
const resolvedPath = useResolvedAssetPath({ basePath: "/empty-state/disabled-feature/modules" });
const handleRemoveFilter = useCallback(
(key: keyof TModuleFilters, value: string | null) => {
if (!projectId) return;
let newValues = currentProjectFilters?.[key] ?? [];
if (!value) newValues = [];
else newValues = newValues.filter((val) => val !== value);
updateFilters(projectId, { [key]: newValues });
updateFilters(projectId.toString(), { [key]: newValues });
},
[currentProjectFilters, projectId, updateFilters]
);
if (!workspaceSlug || !projectId) return <></>;
// No access to
if (currentProjectDetails?.module_view === false)
@@ -84,10 +80,11 @@ function ProjectModulesPage({ params }: ProjectModulesPageProps) {
<ModuleAppliedFiltersList
appliedFilters={currentProjectFilters ?? {}}
isFavoriteFilterApplied={currentProjectDisplayFilters?.favorites ?? false}
handleClearAllFilters={() => clearAllFilters(projectId)}
handleClearAllFilters={() => clearAllFilters(`${projectId}`)}
handleRemoveFilter={handleRemoveFilter}
handleDisplayFiltersUpdate={(val) => {
updateDisplayFilters(projectId, val);
if (!projectId) return;
updateDisplayFilters(projectId.toString(), val);
}}
alwaysAllowEditing
/>
@@ -96,6 +93,6 @@ function ProjectModulesPage({ params }: ProjectModulesPageProps) {
</div>
</>
);
}
});
export default observer(ProjectModulesPage);
export default ProjectModulesPage;
@@ -3,6 +3,7 @@
import { useCallback, useEffect, useMemo } from "react";
import { observer } from "mobx-react";
import Link from "next/link";
import { useParams } from "next/navigation";
import useSWR from "swr";
// plane types
import { getButtonStyling } from "@plane/propel/button";
@@ -34,35 +35,27 @@ const projectPageVersionService = new ProjectPageVersionService();
const storeType = EPageStoreType.PROJECT;
type PageDetailsPageProps = {
params: {
workspaceSlug: string;
projectId: string;
pageId: string;
};
};
function PageDetailsPage({ params }: PageDetailsPageProps) {
const { workspaceSlug, projectId, pageId } = params;
const PageDetailsPage = observer(() => {
// router
const router = useAppRouter();
const { workspaceSlug, projectId, pageId } = useParams();
// store hooks
const { createPage, fetchPageDetails } = usePageStore(storeType);
const page = usePage({
pageId,
pageId: pageId?.toString() ?? "",
storeType,
});
const { getWorkspaceBySlug } = useWorkspace();
const { uploadEditorAsset } = useEditorAsset();
// derived values
const workspaceId = getWorkspaceBySlug(workspaceSlug)?.id ?? "";
const workspaceId = workspaceSlug ? (getWorkspaceBySlug(workspaceSlug.toString())?.id ?? "") : "";
const { canCurrentUserAccessPage, id, name, updateDescription } = page ?? {};
// entity search handler
const fetchEntityCallback = useCallback(
async (payload: TSearchEntityRequestPayload) =>
await workspaceService.searchEntity(workspaceSlug, {
await workspaceService.searchEntity(workspaceSlug?.toString() ?? "", {
...payload,
project_id: projectId,
project_id: projectId?.toString() ?? "",
}),
[projectId, workspaceSlug]
);
@@ -70,8 +63,10 @@ function PageDetailsPage({ params }: PageDetailsPageProps) {
const { getEditorFileHandlers } = useEditorConfig();
// fetch page details
const { error: pageDetailsError } = useSWR(
`PAGE_DETAILS_${pageId}`,
() => fetchPageDetails(workspaceSlug, projectId, pageId),
workspaceSlug && projectId && pageId ? `PAGE_DETAILS_${pageId}` : null,
workspaceSlug && projectId && pageId
? () => fetchPageDetails(workspaceSlug?.toString(), projectId?.toString(), pageId.toString())
: null,
{
revalidateIfStale: true,
revalidateOnFocus: true,
@@ -83,18 +78,31 @@ function PageDetailsPage({ params }: PageDetailsPageProps) {
() => ({
create: createPage,
fetchAllVersions: async (pageId) => {
return await projectPageVersionService.fetchAllVersions(workspaceSlug, projectId, pageId);
if (!workspaceSlug || !projectId) return;
return await projectPageVersionService.fetchAllVersions(workspaceSlug.toString(), projectId.toString(), pageId);
},
fetchDescriptionBinary: async () => {
if (!id) return;
return await projectPageService.fetchDescriptionBinary(workspaceSlug, projectId, id);
if (!workspaceSlug || !projectId || !id) return;
return await projectPageService.fetchDescriptionBinary(workspaceSlug.toString(), projectId.toString(), id);
},
fetchEntity: fetchEntityCallback,
fetchVersionDetails: async (pageId, versionId) => {
return await projectPageVersionService.fetchVersionById(workspaceSlug, projectId, pageId, versionId);
if (!workspaceSlug || !projectId) return;
return await projectPageVersionService.fetchVersionById(
workspaceSlug.toString(),
projectId.toString(),
pageId,
versionId
);
},
restoreVersion: async (pageId, versionId) => {
await projectPageVersionService.restoreVersion(workspaceSlug, projectId, pageId, versionId);
if (!workspaceSlug || !projectId) return;
await projectPageVersionService.restoreVersion(
workspaceSlug.toString(),
projectId.toString(),
pageId,
versionId
);
},
getRedirectionLink: (pageId) => {
if (pageId) {
@@ -111,7 +119,7 @@ function PageDetailsPage({ params }: PageDetailsPageProps) {
const pageRootConfig: TPageRootConfig = useMemo(
() => ({
fileHandler: getEditorFileHandlers({
projectId,
projectId: projectId?.toString() ?? "",
uploadFile: async (blockId, file) => {
const { asset_id } = await uploadEditorAsset({
blockId,
@@ -120,13 +128,13 @@ function PageDetailsPage({ params }: PageDetailsPageProps) {
entity_type: EFileAssetType.PAGE_DESCRIPTION,
},
file,
projectId,
workspaceSlug,
projectId: projectId?.toString() ?? "",
workspaceSlug: workspaceSlug?.toString() ?? "",
});
return asset_id;
},
workspaceId,
workspaceSlug,
workspaceSlug: workspaceSlug?.toString() ?? "",
}),
}),
[getEditorFileHandlers, id, uploadEditorAsset, projectId, workspaceId, workspaceSlug]
@@ -135,8 +143,8 @@ function PageDetailsPage({ params }: PageDetailsPageProps) {
const webhookConnectionParams: TWebhookConnectionQueryParams = useMemo(
() => ({
documentType: "project_page",
projectId,
workspaceSlug,
projectId: projectId?.toString() ?? "",
workspaceSlug: workspaceSlug?.toString() ?? "",
}),
[projectId, workspaceSlug]
);
@@ -170,7 +178,7 @@ function PageDetailsPage({ params }: PageDetailsPageProps) {
</div>
);
if (!page) return null;
if (!page || !workspaceSlug || !projectId) return null;
return (
<>
@@ -183,14 +191,14 @@ function PageDetailsPage({ params }: PageDetailsPageProps) {
storeType={storeType}
page={page}
webhookConnectionParams={webhookConnectionParams}
workspaceSlug={workspaceSlug}
projectId={projectId}
workspaceSlug={workspaceSlug.toString()}
projectId={projectId?.toString()}
/>
<IssuePeekOverview />
</div>
</div>
</>
);
}
});
export default observer(PageDetailsPage);
export default PageDetailsPage;
@@ -1,6 +1,7 @@
"use client";
// component
import { useParams } from "next/navigation";
import useSWR from "swr";
import { AppHeader } from "@/components/core/app-header";
import { ContentWrapper } from "@/components/core/content-wrapper";
@@ -9,19 +10,14 @@ import { EPageStoreType, usePageStore } from "@/plane-web/hooks/store";
// local components
import { PageDetailsHeader } from "./header";
type ProjectPageDetailsLayoutProps = {
params: {
workspaceSlug: string;
projectId: string;
};
children: React.ReactNode;
};
export default function ProjectPageDetailsLayout({ params, children }: ProjectPageDetailsLayoutProps) {
const { workspaceSlug, projectId } = params;
export default function ProjectPageDetailsLayout({ children }: { children: React.ReactNode }) {
const { workspaceSlug, projectId } = useParams();
const { fetchPagesList } = usePageStore(EPageStoreType.PROJECT);
// fetching pages list
useSWR(`PROJECT_PAGES_${projectId}`, () => fetchPagesList(workspaceSlug, projectId));
useSWR(
workspaceSlug && projectId ? `PROJECT_PAGES_${projectId}` : null,
workspaceSlug && projectId ? () => fetchPagesList(workspaceSlug.toString(), projectId.toString()) : null
);
return (
<>
<AppHeader header={<PageDetailsHeader />} />
@@ -1,7 +1,7 @@
"use client";
import { observer } from "mobx-react";
import { useSearchParams } from "next/navigation";
import { useParams, useSearchParams } from "next/navigation";
// plane imports
import { EUserPermissionsLevel } from "@plane/constants";
import { useTranslation } from "@plane/i18n";
@@ -20,43 +20,31 @@ import { useResolvedAssetPath } from "@/hooks/use-resolved-asset-path";
// plane web hooks
import { EPageStoreType } from "@/plane-web/hooks/store";
type ProjectPagesPageProps = {
params: {
workspaceSlug: string;
projectId: string;
};
};
function getPageType(type: string | null): TPageNavigationTabs {
switch (type) {
case "private":
return "private";
case "archived":
return "archived";
default:
return "public";
}
};
function ProjectPagesPage({ params }: ProjectPagesPageProps) {
const { workspaceSlug, projectId } = params;
const ProjectPagesPage = observer(() => {
// router
const router = useAppRouter();
const searchParams = useSearchParams();
const type = searchParams.get("type");
const { workspaceSlug, projectId } = useParams();
// plane hooks
const { t } = useTranslation();
// store hooks
const { getProjectById, currentProjectDetails } = useProject();
const { allowPermissions } = useUserPermissions();
// derived values
const project = getProjectById(projectId);
const project = projectId ? getProjectById(projectId.toString()) : undefined;
const pageTitle = project?.name ? `${project?.name} - Pages` : undefined;
const pageType = getPageType(type);
const canPerformEmptyStateActions = allowPermissions([EUserProjectRoles.ADMIN], EUserPermissionsLevel.PROJECT);
const resolvedPath = useResolvedAssetPath({ basePath: "/empty-state/disabled-feature/pages" });
const currentPageType = (): TPageNavigationTabs => {
const pageType = type?.toString();
if (pageType === "private") return "private";
if (pageType === "archived") return "archived";
return "public";
};
if (!workspaceSlug || !projectId) return <></>;
// No access to cycle
if (currentProjectDetails?.page_view === false)
@@ -80,15 +68,15 @@ function ProjectPagesPage({ params }: ProjectPagesPageProps) {
<>
<PageHead title={pageTitle} />
<PagesListView
pageType={pageType}
projectId={projectId}
pageType={currentPageType()}
projectId={projectId.toString()}
storeType={EPageStoreType.PROJECT}
workspaceSlug={workspaceSlug}
workspaceSlug={workspaceSlug.toString()}
>
<PagesListRoot pageType={pageType} storeType={EPageStoreType.PROJECT} />
<PagesListRoot pageType={currentPageType()} storeType={EPageStoreType.PROJECT} />
</PagesListView>
</>
);
}
});
export default observer(ProjectPagesPage);
export default ProjectPagesPage;
@@ -1,6 +1,7 @@
"use client";
import { observer } from "mobx-react";
import { useParams } from "next/navigation";
import useSWR from "swr";
// components
import { EmptyState } from "@/components/common/empty-state";
@@ -13,27 +14,24 @@ import { useProjectView } from "@/hooks/store/use-project-view";
import { useAppRouter } from "@/hooks/use-app-router";
import emptyView from "@/public/empty-state/view.svg";
type ProjectViewIssuesPageProps = {
params: {
workspaceSlug: string;
projectId: string;
viewId: string;
};
};
function ProjectViewIssuesPage({ params }: ProjectViewIssuesPageProps) {
const { workspaceSlug, projectId, viewId } = params;
const ProjectViewIssuesPage = observer(() => {
// router
const router = useAppRouter();
const { workspaceSlug, projectId, viewId } = useParams();
// store hooks
const { fetchViewDetails, getViewById } = useProjectView();
const { getProjectById } = useProject();
// derived values
const projectView = getViewById(viewId);
const project = getProjectById(projectId);
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(`VIEW_DETAILS_${viewId}`, () => fetchViewDetails(workspaceSlug, projectId, viewId));
const { error } = useSWR(
workspaceSlug && projectId && viewId ? `VIEW_DETAILS_${viewId.toString()}` : null,
workspaceSlug && projectId && viewId
? () => fetchViewDetails(workspaceSlug.toString(), projectId.toString(), viewId.toString())
: null
);
if (error) {
return (
@@ -55,6 +53,6 @@ function ProjectViewIssuesPage({ params }: ProjectViewIssuesPageProps) {
<ProjectViewLayoutRoot />
</>
);
}
});
export default observer(ProjectViewIssuesPage);
export default ProjectViewIssuesPage;
@@ -2,6 +2,7 @@
import { useCallback } from "react";
import { observer } from "mobx-react";
import { useParams } from "next/navigation";
// components
import { EUserPermissionsLevel } from "@plane/constants";
import { useTranslation } from "@plane/i18n";
@@ -22,17 +23,10 @@ import { useUserPermissions } from "@/hooks/store/user";
import { useAppRouter } from "@/hooks/use-app-router";
import { useResolvedAssetPath } from "@/hooks/use-resolved-asset-path";
type ProjectViewsPageProps = {
params: {
workspaceSlug: string;
projectId: string;
};
};
function ProjectViewsPage({ params }: ProjectViewsPageProps) {
const { workspaceSlug, projectId } = params;
const ProjectViewsPage = observer(() => {
// router
const router = useAppRouter();
const { workspaceSlug, projectId } = useParams();
// plane hooks
const { t } = useTranslation();
// store
@@ -40,7 +34,7 @@ function ProjectViewsPage({ params }: ProjectViewsPageProps) {
const { filters, updateFilters, clearAllFilters } = useProjectView();
const { allowPermissions } = useUserPermissions();
// derived values
const project = getProjectById(projectId);
const project = projectId ? getProjectById(projectId.toString()) : undefined;
const pageTitle = project?.name ? `${project?.name} - Views` : undefined;
const canPerformEmptyStateActions = allowPermissions([EUserProjectRoles.ADMIN], EUserPermissionsLevel.PROJECT);
const resolvedPath = useResolvedAssetPath({ basePath: "/empty-state/disabled-feature/views" });
@@ -64,6 +58,8 @@ function ProjectViewsPage({ params }: ProjectViewsPageProps) {
const isFiltersApplied = calculateTotalFilters(filters?.filters ?? {}) !== 0;
if (!workspaceSlug || !projectId) return <></>;
// No access to
if (currentProjectDetails?.issue_views_view === false)
return (
@@ -99,6 +95,6 @@ function ProjectViewsPage({ params }: ProjectViewsPageProps) {
<ProjectViewsList />
</>
);
}
});
export default observer(ProjectViewsPage);
export default ProjectViewsPage;
@@ -1,7 +1,4 @@
import { ProjectPageRoot } from "@/plane-web/components/projects/page";
function ProjectsPage() {
return <ProjectPageRoot />;
}
const ProjectsPage = () => <ProjectPageRoot />;
export default ProjectsPage;
@@ -1,24 +1,18 @@
"use client";
import type { ReactNode } from "react";
import { useParams } from "next/navigation";
// plane web layouts
import { ProjectAuthWrapper } from "@/plane-web/layouts/project-wrapper";
type ProjectDetailLayoutProps = {
children: ReactNode;
params: {
workspaceSlug: string;
projectId: string;
};
};
function ProjectDetailLayout({ children, params }: ProjectDetailLayoutProps) {
const { workspaceSlug, projectId } = params;
const ProjectDetailLayout = ({ children }: { children: ReactNode }) => {
// router
const { workspaceSlug, projectId } = useParams();
return (
<ProjectAuthWrapper workspaceSlug={workspaceSlug} projectId={projectId}>
<ProjectAuthWrapper workspaceSlug={workspaceSlug?.toString()} projectId={projectId?.toString()}>
{children}
</ProjectAuthWrapper>
);
}
};
export default ProjectDetailLayout;
@@ -1,7 +1,4 @@
import { ProjectPageRoot } from "@/plane-web/components/projects/page";
function ProjectsPage() {
return <ProjectPageRoot />;
}
const ProjectsPage = () => <ProjectPageRoot />;
export default ProjectsPage;
@@ -2,6 +2,7 @@
import { useState } from "react";
import { observer } from "mobx-react";
import { useParams } from "next/navigation";
// plane imports
import { DEFAULT_GLOBAL_VIEWS_LIST } from "@plane/constants";
// components
@@ -10,15 +11,9 @@ import { AllIssueLayoutRoot } from "@/components/issues/issue-layouts/roots/all-
// hooks
import { useWorkspace } from "@/hooks/store/use-workspace";
type GlobalViewIssuesPageProps = {
params: {
workspaceSlug: string;
globalViewId: string;
};
};
function GlobalViewIssuesPage({ params }: GlobalViewIssuesPageProps) {
const { globalViewId } = params;
const GlobalViewIssuesPage = observer(() => {
// router
const { globalViewId } = useParams();
// store hooks
const { currentWorkspace } = useWorkspace();
// states
@@ -36,6 +31,6 @@ function GlobalViewIssuesPage({ params }: GlobalViewIssuesPageProps) {
<AllIssueLayoutRoot isDefaultView={!!defaultView} isLoading={isLoading} toggleLoading={toggleLoading} />
</>
);
}
});
export default observer(GlobalViewIssuesPage);
export default GlobalViewIssuesPage;
@@ -14,7 +14,7 @@ import { GlobalViewsList } from "@/components/workspace/views/views-list";
// hooks
import { useWorkspace } from "@/hooks/store/use-workspace";
function WorkspaceViewsPage() {
const WorkspaceViewsPage = observer(() => {
const [query, setQuery] = useState("");
// store
const { currentWorkspace } = useWorkspace();
@@ -47,6 +47,6 @@ function WorkspaceViewsPage() {
</div>
</>
);
}
});
export default observer(WorkspaceViewsPage);
export default WorkspaceViewsPage;
@@ -12,7 +12,7 @@ import { useUserPermissions } from "@/hooks/store/user";
// plane web components
import { BillingRoot } from "@/plane-web/components/workspace/billing";
function BillingSettingsPage() {
const BillingSettingsPage = observer(() => {
// store hooks
const { workspaceUserInfo, allowPermissions } = useUserPermissions();
const { currentWorkspace } = useWorkspace();
@@ -30,6 +30,6 @@ function BillingSettingsPage() {
<BillingRoot />
</SettingsContentWrapper>
);
}
});
export default observer(BillingSettingsPage);
export default BillingSettingsPage;
@@ -15,7 +15,7 @@ import SettingsHeading from "@/components/settings/heading";
import { useWorkspace } from "@/hooks/store/use-workspace";
import { useUserPermissions } from "@/hooks/store/user";
function ExportsPage() {
const ExportsPage = observer(() => {
// store hooks
const { workspaceUserInfo, allowPermissions } = useUserPermissions();
const { currentWorkspace } = useWorkspace();
@@ -51,6 +51,6 @@ function ExportsPage() {
</div>
</SettingsContentWrapper>
);
}
});
export default observer(ExportsPage);
export default ExportsPage;
@@ -12,7 +12,7 @@ import { SettingsHeading } from "@/components/settings/heading";
import { useWorkspace } from "@/hooks/store/use-workspace";
import { useUserPermissions } from "@/hooks/store/user";
function ImportsPage() {
const ImportsPage = observer(() => {
// router
// store hooks
const { currentWorkspace } = useWorkspace();
@@ -32,6 +32,6 @@ function ImportsPage() {
</section>
</SettingsContentWrapper>
);
}
});
export default observer(ImportsPage);
export default ImportsPage;
@@ -1,5 +1,6 @@
"use client";
import { observer } from "mobx-react";
import { useParams } from "next/navigation";
import useSWR from "swr";
// components
import { EUserPermissions, EUserPermissionsLevel } from "@plane/constants";
@@ -19,8 +20,9 @@ import { IntegrationService } from "@/services/integrations";
const integrationService = new IntegrationService();
function WorkspaceIntegrationsPage() {
const WorkspaceIntegrationsPage = observer(() => {
// router
const { workspaceSlug } = useParams();
// store hooks
const { currentWorkspace } = useWorkspace();
const { allowPermissions } = useUserPermissions();
@@ -28,8 +30,8 @@ function WorkspaceIntegrationsPage() {
// derived values
const isAdmin = allowPermissions([EUserPermissions.ADMIN], EUserPermissionsLevel.WORKSPACE);
const pageTitle = currentWorkspace?.name ? `${currentWorkspace.name} - Integrations` : undefined;
const { data: appIntegrations } = useSWR(isAdmin ? APP_INTEGRATIONS : null, () =>
isAdmin ? integrationService.getAppIntegrationsList() : null
const { data: appIntegrations } = useSWR(workspaceSlug && isAdmin ? APP_INTEGRATIONS : null, () =>
workspaceSlug && isAdmin ? integrationService.getAppIntegrationsList() : null
);
if (!isAdmin) return <NotAuthorizedView section="settings" className="h-auto" />;
@@ -51,6 +53,6 @@ function WorkspaceIntegrationsPage() {
</section>
</SettingsContentWrapper>
);
}
});
export default observer(WorkspaceIntegrationsPage);
export default WorkspaceIntegrationsPage;
@@ -1,6 +1,6 @@
"use client";
import type { ReactNode } from "react";
import type { FC, ReactNode } from "react";
import { observer } from "mobx-react";
import { usePathname } from "next/navigation";
// constants
@@ -15,11 +15,11 @@ import { useUserPermissions } from "@/hooks/store/user";
// local components
import { WorkspaceSettingsSidebar } from "./sidebar";
type WorkspaceSettingLayoutProps = {
export interface IWorkspaceSettingLayout {
children: ReactNode;
};
}
function WorkspaceSettingLayout(props: WorkspaceSettingLayoutProps) {
const WorkspaceSettingLayout: FC<IWorkspaceSettingLayout> = observer((props) => {
const { children } = props;
// store hooks
const { workspaceUserInfo, getWorkspaceRoleByWorkspaceSlug } = useUserPermissions();
@@ -27,10 +27,10 @@ function WorkspaceSettingLayout(props: WorkspaceSettingLayoutProps) {
const pathname = usePathname();
// derived values
const { workspaceSlug, accessKey } = pathnameToAccessKey(pathname);
const userWorkspaceRole = getWorkspaceRoleByWorkspaceSlug(workspaceSlug);
const userWorkspaceRole = getWorkspaceRoleByWorkspaceSlug(workspaceSlug.toString());
let isAuthorized: boolean | string = false;
if (pathname && userWorkspaceRole) {
if (pathname && workspaceSlug && userWorkspaceRole) {
isAuthorized = WORKSPACE_SETTINGS_ACCESS[accessKey]?.includes(userWorkspaceRole as EUserWorkspaceRoles);
}
@@ -52,6 +52,6 @@ function WorkspaceSettingLayout(props: WorkspaceSettingLayoutProps) {
</div>
</>
);
}
});
export default observer(WorkspaceSettingLayout);
export default WorkspaceSettingLayout;
@@ -2,6 +2,7 @@
import { useState } from "react";
import { observer } from "mobx-react";
import { useParams } from "next/navigation";
import { Search } from "lucide-react";
// types
import {
@@ -32,17 +33,12 @@ import { useUserPermissions } from "@/hooks/store/user";
import { BillingActionsButton } from "@/plane-web/components/workspace/billing/billing-actions-button";
import { SendWorkspaceInvitationModal } from "@/plane-web/components/workspace/members/invite-modal";
type WorkspaceMembersSettingsPageProps = {
params: {
workspaceSlug: string;
};
};
function WorkspaceMembersSettingsPage({ params }: WorkspaceMembersSettingsPageProps) {
const { workspaceSlug } = params;
const WorkspaceMembersSettingsPage = observer(() => {
// states
const [inviteModal, setInviteModal] = useState(false);
const [searchQuery, setSearchQuery] = useState<string>("");
// router
const { workspaceSlug } = useParams();
// store hooks
const { workspaceUserInfo, allowPermissions } = useUserPermissions();
const {
@@ -59,8 +55,9 @@ function WorkspaceMembersSettingsPage({ params }: WorkspaceMembersSettingsPagePr
);
const handleWorkspaceInvite = (data: IWorkspaceBulkInviteFormData) => {
if (!workspaceSlug) return;
return inviteMembersToWorkspace(workspaceSlug, data)
return inviteMembersToWorkspace(workspaceSlug.toString(), data)
.then(() => {
setInviteModal(false);
captureSuccess({
@@ -165,6 +162,6 @@ function WorkspaceMembersSettingsPage({ params }: WorkspaceMembersSettingsPagePr
</section>
</SettingsContentWrapper>
);
}
});
export default observer(WorkspaceMembersSettingsPage);
export default WorkspaceMembersSettingsPage;
@@ -10,7 +10,7 @@ import { WorkspaceDetails } from "@/components/workspace/settings/workspace-deta
// hooks
import { useWorkspace } from "@/hooks/store/use-workspace";
function WorkspaceSettingsPage() {
const WorkspaceSettingsPage = observer(() => {
// store hooks
const { currentWorkspace } = useWorkspace();
const { t } = useTranslation();
@@ -25,6 +25,6 @@ function WorkspaceSettingsPage() {
<WorkspaceDetails />
</SettingsContentWrapper>
);
}
});
export default observer(WorkspaceSettingsPage);
export default WorkspaceSettingsPage;
@@ -2,6 +2,7 @@
import { useState } from "react";
import { observer } from "mobx-react";
import { useParams } from "next/navigation";
import useSWR from "swr";
import { EUserPermissions, EUserPermissionsLevel, WORKSPACE_SETTINGS_TRACKER_EVENTS } from "@plane/constants";
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
@@ -18,17 +19,11 @@ import { useWebhook } from "@/hooks/store/use-webhook";
import { useWorkspace } from "@/hooks/store/use-workspace";
import { useUserPermissions } from "@/hooks/store/user";
type WebhookDetailsPageProps = {
params: {
workspaceSlug: string;
webhookId: string;
};
};
function WebhookDetailsPage({ params }: WebhookDetailsPageProps) {
const { workspaceSlug, webhookId } = params;
const WebhookDetailsPage = observer(() => {
// states
const [deleteWebhookModal, setDeleteWebhookModal] = useState(false);
// router
const { workspaceSlug, webhookId } = useParams();
// mobx store
const { currentWebhook, fetchWebhookById, updateWebhook } = useWebhook();
const { currentWorkspace } = useWorkspace();
@@ -44,8 +39,10 @@ function WebhookDetailsPage({ params }: WebhookDetailsPageProps) {
const pageTitle = currentWorkspace?.name ? `${currentWorkspace.name} - Webhook` : undefined;
useSWR(
isAdmin ? `WEBHOOK_DETAILS_${workspaceSlug}_${webhookId}` : null,
isAdmin ? () => fetchWebhookById(workspaceSlug, webhookId) : null
workspaceSlug && webhookId && isAdmin ? `WEBHOOK_DETAILS_${workspaceSlug}_${webhookId}` : null,
workspaceSlug && webhookId && isAdmin
? () => fetchWebhookById(workspaceSlug.toString(), webhookId.toString())
: null
);
const handleUpdateWebhook = async (formData: IWebhook) => {
@@ -59,7 +56,7 @@ function WebhookDetailsPage({ params }: WebhookDetailsPageProps) {
issue: formData?.issue,
issue_comment: formData?.issue_comment,
};
await updateWebhook(workspaceSlug, formData.id, payload)
await updateWebhook(workspaceSlug.toString(), formData.id, payload)
.then(() => {
captureSuccess({
eventName: WORKSPACE_SETTINGS_TRACKER_EVENTS.webhook_updated,
@@ -118,6 +115,6 @@ function WebhookDetailsPage({ params }: WebhookDetailsPageProps) {
</div>
</SettingsContentWrapper>
);
}
});
export default observer(WebhookDetailsPage);
export default WebhookDetailsPage;
@@ -2,6 +2,7 @@
import React, { useEffect, useState } from "react";
import { observer } from "mobx-react";
import { useParams } from "next/navigation";
import useSWR from "swr";
// plane imports
import { EUserPermissions, EUserPermissionsLevel, WORKSPACE_SETTINGS_TRACKER_ELEMENTS } from "@plane/constants";
@@ -21,16 +22,11 @@ import { useWorkspace } from "@/hooks/store/use-workspace";
import { useUserPermissions } from "@/hooks/store/user";
import { useResolvedAssetPath } from "@/hooks/use-resolved-asset-path";
type WebhooksListPageProps = {
params: {
workspaceSlug: string;
};
};
function WebhooksListPage({ params }: WebhooksListPageProps) {
const { workspaceSlug } = params;
const WebhooksListPage = observer(() => {
// states
const [showCreateWebhookModal, setShowCreateWebhookModal] = useState(false);
// router
const { workspaceSlug } = useParams();
// plane hooks
const { t } = useTranslation();
// mobx store
@@ -42,8 +38,8 @@ function WebhooksListPage({ params }: WebhooksListPageProps) {
const resolvedPath = useResolvedAssetPath({ basePath: "/empty-state/workspace-settings/webhooks" });
useSWR(
canPerformWorkspaceAdminActions ? `WEBHOOKS_LIST_${workspaceSlug}` : null,
canPerformWorkspaceAdminActions ? () => fetchWebhooks(workspaceSlug) : null
workspaceSlug && canPerformWorkspaceAdminActions ? `WEBHOOKS_LIST_${workspaceSlug}` : null,
workspaceSlug && canPerformWorkspaceAdminActions ? () => fetchWebhooks(workspaceSlug.toString()) : null
);
const pageTitle = currentWorkspace?.name
@@ -116,6 +112,6 @@ function WebhooksListPage({ params }: WebhooksListPageProps) {
</div>
</SettingsContentWrapper>
);
}
});
export default observer(WebhooksListPage);
export default WebhooksListPage;
@@ -15,7 +15,7 @@ import { useResolvedAssetPath } from "@/hooks/use-resolved-asset-path";
const PER_PAGE = 100;
function ProfileActivityPage() {
const ProfileActivityPage = observer(() => {
// states
const [pageCount, setPageCount] = useState(1);
const [totalPages, setTotalPages] = useState(0);
@@ -84,6 +84,6 @@ function ProfileActivityPage() {
)}
</>
);
}
});
export default observer(ProfileActivityPage);
export default ProfileActivityPage;
@@ -22,7 +22,7 @@ import { useResolvedAssetPath } from "@/hooks/use-resolved-asset-path";
const apiTokenService = new APITokenService();
function ApiTokensPage() {
const ApiTokensPage = observer(() => {
// states
const [isCreateTokenModalOpen, setIsCreateTokenModalOpen] = useState(false);
// router
@@ -107,6 +107,6 @@ function ApiTokensPage() {
</section>
</div>
);
}
});
export default observer(ApiTokensPage);
export default ApiTokensPage;
@@ -10,11 +10,11 @@ import { SettingsMobileNav } from "@/components/settings/mobile";
// local imports
import { ProfileSidebar } from "./sidebar";
type ProfileSettingsLayoutProps = {
type Props = {
children: ReactNode;
};
function ProfileSettingsLayout(props: ProfileSettingsLayoutProps) {
const ProfileSettingsLayout = observer((props: Props) => {
const { children } = props;
// router
const pathname = usePathname();
@@ -32,6 +32,6 @@ function ProfileSettingsLayout(props: ProfileSettingsLayoutProps) {
</div>
</>
);
}
});
export default observer(ProfileSettingsLayout);
export default ProfileSettingsLayout;
@@ -9,7 +9,7 @@ import { ProfileForm } from "@/components/profile/form";
// hooks
import { useUser } from "@/hooks/store/user";
function ProfileSettingsPage() {
const ProfileSettingsPage = observer(() => {
const { t } = useTranslation();
// store hooks
const { data: currentUser, userProfile } = useUser();
@@ -27,6 +27,6 @@ function ProfileSettingsPage() {
<ProfileForm user={currentUser} profile={userProfile.data} />
</>
);
}
});
export default observer(ProfileSettingsPage);
export default ProfileSettingsPage;
@@ -13,7 +13,7 @@ import { SettingsHeading } from "@/components/settings/heading";
// hooks
import { useUserProfile } from "@/hooks/store/user";
function ProfileAppearancePage() {
const ProfileAppearancePage = observer(() => {
const { t } = useTranslation();
// hooks
const { data: userProfile } = useUserProfile();
@@ -44,6 +44,6 @@ function ProfileAppearancePage() {
)}
</>
);
}
});
export default observer(ProfileAppearancePage);
export default ProfileAppearancePage;
@@ -42,7 +42,7 @@ const defaultShowPassword = {
confirmPassword: false,
};
function SecurityPage() {
const SecurityPage = observer(() => {
// store
const { data: currentUser, changePassword } = useUser();
// states
@@ -255,6 +255,6 @@ function SecurityPage() {
</form>
</>
);
}
});
export default observer(SecurityPage);
export default SecurityPage;
@@ -2,6 +2,7 @@
import React from "react";
import { observer } from "mobx-react";
import { useParams } from "next/navigation";
import { EUserPermissions, EUserPermissionsLevel } from "@plane/constants";
import { useTranslation } from "@plane/i18n";
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
@@ -19,15 +20,11 @@ import { useUserPermissions } from "@/hooks/store/user";
// plane web imports
import { CustomAutomationsRoot } from "@/plane-web/components/automations/root";
type AutomationSettingsPageProps = {
params: {
workspaceSlug: string;
projectId: string;
};
};
function AutomationSettingsPage({ params }: AutomationSettingsPageProps) {
const { workspaceSlug, projectId } = params;
const AutomationSettingsPage = observer(() => {
// router
const { workspaceSlug: workspaceSlugParam, projectId: projectIdParam } = useParams();
const workspaceSlug = workspaceSlugParam?.toString();
const projectId = projectIdParam?.toString();
// store hooks
const { workspaceUserInfo, allowPermissions } = useUserPermissions();
const { currentProjectDetails: projectDetails, updateProject } = useProject();
@@ -40,7 +37,7 @@ function AutomationSettingsPage({ params }: AutomationSettingsPageProps) {
const handleChange = async (formData: Partial<IProject>) => {
if (!workspaceSlug || !projectId || !projectDetails) return;
await updateProject(workspaceSlug, projectId, formData).catch(() => {
await updateProject(workspaceSlug.toString(), projectId.toString(), formData).catch(() => {
setToast({
type: TOAST_TYPE.ERROR,
title: "Error!",
@@ -70,6 +67,6 @@ function AutomationSettingsPage({ params }: AutomationSettingsPageProps) {
<CustomAutomationsRoot projectId={projectId} workspaceSlug={workspaceSlug} />
</SettingsContentWrapper>
);
}
});
export default observer(AutomationSettingsPage);
export default AutomationSettingsPage;
@@ -1,6 +1,7 @@
"use client";
import { observer } from "mobx-react";
import { useParams } from "next/navigation";
// components
import { EUserPermissions, EUserPermissionsLevel } from "@plane/constants";
import { NotAuthorizedView } from "@/components/auth-screens/not-authorized-view";
@@ -11,15 +12,8 @@ import { SettingsContentWrapper } from "@/components/settings/content-wrapper";
import { useProject } from "@/hooks/store/use-project";
import { useUserPermissions } from "@/hooks/store/user";
type EstimatesSettingsPageProps = {
params: {
workspaceSlug: string;
projectId: string;
};
};
function EstimatesSettingsPage({ params }: EstimatesSettingsPageProps) {
const { workspaceSlug, projectId } = params;
const EstimatesSettingsPage = observer(() => {
const { workspaceSlug, projectId } = useParams();
// store
const { currentProjectDetails } = useProject();
const { workspaceUserInfo, allowPermissions } = useUserPermissions();
@@ -28,6 +22,8 @@ function EstimatesSettingsPage({ params }: EstimatesSettingsPageProps) {
const pageTitle = currentProjectDetails?.name ? `${currentProjectDetails?.name} - Estimates` : undefined;
const canPerformProjectAdminActions = allowPermissions([EUserPermissions.ADMIN], EUserPermissionsLevel.PROJECT);
if (!workspaceSlug || !projectId) return <></>;
if (workspaceUserInfo && !canPerformProjectAdminActions) {
return <NotAuthorizedView section="settings" isProjectView className="h-auto" />;
}
@@ -36,10 +32,14 @@ function EstimatesSettingsPage({ params }: EstimatesSettingsPageProps) {
<SettingsContentWrapper>
<PageHead title={pageTitle} />
<div className={`w-full ${canPerformProjectAdminActions ? "" : "pointer-events-none opacity-60"}`}>
<EstimateRoot workspaceSlug={workspaceSlug} projectId={projectId} isAdmin={canPerformProjectAdminActions} />
<EstimateRoot
workspaceSlug={workspaceSlug?.toString()}
projectId={projectId?.toString()}
isAdmin={canPerformProjectAdminActions}
/>
</div>
</SettingsContentWrapper>
);
}
});
export default observer(EstimatesSettingsPage);
export default EstimatesSettingsPage;
@@ -1,6 +1,7 @@
"use client";
import { observer } from "mobx-react";
import { useParams } from "next/navigation";
// components
import { EUserPermissions, EUserPermissionsLevel } from "@plane/constants";
import { NotAuthorizedView } from "@/components/auth-screens/not-authorized-view";
@@ -11,15 +12,8 @@ import { SettingsContentWrapper } from "@/components/settings/content-wrapper";
import { useProject } from "@/hooks/store/use-project";
import { useUserPermissions } from "@/hooks/store/user";
type FeaturesSettingsPageProps = {
params: {
workspaceSlug: string;
projectId: string;
};
};
function FeaturesSettingsPage({ params }: FeaturesSettingsPageProps) {
const { workspaceSlug, projectId } = params;
const FeaturesSettingsPage = observer(() => {
const { workspaceSlug, projectId } = useParams();
// store
const { workspaceUserInfo, allowPermissions } = useUserPermissions();
@@ -28,6 +22,8 @@ function FeaturesSettingsPage({ params }: FeaturesSettingsPageProps) {
const pageTitle = currentProjectDetails?.name ? `${currentProjectDetails?.name} - Features` : undefined;
const canPerformProjectAdminActions = allowPermissions([EUserPermissions.ADMIN], EUserPermissionsLevel.PROJECT);
if (!workspaceSlug || !projectId) return null;
if (workspaceUserInfo && !canPerformProjectAdminActions) {
return <NotAuthorizedView section="settings" isProjectView className="h-auto" />;
}
@@ -37,13 +33,13 @@ function FeaturesSettingsPage({ params }: FeaturesSettingsPageProps) {
<PageHead title={pageTitle} />
<section className={`w-full ${canPerformProjectAdminActions ? "" : "opacity-60"}`}>
<ProjectFeaturesList
workspaceSlug={workspaceSlug}
projectId={projectId}
workspaceSlug={workspaceSlug.toString()}
projectId={projectId.toString()}
isAdmin={canPerformProjectAdminActions}
/>
</section>
</SettingsContentWrapper>
);
}
});
export default observer(FeaturesSettingsPage);
export default FeaturesSettingsPage;
@@ -14,7 +14,7 @@ import { SettingsContentWrapper } from "@/components/settings/content-wrapper";
import { useProject } from "@/hooks/store/use-project";
import { useUserPermissions } from "@/hooks/store/user";
function LabelsSettingsPage() {
const LabelsSettingsPage = observer(() => {
// store hooks
const { currentProjectDetails } = useProject();
const { workspaceUserInfo, allowPermissions } = useUserPermissions();
@@ -54,6 +54,6 @@ function LabelsSettingsPage() {
</div>
</SettingsContentWrapper>
);
}
});
export default observer(LabelsSettingsPage);
export default LabelsSettingsPage;
@@ -1,6 +1,7 @@
"use client";
import { observer } from "mobx-react";
import { useParams } from "next/navigation";
// plane imports
import { EUserPermissions, EUserPermissionsLevel } from "@plane/constants";
import { useTranslation } from "@plane/i18n";
@@ -18,20 +19,17 @@ import { useUserPermissions } from "@/hooks/store/user";
import { ProjectTeamspaceList } from "@/plane-web/components/projects/teamspaces/teamspace-list";
import { getProjectSettingsPageLabelI18nKey } from "@/plane-web/helpers/project-settings";
type MembersSettingsPageProps = {
params: {
workspaceSlug: string;
projectId: string;
};
};
function MembersSettingsPage({ params }: MembersSettingsPageProps) {
const { workspaceSlug, projectId } = params;
const MembersSettingsPage = observer(() => {
// router
const { workspaceSlug: routerWorkspaceSlug, projectId: routerProjectId } = useParams();
// plane hooks
const { t } = useTranslation();
// store hooks
const { currentProjectDetails } = useProject();
const { workspaceUserInfo, allowPermissions } = useUserPermissions();
// derived values
const projectId = routerProjectId?.toString();
const workspaceSlug = routerWorkspaceSlug?.toString();
const pageTitle = currentProjectDetails?.name ? `${currentProjectDetails?.name} - Members` : undefined;
const isProjectMemberOrAdmin = allowPermissions(
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
@@ -53,6 +51,6 @@ function MembersSettingsPage({ params }: MembersSettingsPageProps) {
<ProjectMemberList projectId={projectId} workspaceSlug={workspaceSlug} />
</SettingsContentWrapper>
);
}
});
export default observer(MembersSettingsPage);
export default MembersSettingsPage;
@@ -2,6 +2,7 @@
import { useState } from "react";
import { observer } from "mobx-react";
import { useParams } from "next/navigation";
import useSWR from "swr";
// plane imports
import { EUserPermissions, EUserPermissionsLevel } from "@plane/constants";
@@ -18,38 +19,40 @@ import { SettingsContentWrapper } from "@/components/settings/content-wrapper";
import { useProject } from "@/hooks/store/use-project";
import { useUserPermissions } from "@/hooks/store/user";
type ProjectSettingsPageProps = {
params: {
workspaceSlug: string;
projectId: string;
};
};
function ProjectSettingsPage({ params }: ProjectSettingsPageProps) {
const { workspaceSlug, projectId } = params;
const ProjectSettingsPage = observer(() => {
// states
const [selectProject, setSelectedProject] = useState<string | null>(null);
const [archiveProject, setArchiveProject] = useState<boolean>(false);
// router
const { workspaceSlug, projectId } = useParams();
// store hooks
const { currentProjectDetails, fetchProjectDetails } = useProject();
const { allowPermissions } = useUserPermissions();
// api call to fetch project details
// TODO: removed this API if not necessary
const { isLoading } = useSWR(`PROJECT_DETAILS_${projectId}`, () => fetchProjectDetails(workspaceSlug, projectId));
const { isLoading } = useSWR(
workspaceSlug && projectId ? `PROJECT_DETAILS_${projectId}` : null,
workspaceSlug && projectId ? () => fetchProjectDetails(workspaceSlug.toString(), projectId.toString()) : null
);
// derived values
const isAdmin = allowPermissions([EUserPermissions.ADMIN], EUserPermissionsLevel.PROJECT, workspaceSlug, projectId);
const isAdmin = allowPermissions(
[EUserPermissions.ADMIN],
EUserPermissionsLevel.PROJECT,
workspaceSlug.toString(),
projectId.toString()
);
const pageTitle = currentProjectDetails?.name ? `${currentProjectDetails?.name} - General Settings` : undefined;
return (
<SettingsContentWrapper>
<PageHead title={pageTitle} />
{currentProjectDetails && (
{currentProjectDetails && workspaceSlug && projectId && (
<>
<ArchiveRestoreProjectModal
workspaceSlug={workspaceSlug}
projectId={projectId}
workspaceSlug={workspaceSlug.toString()}
projectId={projectId.toString()}
isOpen={archiveProject}
onClose={() => setArchiveProject(false)}
archive
@@ -63,11 +66,11 @@ function ProjectSettingsPage({ params }: ProjectSettingsPageProps) {
)}
<div className={`w-full ${isAdmin ? "" : "opacity-60"}`}>
{currentProjectDetails && !isLoading ? (
{currentProjectDetails && workspaceSlug && projectId && !isLoading ? (
<ProjectDetailsForm
project={currentProjectDetails}
workspaceSlug={workspaceSlug}
projectId={projectId}
workspaceSlug={workspaceSlug.toString()}
projectId={projectId.toString()}
isAdmin={isAdmin}
/>
) : (
@@ -89,6 +92,6 @@ function ProjectSettingsPage({ params }: ProjectSettingsPageProps) {
</div>
</SettingsContentWrapper>
);
}
});
export default observer(ProjectSettingsPage);
export default ProjectSettingsPage;
@@ -1,6 +1,7 @@
"use client";
import { observer } from "mobx-react";
import { useParams } from "next/navigation";
import { EUserPermissions, EUserPermissionsLevel } from "@plane/constants";
import { useTranslation } from "@plane/i18n";
// components
@@ -13,15 +14,8 @@ import { SettingsHeading } from "@/components/settings/heading";
import { useProject } from "@/hooks/store/use-project";
import { useUserPermissions } from "@/hooks/store/user";
type StatesSettingsPageProps = {
params: {
workspaceSlug: string;
projectId: string;
};
};
function StatesSettingsPage({ params }: StatesSettingsPageProps) {
const { workspaceSlug, projectId } = params;
const StatesSettingsPage = observer(() => {
const { workspaceSlug, projectId } = useParams();
// store
const { currentProjectDetails } = useProject();
const { workspaceUserInfo, allowPermissions } = useUserPermissions();
@@ -48,10 +42,12 @@ function StatesSettingsPage({ params }: StatesSettingsPageProps) {
title={t("project_settings.states.heading")}
description={t("project_settings.states.description")}
/>
<ProjectStateRoot workspaceSlug={workspaceSlug} projectId={projectId} />
{workspaceSlug && projectId && (
<ProjectStateRoot workspaceSlug={workspaceSlug.toString()} projectId={projectId.toString()} />
)}
</div>
</SettingsContentWrapper>
);
}
});
export default observer(StatesSettingsPage);
export default StatesSettingsPage;
@@ -3,7 +3,7 @@
import type { ReactNode } from "react";
import { useEffect } from "react";
import { observer } from "mobx-react";
import { usePathname } from "next/navigation";
import { useParams, usePathname } from "next/navigation";
// components
import { getProjectActivePath } from "@/components/settings/helper";
import { SettingsMobileNav } from "@/components/settings/mobile";
@@ -12,19 +12,16 @@ import { useProject } from "@/hooks/store/use-project";
import { useAppRouter } from "@/hooks/use-app-router";
import { ProjectAuthWrapper } from "@/plane-web/layouts/project-wrapper";
type ProjectSettingsLayoutProps = {
type Props = {
children: ReactNode;
params: {
workspaceSlug: string;
projectId?: string;
};
};
function ProjectSettingsLayout({ children, params }: ProjectSettingsLayoutProps) {
const { workspaceSlug, projectId } = params;
const ProjectSettingsLayout = observer((props: Props) => {
const { children } = props;
// router
const router = useAppRouter();
const pathname = usePathname();
const { workspaceSlug, projectId } = useParams();
const { joinedProjectIds } = useProject();
useEffect(() => {
@@ -37,22 +34,14 @@ function ProjectSettingsLayout({ children, params }: ProjectSettingsLayoutProps)
return (
<>
<SettingsMobileNav hamburgerContent={ProjectSettingsSidebar} activePath={getProjectActivePath(pathname) || ""} />
{projectId ? (
<ProjectAuthWrapper workspaceSlug={workspaceSlug} projectId={projectId}>
<div className="relative flex h-full w-full">
<div className="hidden md:block">
<ProjectSettingsSidebar />
</div>
<div className="w-full h-full overflow-y-scroll md:pt-page-y">{children}</div>
</div>
</ProjectAuthWrapper>
) : (
<ProjectAuthWrapper workspaceSlug={workspaceSlug?.toString()} projectId={projectId?.toString()}>
<div className="relative flex h-full w-full">
<div className="hidden md:block">{projectId && <ProjectSettingsSidebar />}</div>
<div className="w-full h-full overflow-y-scroll md:pt-page-y">{children}</div>
</div>
)}
</ProjectAuthWrapper>
</>
);
}
});
export default observer(ProjectSettingsLayout);
export default ProjectSettingsLayout;
@@ -7,7 +7,7 @@ import { Button, getButtonStyling } from "@plane/propel/button";
import { cn } from "@plane/utils";
import { useCommandPalette } from "@/hooks/store/use-command-palette";
function ProjectSettingsPage() {
const ProjectSettingsPage = () => {
// store hooks
const { resolvedTheme } = useTheme();
const { toggleCreateProjectModal } = useCommandPalette();
@@ -38,6 +38,6 @@ function ProjectSettingsPage() {
</div>
</div>
);
}
};
export default ProjectSettingsPage;
@@ -10,17 +10,15 @@ import { EAuthModes, EPageTypes } from "@/helpers/authentication.helper";
import DefaultLayout from "@/layouts/default-layout";
import { AuthenticationWrapper } from "@/lib/wrappers/authentication-wrapper";
function ForgotPasswordPage() {
return (
<DefaultLayout>
<AuthenticationWrapper pageType={EPageTypes.NON_AUTHENTICATED}>
<div className="relative z-10 flex flex-col items-center w-screen h-screen overflow-hidden overflow-y-auto pt-6 pb-10 px-8">
<AuthHeader type={EAuthModes.SIGN_IN} />
<ForgotPasswordForm />
</div>
</AuthenticationWrapper>
</DefaultLayout>
);
}
const ForgotPasswordPage = observer(() => (
<DefaultLayout>
<AuthenticationWrapper pageType={EPageTypes.NON_AUTHENTICATED}>
<div className="relative z-10 flex flex-col items-center w-screen h-screen overflow-hidden overflow-y-auto pt-6 pb-10 px-8">
<AuthHeader type={EAuthModes.SIGN_IN} />
<ForgotPasswordForm />
</div>
</AuthenticationWrapper>
</DefaultLayout>
));
export default observer(ForgotPasswordPage);
export default ForgotPasswordPage;
@@ -11,17 +11,15 @@ import { EPageTypes } from "@/helpers/authentication.helper";
import DefaultLayout from "@/layouts/default-layout";
import { AuthenticationWrapper } from "@/lib/wrappers/authentication-wrapper";
function ResetPasswordPage() {
return (
<DefaultLayout>
<AuthenticationWrapper pageType={EPageTypes.NON_AUTHENTICATED}>
<div className="relative z-10 flex flex-col items-center w-screen h-screen overflow-hidden overflow-y-auto pt-6 pb-10 px-8">
<AuthHeader type={EAuthModes.SIGN_IN} />
<ResetPasswordForm />
</div>
</AuthenticationWrapper>
</DefaultLayout>
);
}
const ResetPasswordPage = () => (
<DefaultLayout>
<AuthenticationWrapper pageType={EPageTypes.NON_AUTHENTICATED}>
<div className="relative z-10 flex flex-col items-center w-screen h-screen overflow-hidden overflow-y-auto pt-6 pb-10 px-8">
<AuthHeader type={EAuthModes.SIGN_IN} />
<ResetPasswordForm />
</div>
</AuthenticationWrapper>
</DefaultLayout>
);
export default ResetPasswordPage;
@@ -11,17 +11,15 @@ import { EPageTypes } from "@/helpers/authentication.helper";
import DefaultLayout from "@/layouts/default-layout";
import { AuthenticationWrapper } from "@/lib/wrappers/authentication-wrapper";
function SetPasswordPage() {
return (
<DefaultLayout>
<AuthenticationWrapper pageType={EPageTypes.SET_PASSWORD}>
<div className="relative z-10 flex flex-col items-center w-screen h-screen overflow-hidden overflow-y-auto pt-6 pb-10 px-8">
<AuthHeader type={EAuthModes.SIGN_IN} />
<ResetPasswordForm />
</div>
</AuthenticationWrapper>
</DefaultLayout>
);
}
const SetPasswordPage = () => (
<DefaultLayout>
<AuthenticationWrapper pageType={EPageTypes.SET_PASSWORD}>
<div className="relative z-10 flex flex-col items-center w-screen h-screen overflow-hidden overflow-y-auto pt-6 pb-10 px-8">
<AuthHeader type={EAuthModes.SIGN_IN} />
<ResetPasswordForm />
</div>
</AuthenticationWrapper>
</DefaultLayout>
);
export default SetPasswordPage;
+3 -3
View File
@@ -21,7 +21,7 @@ import { getIsWorkspaceCreationDisabled } from "@/plane-web/helpers/instance.hel
// images
import WorkspaceCreationDisabled from "@/public/workspace/workspace-creation-disabled.png";
function CreateWorkspacePage() {
const CreateWorkspacePage = observer(() => {
const { t } = useTranslation();
// router
const router = useAppRouter();
@@ -103,6 +103,6 @@ function CreateWorkspacePage() {
</div>
</AuthenticationWrapper>
);
}
});
export default observer(CreateWorkspacePage);
export default CreateWorkspacePage;
@@ -1,7 +1,7 @@
"use client";
import React, { useEffect } from "react";
import { useSearchParams } from "next/navigation";
import { useParams, useSearchParams } from "next/navigation";
// ui
import { LogoSpinner } from "@/components/common/logo-spinner";
// services
@@ -10,14 +10,9 @@ import { AppInstallationService } from "@/services/app_installation.service";
// services
const appInstallationService = new AppInstallationService();
type AppPostInstallationProps = {
params: {
provider: string;
};
};
export default function AppPostInstallation({ params }: AppPostInstallationProps) {
const { provider } = params;
export default function AppPostInstallation() {
// params
const { provider } = useParams();
// query params
const searchParams = useSearchParams();
const installation_id = searchParams.get("installation_id");
+3 -3
View File
@@ -34,7 +34,7 @@ import emptyInvitation from "@/public/empty-state/invitation.svg";
const workspaceService = new WorkspaceService();
function UserInvitationsPage() {
const UserInvitationsPage = observer(() => {
// states
const [invitationsRespond, setInvitationsRespond] = useState<string[]>([]);
const [isJoiningWorkspaces, setIsJoiningWorkspaces] = useState(false);
@@ -220,6 +220,6 @@ function UserInvitationsPage() {
</div>
</AuthenticationWrapper>
);
}
});
export default observer(UserInvitationsPage);
export default UserInvitationsPage;
+3 -3
View File
@@ -20,7 +20,7 @@ import { WorkspaceService } from "@/plane-web/services";
const workspaceService = new WorkspaceService();
function OnboardingPage() {
const OnboardingPage = observer(() => {
// store hooks
const { data: user } = useUser();
const { fetchWorkspaces } = useWorkspace();
@@ -57,6 +57,6 @@ function OnboardingPage() {
</div>
</AuthenticationWrapper>
);
}
});
export default observer(OnboardingPage);
export default OnboardingPage;
+3 -3
View File
@@ -16,7 +16,7 @@ import { useResolvedAssetPath } from "@/hooks/use-resolved-asset-path";
const PER_PAGE = 100;
function ProfileActivityPage() {
const ProfileActivityPage = observer(() => {
// states
const [pageCount, setPageCount] = useState(1);
const [totalPages, setTotalPages] = useState(0);
@@ -76,6 +76,6 @@ function ProfileActivityPage() {
</ProfileSettingContentWrapper>
</>
);
}
});
export default observer(ProfileActivityPage);
export default ProfileActivityPage;
@@ -20,7 +20,7 @@ import { ProfileSettingContentWrapper } from "@/components/profile/profile-setti
// hooks
import { useUserProfile } from "@/hooks/store/user";
function ProfileAppearancePage() {
const ProfileAppearancePage = observer(() => {
const { t } = useTranslation();
const { setTheme } = useTheme();
// states
@@ -86,6 +86,6 @@ function ProfileAppearancePage() {
)}
</>
);
}
});
export default observer(ProfileAppearancePage);
export default ProfileAppearancePage;
+2 -2
View File
@@ -8,11 +8,11 @@ import { AuthenticationWrapper } from "@/lib/wrappers/authentication-wrapper";
// layout
import { ProfileLayoutSidebar } from "./sidebar";
type ProfileSettingsLayoutProps = {
type Props = {
children: ReactNode;
};
export default function ProfileSettingsLayout(props: ProfileSettingsLayoutProps) {
export default function ProfileSettingsLayout(props: Props) {
const { children } = props;
return (
+3 -3
View File
@@ -11,7 +11,7 @@ import { ProfileSettingContentWrapper } from "@/components/profile/profile-setti
// hooks
import { useUser } from "@/hooks/store/user";
function ProfileSettingsPage() {
const ProfileSettingsPage = observer(() => {
const { t } = useTranslation();
// store hooks
const { data: currentUser, userProfile } = useUser();
@@ -31,6 +31,6 @@ function ProfileSettingsPage() {
</ProfileSettingContentWrapper>
</>
);
}
});
export default observer(ProfileSettingsPage);
export default ProfileSettingsPage;
+3 -3
View File
@@ -43,7 +43,7 @@ const defaultShowPassword = {
confirmPassword: false,
};
function SecurityPage() {
const SecurityPage = observer(() => {
// store
const { data: currentUser, changePassword } = useUser();
// states
@@ -254,6 +254,6 @@ function SecurityPage() {
</ProfileSettingContentWrapper>
</>
);
}
});
export default observer(SecurityPage);
export default SecurityPage;
+7 -9
View File
@@ -8,14 +8,12 @@ import { EAuthModes, EPageTypes } from "@/helpers/authentication.helper";
import DefaultLayout from "@/layouts/default-layout";
import { AuthenticationWrapper } from "@/lib/wrappers/authentication-wrapper";
function SignUpPage() {
return (
<DefaultLayout>
<AuthenticationWrapper pageType={EPageTypes.NON_AUTHENTICATED}>
<AuthBase authType={EAuthModes.SIGN_UP} />
</AuthenticationWrapper>
</DefaultLayout>
);
}
const SignUpPage = () => (
<DefaultLayout>
<AuthenticationWrapper pageType={EPageTypes.NON_AUTHENTICATED}>
<AuthBase authType={EAuthModes.SIGN_UP} />
</AuthenticationWrapper>
</DefaultLayout>
);
export default SignUpPage;
@@ -23,7 +23,7 @@ import { WorkspaceService } from "@/plane-web/services";
// service initialization
const workspaceService = new WorkspaceService();
function WorkspaceInvitationPage() {
const WorkspaceInvitationPage = observer(() => {
// router
const router = useAppRouter();
// query params
@@ -123,6 +123,6 @@ function WorkspaceInvitationPage() {
</div>
</AuthenticationWrapper>
);
}
});
export default observer(WorkspaceInvitationPage);
export default WorkspaceInvitationPage;
+7 -9
View File
@@ -9,14 +9,12 @@ import DefaultLayout from "@/layouts/default-layout";
// wrappers
import { AuthenticationWrapper } from "@/lib/wrappers/authentication-wrapper";
function HomePage() {
return (
<DefaultLayout>
<AuthenticationWrapper pageType={EPageTypes.NON_AUTHENTICATED}>
<AuthBase authType={EAuthModes.SIGN_IN} />
</AuthenticationWrapper>
</DefaultLayout>
);
}
const HomePage = () => (
<DefaultLayout>
<AuthenticationWrapper pageType={EPageTypes.NON_AUTHENTICATED}>
<AuthBase authType={EAuthModes.SIGN_IN} />
</AuthenticationWrapper>
</DefaultLayout>
);
export default HomePage;