mirror of
https://github.com/makeplane/plane.git
synced 2026-07-12 21:40:18 +02:00
Merge pull request #451 from makeplane/sync/ce-ee
sync: community changes
This commit is contained in:
2
.github/workflows/repo-sync.yml
vendored
2
.github/workflows/repo-sync.yml
vendored
@@ -4,7 +4,7 @@ on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- develop
|
||||
- preview
|
||||
|
||||
env:
|
||||
SOURCE_BRANCH_NAME: ${{ github.ref_name }}
|
||||
|
||||
@@ -3,18 +3,12 @@ from rest_framework import serializers
|
||||
|
||||
# Module imports
|
||||
from .base import DynamicBaseSerializer
|
||||
from .workspace import WorkspaceLiteSerializer
|
||||
from .project import ProjectLiteSerializer
|
||||
from plane.db.models import IssueView
|
||||
from plane.utils.issue_filters import issue_filters
|
||||
|
||||
|
||||
class IssueViewSerializer(DynamicBaseSerializer):
|
||||
is_favorite = serializers.BooleanField(read_only=True)
|
||||
project_detail = ProjectLiteSerializer(source="project", read_only=True)
|
||||
workspace_detail = WorkspaceLiteSerializer(
|
||||
source="workspace", read_only=True
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = IssueView
|
||||
@@ -24,6 +18,8 @@ class IssueViewSerializer(DynamicBaseSerializer):
|
||||
"project",
|
||||
"query",
|
||||
"owned_by",
|
||||
"access",
|
||||
"is_locked",
|
||||
]
|
||||
|
||||
def create(self, validated_data):
|
||||
|
||||
@@ -69,11 +69,40 @@ class WorkspaceViewViewSet(BaseViewSet):
|
||||
.get_queryset()
|
||||
.filter(workspace__slug=self.kwargs.get("slug"))
|
||||
.filter(project__isnull=True)
|
||||
.filter(Q(owned_by=self.request.user) | Q(access=1))
|
||||
.select_related("workspace")
|
||||
.order_by(self.request.GET.get("order_by", "-created_at"))
|
||||
.distinct()
|
||||
)
|
||||
|
||||
def partial_update(self, request, slug, pk):
|
||||
workspace_view = IssueView.objects.get(
|
||||
pk=pk,
|
||||
workspace__slug=slug,
|
||||
)
|
||||
|
||||
if workspace_view.is_locked:
|
||||
return Response(
|
||||
{"error": "view is locked"},
|
||||
status=status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
|
||||
# Only update the view if owner is updating
|
||||
if workspace_view.owned_by_id != request.user.id:
|
||||
return Response(
|
||||
{"error": "Only the owner of the view can update the view"},
|
||||
status=status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
|
||||
serializer = IssueViewSerializer(
|
||||
workspace_view, data=request.data, partial=True
|
||||
)
|
||||
|
||||
if serializer.is_valid():
|
||||
serializer.save()
|
||||
return Response(serializer.data, status=status.HTTP_200_OK)
|
||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
|
||||
class WorkspaceViewIssuesViewSet(BaseViewSet):
|
||||
permission_classes = [
|
||||
@@ -294,6 +323,7 @@ class IssueViewViewSet(BaseViewSet):
|
||||
project__project_projectmember__is_active=True,
|
||||
project__archived_at__isnull=True,
|
||||
)
|
||||
.filter(Q(owned_by=self.request.user) | Q(access=1))
|
||||
.select_related("project")
|
||||
.select_related("workspace")
|
||||
.annotate(is_favorite=Exists(subquery))
|
||||
@@ -313,6 +343,33 @@ class IssueViewViewSet(BaseViewSet):
|
||||
).data
|
||||
return Response(views, status=status.HTTP_200_OK)
|
||||
|
||||
def partial_update(self, request, slug, project_id, pk):
|
||||
issue_view = IssueView.objects.get(
|
||||
pk=pk, workspace__slug=slug, project_id=project_id
|
||||
)
|
||||
|
||||
if issue_view.is_locked:
|
||||
return Response(
|
||||
{"error": "view is locked"},
|
||||
status=status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
|
||||
# Only update the view if owner is updating
|
||||
if issue_view.owned_by_id != request.user.id:
|
||||
return Response(
|
||||
{"error": "Only the owner of the view can update the view"},
|
||||
status=status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
|
||||
serializer = IssueViewSerializer(
|
||||
issue_view, data=request.data, partial=True
|
||||
)
|
||||
|
||||
if serializer.is_valid():
|
||||
serializer.save()
|
||||
return Response(serializer.data, status=status.HTTP_200_OK)
|
||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
|
||||
class IssueViewFavoriteViewSet(BaseViewSet):
|
||||
model = UserFavorite
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
from django.db.models import (
|
||||
Q,
|
||||
Count,
|
||||
Sum,
|
||||
)
|
||||
|
||||
# Third party modules
|
||||
@@ -87,29 +86,6 @@ class WorkspaceCyclesEndpoint(BaseAPIView):
|
||||
),
|
||||
)
|
||||
)
|
||||
.annotate(
|
||||
total_estimates=Sum("issue_cycle__issue__estimate_point")
|
||||
)
|
||||
.annotate(
|
||||
completed_estimates=Sum(
|
||||
"issue_cycle__issue__estimate_point",
|
||||
filter=Q(
|
||||
issue_cycle__issue__state__group="completed",
|
||||
issue_cycle__issue__archived_at__isnull=True,
|
||||
issue_cycle__issue__is_draft=False,
|
||||
),
|
||||
)
|
||||
)
|
||||
.annotate(
|
||||
started_estimates=Sum(
|
||||
"issue_cycle__issue__estimate_point",
|
||||
filter=Q(
|
||||
issue_cycle__issue__state__group="started",
|
||||
issue_cycle__issue__archived_at__isnull=True,
|
||||
issue_cycle__issue__is_draft=False,
|
||||
),
|
||||
)
|
||||
)
|
||||
.order_by(self.kwargs.get("order_by", "-created_at"))
|
||||
.distinct()
|
||||
)
|
||||
|
||||
@@ -4,9 +4,10 @@ import { ChevronRight } from "lucide-react";
|
||||
type BreadcrumbsProps = {
|
||||
children: React.ReactNode;
|
||||
onBack?: () => void;
|
||||
isLoading?: boolean;
|
||||
};
|
||||
|
||||
const Breadcrumbs = ({ children, onBack }: BreadcrumbsProps) => {
|
||||
const Breadcrumbs = ({ children, onBack, isLoading = false }: BreadcrumbsProps) => {
|
||||
const [isSmallScreen, setIsSmallScreen] = React.useState(false);
|
||||
|
||||
React.useEffect(() => {
|
||||
@@ -21,6 +22,13 @@ const Breadcrumbs = ({ children, onBack }: BreadcrumbsProps) => {
|
||||
|
||||
const childrenArray = React.Children.toArray(children);
|
||||
|
||||
const BreadcrumbItemLoader = (
|
||||
<div className="flex items-center gap-1 animate-pulse">
|
||||
<span className="h-5 w-5 bg-custom-background-80 rounded" />
|
||||
<span className="h-5 w-16 bg-custom-background-80 rounded" />
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="flex items-center space-x-2 overflow-hidden">
|
||||
{!isSmallScreen && (
|
||||
@@ -33,7 +41,7 @@ const Breadcrumbs = ({ children, onBack }: BreadcrumbsProps) => {
|
||||
</div>
|
||||
)}
|
||||
<div className={`flex items-center gap-2.5 ${isSmallScreen && index > 0 ? "hidden sm:flex" : "flex"}`}>
|
||||
{child}
|
||||
{isLoading ? BreadcrumbItemLoader : child}
|
||||
</div>
|
||||
</React.Fragment>
|
||||
))}
|
||||
@@ -50,7 +58,9 @@ const Breadcrumbs = ({ children, onBack }: BreadcrumbsProps) => {
|
||||
)}
|
||||
<ChevronRight className="h-3.5 w-3.5 flex-shrink-0 text-custom-text-400" aria-hidden="true" />
|
||||
</div>
|
||||
<div className="flex items-center gap-2.5">{childrenArray[childrenArray.length - 1]}</div>
|
||||
<div className="flex items-center gap-2.5">
|
||||
{isLoading ? BreadcrumbItemLoader : childrenArray[childrenArray.length - 1]}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{isSmallScreen && childrenArray.length === 1 && childrenArray}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React from "react";
|
||||
import { Star } from "lucide-react";
|
||||
// helpers
|
||||
import { cn } from "@/helpers/common.helper";
|
||||
import { cn } from "../helpers";
|
||||
|
||||
type Props = {
|
||||
buttonClassName?: string;
|
||||
@@ -1,21 +1,22 @@
|
||||
export * from "./avatar";
|
||||
export * from "./breadcrumbs";
|
||||
export * from "./badge";
|
||||
export * from "./breadcrumbs";
|
||||
export * from "./button";
|
||||
export * from "./emoji";
|
||||
export * from "./dropdowns";
|
||||
export * from "./control-link";
|
||||
export * from "./dropdown";
|
||||
export * from "./dropdowns";
|
||||
export * from "./emoji";
|
||||
export * from "./form-fields";
|
||||
export * from "./hooks";
|
||||
export * from "./icons";
|
||||
export * from "./modals";
|
||||
export * from "./progress";
|
||||
export * from "./spinners";
|
||||
export * from "./tooltip";
|
||||
export * from "./loader";
|
||||
export * from "./control-link";
|
||||
export * from "./toast";
|
||||
export * from "./drag-handle";
|
||||
export * from "./typography";
|
||||
export * from "./drop-indicator";
|
||||
export * from "./sortable";
|
||||
export * from "./hooks";
|
||||
export * from "./spinners";
|
||||
export * from "./toast";
|
||||
export * from "./tooltip";
|
||||
export * from "./typography";
|
||||
export * from "./drag-handle";
|
||||
export * from "./drop-indicator";
|
||||
export * from "./favorite-star";
|
||||
export * from "./loader";
|
||||
|
||||
3
web/.gitignore
vendored
Normal file
3
web/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
|
||||
# Sentry Config File
|
||||
.env.sentry-build-plugin
|
||||
@@ -25,7 +25,7 @@ export const ProjectArchivesHeader: FC = observer(() => {
|
||||
const {
|
||||
issuesFilter: { issueFilters },
|
||||
} = useIssues(EIssuesStoreType.ARCHIVED);
|
||||
const { currentProjectDetails } = useProject();
|
||||
const { currentProjectDetails, loader } = useProject();
|
||||
// hooks
|
||||
const { isMobile } = usePlatformOS();
|
||||
|
||||
@@ -42,7 +42,7 @@ export const ProjectArchivesHeader: FC = observer(() => {
|
||||
<div className="relative z-10 flex h-[3.75rem] w-full flex-shrink-0 flex-row items-center justify-between gap-x-2 gap-y-4 bg-custom-sidebar-background-100 p-4">
|
||||
<div className="flex w-full flex-grow items-center gap-2 overflow-ellipsis whitespace-nowrap">
|
||||
<div className="flex items-center gap-2.5">
|
||||
<Breadcrumbs onBack={router.back}>
|
||||
<Breadcrumbs onBack={router.back} isLoading={loader}>
|
||||
<Breadcrumbs.BreadcrumbItem
|
||||
type="text"
|
||||
link={
|
||||
|
||||
@@ -21,7 +21,7 @@ export const ProjectArchivedIssueDetailsHeader = observer(() => {
|
||||
// router
|
||||
const { workspaceSlug, projectId, archivedIssueId } = useParams();
|
||||
// store hooks
|
||||
const { currentProjectDetails } = useProject();
|
||||
const { currentProjectDetails, loader } = useProject();
|
||||
|
||||
const { data: issueDetails } = useSWR(
|
||||
workspaceSlug && projectId && archivedIssueId ? ISSUE_DETAILS(archivedIssueId as string) : null,
|
||||
@@ -39,7 +39,7 @@ export const ProjectArchivedIssueDetailsHeader = observer(() => {
|
||||
<div className="relative z-10 flex h-[3.75rem] w-full flex-shrink-0 flex-row items-center justify-between gap-x-2 gap-y-4 bg-custom-sidebar-background-100 p-4">
|
||||
<div className="flex w-full flex-grow items-center gap-2 overflow-ellipsis whitespace-nowrap">
|
||||
<div>
|
||||
<Breadcrumbs>
|
||||
<Breadcrumbs isLoading={loader}>
|
||||
<Breadcrumbs.BreadcrumbItem
|
||||
type="text"
|
||||
link={
|
||||
|
||||
@@ -15,7 +15,12 @@ import { ProjectAnalyticsModal } from "@/components/analytics";
|
||||
import { BreadcrumbLink, Logo } from "@/components/common";
|
||||
import { DisplayFiltersSelection, FiltersDropdown, FilterSelection, LayoutSelection } from "@/components/issues";
|
||||
// constants
|
||||
import { EIssueFilterType, EIssueLayoutTypes, EIssuesStoreType, ISSUE_DISPLAY_FILTERS_BY_LAYOUT } from "@/constants/issue";
|
||||
import {
|
||||
EIssueFilterType,
|
||||
EIssueLayoutTypes,
|
||||
EIssuesStoreType,
|
||||
ISSUE_DISPLAY_FILTERS_BY_LAYOUT,
|
||||
} from "@/constants/issue";
|
||||
import { EUserProjectRoles } from "@/constants/project";
|
||||
// helpers
|
||||
import { cn } from "@/helpers/common.helper";
|
||||
@@ -79,7 +84,7 @@ export const CycleIssuesHeader: React.FC = observer(() => {
|
||||
const {
|
||||
membership: { currentProjectRole },
|
||||
} = useUser();
|
||||
const { currentProjectDetails } = useProject();
|
||||
const { currentProjectDetails, loader } = useProject();
|
||||
const { projectStates } = useProjectState();
|
||||
const { projectLabels } = useLabel();
|
||||
const {
|
||||
@@ -160,7 +165,7 @@ export const CycleIssuesHeader: React.FC = observer(() => {
|
||||
<div className="relative z-[15] w-full items-center gap-x-2 gap-y-4">
|
||||
<div className="flex justify-between bg-custom-sidebar-background-100 p-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<Breadcrumbs onBack={router.back}>
|
||||
<Breadcrumbs onBack={router.back} isLoading={loader}>
|
||||
<Breadcrumbs.BreadcrumbItem
|
||||
type="text"
|
||||
link={
|
||||
@@ -209,8 +214,9 @@ export const CycleIssuesHeader: React.FC = observer(() => {
|
||||
{issuesCount && issuesCount > 0 ? (
|
||||
<Tooltip
|
||||
isMobile={isMobile}
|
||||
tooltipContent={`There are ${issuesCount} ${issuesCount > 1 ? "issues" : "issue"
|
||||
} in this cycle`}
|
||||
tooltipContent={`There are ${issuesCount} ${
|
||||
issuesCount > 1 ? "issues" : "issue"
|
||||
} in this cycle`}
|
||||
position="bottom"
|
||||
>
|
||||
<span className="flex flex-shrink-0 cursor-default items-center justify-center rounded-xl bg-custom-primary-100/20 px-2 text-center text-xs font-semibold text-custom-primary-100">
|
||||
|
||||
@@ -24,7 +24,7 @@ export const CyclesListHeader: FC = observer(() => {
|
||||
const {
|
||||
membership: { currentProjectRole },
|
||||
} = useUser();
|
||||
const { currentProjectDetails } = useProject();
|
||||
const { currentProjectDetails, loader } = useProject();
|
||||
|
||||
const canUserCreateCycle =
|
||||
currentProjectRole && [EUserProjectRoles.ADMIN, EUserProjectRoles.MEMBER].includes(currentProjectRole);
|
||||
@@ -33,7 +33,7 @@ export const CyclesListHeader: FC = observer(() => {
|
||||
<div className="relative z-10 flex h-[3.75rem] w-full flex-shrink-0 flex-row items-center justify-between gap-x-2 gap-y-4 bg-custom-sidebar-background-100 p-4">
|
||||
<div className="flex w-full flex-grow items-center gap-2 overflow-ellipsis whitespace-nowrap">
|
||||
<div>
|
||||
<Breadcrumbs onBack={router.back}>
|
||||
<Breadcrumbs onBack={router.back} isLoading={loader}>
|
||||
<Breadcrumbs.BreadcrumbItem
|
||||
type="text"
|
||||
link={
|
||||
|
||||
@@ -11,7 +11,12 @@ import { Breadcrumbs, LayersIcon, Tooltip } from "@plane/ui";
|
||||
import { BreadcrumbLink, Logo } from "@/components/common";
|
||||
import { DisplayFiltersSelection, FiltersDropdown, FilterSelection, LayoutSelection } from "@/components/issues";
|
||||
// constants
|
||||
import { EIssueFilterType, EIssuesStoreType, EIssueLayoutTypes, ISSUE_DISPLAY_FILTERS_BY_LAYOUT } from "@/constants/issue";
|
||||
import {
|
||||
EIssueFilterType,
|
||||
EIssuesStoreType,
|
||||
EIssueLayoutTypes,
|
||||
ISSUE_DISPLAY_FILTERS_BY_LAYOUT,
|
||||
} from "@/constants/issue";
|
||||
// helpers
|
||||
import { calculateTotalFilters } from "@/helpers/filter.helper";
|
||||
// hooks
|
||||
@@ -25,7 +30,7 @@ export const ProjectDraftIssueHeader: FC = observer(() => {
|
||||
const {
|
||||
issuesFilter: { issueFilters, updateFilters },
|
||||
} = useIssues(EIssuesStoreType.DRAFT);
|
||||
const { currentProjectDetails } = useProject();
|
||||
const { currentProjectDetails, loader } = useProject();
|
||||
const { projectStates } = useProjectState();
|
||||
const { projectLabels } = useLabel();
|
||||
const {
|
||||
@@ -91,7 +96,7 @@ export const ProjectDraftIssueHeader: FC = observer(() => {
|
||||
<div className="relative z-10 flex h-[3.75rem] w-full flex-shrink-0 flex-row items-center justify-between gap-x-2 gap-y-4 bg-custom-sidebar-background-100 p-4">
|
||||
<div className="flex w-full flex-grow items-center gap-2 overflow-ellipsis whitespace-nowrap">
|
||||
<div className="flex items-center gap-2.5">
|
||||
<Breadcrumbs>
|
||||
<Breadcrumbs isLoading={loader}>
|
||||
<Breadcrumbs.BreadcrumbItem
|
||||
type="text"
|
||||
link={
|
||||
|
||||
@@ -18,14 +18,14 @@ export const ProjectInboxHeader: FC = observer(() => {
|
||||
// router
|
||||
const { workspaceSlug, projectId } = useParams();
|
||||
// store hooks
|
||||
const { currentProjectDetails } = useProject();
|
||||
const { currentProjectDetails, loader: currentProjectDetailsLoader } = useProject();
|
||||
const { loader } = useProjectInbox();
|
||||
|
||||
return (
|
||||
<div className="relative z-10 flex h-[3.75rem] w-full flex-shrink-0 flex-row items-center justify-between gap-x-2 gap-y-4 bg-custom-sidebar-background-100 p-4">
|
||||
<div className="flex w-full flex-grow items-center gap-2 overflow-ellipsis whitespace-nowrap">
|
||||
<div className="flex items-center gap-4">
|
||||
<Breadcrumbs>
|
||||
<Breadcrumbs isLoading={currentProjectDetailsLoader}>
|
||||
<Breadcrumbs.BreadcrumbItem
|
||||
type="text"
|
||||
link={
|
||||
|
||||
@@ -19,7 +19,7 @@ export const ProjectIssueDetailsHeader = observer(() => {
|
||||
const router = useAppRouter();
|
||||
const { workspaceSlug, projectId, issueId } = useParams();
|
||||
// store hooks
|
||||
const { currentProjectDetails } = useProject();
|
||||
const { currentProjectDetails, loader } = useProject();
|
||||
const { issueDetailSidebarCollapsed, toggleIssueDetailSidebar } = useAppTheme();
|
||||
const {
|
||||
issue: { getIssueById },
|
||||
@@ -32,7 +32,7 @@ export const ProjectIssueDetailsHeader = observer(() => {
|
||||
<div className="relative z-10 flex h-[3.75rem] w-full flex-shrink-0 flex-row items-center justify-between gap-x-2 gap-y-4 bg-custom-sidebar-background-100 p-4">
|
||||
<div className="flex w-full flex-grow items-center gap-2 overflow-ellipsis whitespace-nowrap">
|
||||
<div>
|
||||
<Breadcrumbs onBack={router.back}>
|
||||
<Breadcrumbs onBack={router.back} isLoading={loader}>
|
||||
<Breadcrumbs.BreadcrumbItem
|
||||
type="text"
|
||||
link={
|
||||
|
||||
@@ -14,7 +14,12 @@ import { ProjectAnalyticsModal } from "@/components/analytics";
|
||||
import { BreadcrumbLink, Logo } from "@/components/common";
|
||||
import { DisplayFiltersSelection, FiltersDropdown, FilterSelection, LayoutSelection } from "@/components/issues";
|
||||
// constants
|
||||
import { EIssueFilterType, EIssuesStoreType, EIssueLayoutTypes, ISSUE_DISPLAY_FILTERS_BY_LAYOUT } from "@/constants/issue";
|
||||
import {
|
||||
EIssueFilterType,
|
||||
EIssuesStoreType,
|
||||
EIssueLayoutTypes,
|
||||
ISSUE_DISPLAY_FILTERS_BY_LAYOUT,
|
||||
} from "@/constants/issue";
|
||||
import { EUserProjectRoles } from "@/constants/project";
|
||||
// helpers
|
||||
import { SPACE_BASE_URL } from "@/helpers/common.helper";
|
||||
@@ -52,7 +57,7 @@ export const ProjectIssuesHeader = observer(() => {
|
||||
const {
|
||||
membership: { currentProjectRole },
|
||||
} = useUser();
|
||||
const { currentProjectDetails } = useProject();
|
||||
const { currentProjectDetails, loader } = useProject();
|
||||
const { projectStates } = useProjectState();
|
||||
const { projectLabels } = useLabel();
|
||||
const { isMobile } = usePlatformOS();
|
||||
@@ -122,7 +127,7 @@ export const ProjectIssuesHeader = observer(() => {
|
||||
<div className="relative z-[15] flex h-[3.75rem] w-full flex-shrink-0 flex-row items-center justify-between gap-x-2 gap-y-4 bg-custom-sidebar-background-100 p-4">
|
||||
<div className="flex w-full flex-grow items-center gap-2 overflow-ellipsis whitespace-nowrap">
|
||||
<div className="flex items-center gap-2.5">
|
||||
<Breadcrumbs onBack={() => router.back()}>
|
||||
<Breadcrumbs onBack={() => router.back()} isLoading={loader}>
|
||||
<Breadcrumbs.BreadcrumbItem
|
||||
type="text"
|
||||
link={
|
||||
@@ -178,7 +183,8 @@ export const ProjectIssuesHeader = observer(() => {
|
||||
</div>
|
||||
<div className="items-center gap-2 hidden md:flex">
|
||||
<LayoutSelection
|
||||
layouts={[EIssueLayoutTypes.LIST,
|
||||
layouts={[
|
||||
EIssueLayoutTypes.LIST,
|
||||
EIssueLayoutTypes.KANBAN,
|
||||
EIssueLayoutTypes.CALENDAR,
|
||||
EIssueLayoutTypes.SPREADSHEET,
|
||||
|
||||
@@ -15,7 +15,12 @@ import { ProjectAnalyticsModal } from "@/components/analytics";
|
||||
import { BreadcrumbLink, Logo } from "@/components/common";
|
||||
import { DisplayFiltersSelection, FiltersDropdown, FilterSelection, LayoutSelection } from "@/components/issues";
|
||||
// constants
|
||||
import { EIssuesStoreType, EIssueFilterType, EIssueLayoutTypes, ISSUE_DISPLAY_FILTERS_BY_LAYOUT } from "@/constants/issue";
|
||||
import {
|
||||
EIssuesStoreType,
|
||||
EIssueFilterType,
|
||||
EIssueLayoutTypes,
|
||||
ISSUE_DISPLAY_FILTERS_BY_LAYOUT,
|
||||
} from "@/constants/issue";
|
||||
import { EUserProjectRoles } from "@/constants/project";
|
||||
// helpers
|
||||
import { cn } from "@/helpers/common.helper";
|
||||
@@ -81,7 +86,7 @@ export const ModuleIssuesHeader: React.FC = observer(() => {
|
||||
const {
|
||||
membership: { currentProjectRole },
|
||||
} = useUser();
|
||||
const { currentProjectDetails } = useProject();
|
||||
const { currentProjectDetails, loader } = useProject();
|
||||
const { projectLabels } = useLabel();
|
||||
const { projectStates } = useProjectState();
|
||||
const {
|
||||
@@ -160,7 +165,7 @@ export const ModuleIssuesHeader: React.FC = observer(() => {
|
||||
<div className="relative z-[15] items-center gap-x-2 gap-y-4">
|
||||
<div className="flex justify-between bg-custom-sidebar-background-100 p-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<Breadcrumbs onBack={router.back}>
|
||||
<Breadcrumbs onBack={router.back} isLoading={loader}>
|
||||
<Breadcrumbs.BreadcrumbItem
|
||||
type="text"
|
||||
link={
|
||||
@@ -209,8 +214,9 @@ export const ModuleIssuesHeader: React.FC = observer(() => {
|
||||
{issuesCount && issuesCount > 0 ? (
|
||||
<Tooltip
|
||||
isMobile={isMobile}
|
||||
tooltipContent={`There are ${issuesCount} ${issuesCount > 1 ? "issues" : "issue"
|
||||
} in this module`}
|
||||
tooltipContent={`There are ${issuesCount} ${
|
||||
issuesCount > 1 ? "issues" : "issue"
|
||||
} in this module`}
|
||||
position="bottom"
|
||||
>
|
||||
<span className="flex flex-shrink-0 cursor-default items-center justify-center rounded-xl bg-custom-primary-100/20 px-2 text-center text-xs font-semibold text-custom-primary-100">
|
||||
|
||||
@@ -23,7 +23,7 @@ export const ModulesListHeader: React.FC = observer(() => {
|
||||
const {
|
||||
membership: { currentProjectRole },
|
||||
} = useUser();
|
||||
const { currentProjectDetails } = useProject();
|
||||
const { currentProjectDetails, loader } = useProject();
|
||||
|
||||
// auth
|
||||
const canUserCreateModule =
|
||||
@@ -33,7 +33,7 @@ export const ModulesListHeader: React.FC = observer(() => {
|
||||
<div className="relative z-10 flex h-[3.75rem] w-full flex-shrink-0 flex-row items-center justify-between gap-x-2 gap-y-4 bg-custom-sidebar-background-100 p-4">
|
||||
<div className="flex w-full flex-grow items-center gap-2 overflow-ellipsis whitespace-nowrap">
|
||||
<div>
|
||||
<Breadcrumbs onBack={router.back}>
|
||||
<Breadcrumbs onBack={router.back} isLoading={loader}>
|
||||
<Breadcrumbs.BreadcrumbItem
|
||||
type="text"
|
||||
link={
|
||||
|
||||
@@ -28,7 +28,7 @@ export const PageDetailsHeader = observer(() => {
|
||||
// state
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
// store hooks
|
||||
const { currentProjectDetails } = useProject();
|
||||
const { currentProjectDetails, loader } = useProject();
|
||||
const { isContentEditable, isSubmitting, name, logo_props, updatePageLogo } = usePage(pageId?.toString() ?? "");
|
||||
// use platform
|
||||
const { platform } = usePlatformOS();
|
||||
@@ -59,7 +59,7 @@ export const PageDetailsHeader = observer(() => {
|
||||
<div className="relative z-10 flex h-[3.75rem] w-full flex-shrink-0 flex-row items-center justify-between gap-x-2 gap-y-4 bg-custom-sidebar-background-100 p-4">
|
||||
<div className="flex w-full flex-grow items-center gap-2 overflow-ellipsis whitespace-nowrap">
|
||||
<div>
|
||||
<Breadcrumbs>
|
||||
<Breadcrumbs isLoading={loader}>
|
||||
<Breadcrumbs.BreadcrumbItem
|
||||
type="text"
|
||||
link={
|
||||
|
||||
@@ -23,7 +23,7 @@ export const PagesListHeader = observer(() => {
|
||||
const {
|
||||
membership: { currentProjectRole },
|
||||
} = useUser();
|
||||
const { currentProjectDetails } = useProject();
|
||||
const { currentProjectDetails, loader } = useProject();
|
||||
const { setTrackElement } = useEventTracker();
|
||||
|
||||
const canUserCreatePage =
|
||||
@@ -33,7 +33,7 @@ export const PagesListHeader = observer(() => {
|
||||
<div className="relative z-10 flex h-[3.75rem] w-full flex-shrink-0 flex-row items-center justify-between gap-x-2 gap-y-4 bg-custom-sidebar-background-100 p-4">
|
||||
<div className="flex w-full flex-grow items-center gap-2 overflow-ellipsis whitespace-nowrap">
|
||||
<div>
|
||||
<Breadcrumbs>
|
||||
<Breadcrumbs isLoading={loader}>
|
||||
<Breadcrumbs.BreadcrumbItem
|
||||
type="text"
|
||||
link={
|
||||
|
||||
@@ -22,7 +22,7 @@ export const ProjectSettingHeader: FC = observer(() => {
|
||||
const {
|
||||
membership: { currentProjectRole },
|
||||
} = useUser();
|
||||
const { currentProjectDetails } = useProject();
|
||||
const { currentProjectDetails, loader } = useProject();
|
||||
|
||||
if (currentProjectRole && currentProjectRole <= EUserProjectRoles.VIEWER) return null;
|
||||
|
||||
@@ -31,7 +31,7 @@ export const ProjectSettingHeader: FC = observer(() => {
|
||||
<div className="flex w-full flex-grow items-center gap-2 overflow-ellipsis whitespace-nowrap">
|
||||
<div>
|
||||
<div className="z-50">
|
||||
<Breadcrumbs onBack={router.back}>
|
||||
<Breadcrumbs onBack={router.back} isLoading={loader}>
|
||||
<Breadcrumbs.BreadcrumbItem
|
||||
type="text"
|
||||
link={
|
||||
|
||||
@@ -12,7 +12,12 @@ import { Breadcrumbs, Button, CustomMenu, PhotoFilterIcon } from "@plane/ui";
|
||||
import { BreadcrumbLink, Logo } from "@/components/common";
|
||||
import { DisplayFiltersSelection, FiltersDropdown, FilterSelection, LayoutSelection } from "@/components/issues";
|
||||
// constants
|
||||
import { EIssuesStoreType, EIssueFilterType, EIssueLayoutTypes, ISSUE_DISPLAY_FILTERS_BY_LAYOUT } from "@/constants/issue";
|
||||
import {
|
||||
EIssuesStoreType,
|
||||
EIssueFilterType,
|
||||
EIssueLayoutTypes,
|
||||
ISSUE_DISPLAY_FILTERS_BY_LAYOUT,
|
||||
} from "@/constants/issue";
|
||||
import { EUserProjectRoles } from "@/constants/project";
|
||||
// helpers
|
||||
import { calculateTotalFilters } from "@/helpers/filter.helper";
|
||||
@@ -42,7 +47,7 @@ export const ProjectViewIssuesHeader: React.FC = observer(() => {
|
||||
const {
|
||||
membership: { currentProjectRole },
|
||||
} = useUser();
|
||||
const { currentProjectDetails } = useProject();
|
||||
const { currentProjectDetails, loader } = useProject();
|
||||
const { projectViewIds, getViewById } = useProjectView();
|
||||
const { projectStates } = useProjectState();
|
||||
const { projectLabels } = useLabel();
|
||||
@@ -131,7 +136,7 @@ export const ProjectViewIssuesHeader: React.FC = observer(() => {
|
||||
return (
|
||||
<div className="relative z-[15] flex h-[3.75rem] w-full items-center justify-between gap-x-2 gap-y-4 bg-custom-sidebar-background-100 p-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<Breadcrumbs>
|
||||
<Breadcrumbs isLoading={loader}>
|
||||
<Breadcrumbs.BreadcrumbItem
|
||||
type="text"
|
||||
link={
|
||||
|
||||
@@ -20,7 +20,7 @@ export const ProjectViewsHeader = observer(() => {
|
||||
const {
|
||||
membership: { currentProjectRole },
|
||||
} = useUser();
|
||||
const { currentProjectDetails } = useProject();
|
||||
const { currentProjectDetails, loader } = useProject();
|
||||
|
||||
const canUserCreateIssue =
|
||||
currentProjectRole && [EUserProjectRoles.ADMIN, EUserProjectRoles.MEMBER].includes(currentProjectRole);
|
||||
@@ -30,7 +30,7 @@ export const ProjectViewsHeader = observer(() => {
|
||||
<div className="relative z-10 flex h-[3.75rem] w-full flex-shrink-0 flex-row items-center justify-between gap-x-2 gap-y-4 bg-custom-sidebar-background-100 p-4">
|
||||
<div className="flex w-full flex-grow items-center gap-2 overflow-ellipsis whitespace-nowrap">
|
||||
<div>
|
||||
<Breadcrumbs>
|
||||
<Breadcrumbs isLoading={loader}>
|
||||
<Breadcrumbs.BreadcrumbItem
|
||||
type="text"
|
||||
link={
|
||||
|
||||
@@ -11,13 +11,13 @@ import { BreadcrumbLink } from "@/components/common";
|
||||
import { useWorkspace } from "@/hooks/store";
|
||||
|
||||
export const WorkspaceSettingHeader: FC = observer(() => {
|
||||
const { currentWorkspace } = useWorkspace();
|
||||
const { currentWorkspace, loader } = useWorkspace();
|
||||
|
||||
return (
|
||||
<div className="relative z-10 flex h-[3.75rem] w-full flex-shrink-0 flex-row items-center justify-between gap-x-2 gap-y-4 bg-custom-sidebar-background-100 p-4">
|
||||
<div className="flex w-full flex-grow items-center gap-2 overflow-ellipsis whitespace-nowrap">
|
||||
<div>
|
||||
<Breadcrumbs>
|
||||
<Breadcrumbs isLoading={loader}>
|
||||
<Breadcrumbs.BreadcrumbItem
|
||||
type="text"
|
||||
link={
|
||||
|
||||
@@ -1,49 +1,37 @@
|
||||
"use client";
|
||||
|
||||
// import { useEffect } from "react";
|
||||
// import * as Sentry from "@sentry/nextjs";
|
||||
// services
|
||||
import { Button } from "@plane/ui";
|
||||
// ui
|
||||
import { Button, TOAST_TYPE, setToast } from "@plane/ui";
|
||||
// helpers
|
||||
// import { API_BASE_URL } from "@/helpers/common.helper";
|
||||
import { API_BASE_URL } from "@/helpers/common.helper";
|
||||
// hooks
|
||||
import { useAppRouter } from "@/hooks/use-app-router";
|
||||
// layouts
|
||||
import DefaultLayout from "@/layouts/default-layout";
|
||||
//
|
||||
// import { AuthService } from "@/services/auth.service";
|
||||
// layouts
|
||||
// ui
|
||||
// services
|
||||
import { AuthService } from "@/services/auth.service";
|
||||
|
||||
// services
|
||||
// const authService = new AuthService();
|
||||
const authService = new AuthService();
|
||||
|
||||
// type props = {
|
||||
// error: Error & { digest?: string };
|
||||
// };
|
||||
|
||||
// TODO: adding error sentry logging.
|
||||
// const CustomErrorComponent = ({ error }: props) => {
|
||||
const CustomErrorComponent = () => {
|
||||
// const router = useAppRouter();
|
||||
|
||||
// useEffect(() => {
|
||||
// Sentry.captureException(error);
|
||||
// }, [error]);
|
||||
export default function CustomErrorComponent() {
|
||||
const router = useAppRouter();
|
||||
|
||||
const handleRefresh = () => {
|
||||
window.location.reload();
|
||||
};
|
||||
|
||||
const handleSignOut = async () => {
|
||||
// await authService
|
||||
// .signOut(API_BASE_URL)
|
||||
// .catch(() =>
|
||||
// setToast({
|
||||
// type: TOAST_TYPE.ERROR,
|
||||
// title: "Error!",
|
||||
// message: "Failed to sign out. Please try again.",
|
||||
// })
|
||||
// )
|
||||
// .finally(() => router.push("/"));
|
||||
await authService
|
||||
.signOut(API_BASE_URL)
|
||||
.catch(() =>
|
||||
setToast({
|
||||
type: TOAST_TYPE.ERROR,
|
||||
title: "Error!",
|
||||
message: "Failed to sign out. Please try again.",
|
||||
})
|
||||
)
|
||||
.finally(() => router.push("/"));
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -84,6 +72,4 @@ const CustomErrorComponent = () => {
|
||||
</div>
|
||||
</DefaultLayout>
|
||||
);
|
||||
};
|
||||
|
||||
export default CustomErrorComponent;
|
||||
}
|
||||
|
||||
23
web/app/global-error.tsx
Normal file
23
web/app/global-error.tsx
Normal file
@@ -0,0 +1,23 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect } from "react";
|
||||
import * as Sentry from "@sentry/nextjs";
|
||||
import NextError from "next/error";
|
||||
|
||||
export default function GlobalError({ error }: { error: Error & { digest?: string } }) {
|
||||
useEffect(() => {
|
||||
Sentry.captureException(error);
|
||||
}, [error]);
|
||||
|
||||
return (
|
||||
<html>
|
||||
<body>
|
||||
{/* `NextError` is the default Next.js error page component. Its type
|
||||
definition requires a `statusCode` prop. However, since the App Router
|
||||
does not expose status codes for errors, we simply pass 0 to render a
|
||||
generic error message. */}
|
||||
<NextError statusCode={0} />
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
@@ -3,7 +3,6 @@ export * from "./modals";
|
||||
export * from "./multiple-select";
|
||||
export * from "./sidebar";
|
||||
export * from "./activity";
|
||||
export * from "./favorite-star";
|
||||
export * from "./theme";
|
||||
export * from "./image-picker-popover";
|
||||
export * from "./page-title";
|
||||
|
||||
@@ -6,9 +6,8 @@ import Link from "next/link";
|
||||
import { useParams } from "next/navigation";
|
||||
import { Users } from "lucide-react";
|
||||
// ui
|
||||
import { Avatar, AvatarGroup, setPromiseToast } from "@plane/ui";
|
||||
import { Avatar, AvatarGroup, FavoriteStar, setPromiseToast } from "@plane/ui";
|
||||
// components
|
||||
import { FavoriteStar } from "@/components/core";
|
||||
import { CycleQuickActions } from "@/components/cycles";
|
||||
// constants
|
||||
import { CYCLE_FAVORITED, CYCLE_UNFAVORITED } from "@/constants/event-tracker";
|
||||
|
||||
@@ -8,9 +8,8 @@ import { CalendarCheck2, CalendarClock, Info, MoveRight } from "lucide-react";
|
||||
// types
|
||||
import type { TCycleGroups } from "@plane/types";
|
||||
// ui
|
||||
import { Avatar, AvatarGroup, Tooltip, LayersIcon, CycleGroupIcon, setPromiseToast } from "@plane/ui";
|
||||
import { Avatar, AvatarGroup, Tooltip, LayersIcon, CycleGroupIcon, setPromiseToast, FavoriteStar } from "@plane/ui";
|
||||
// components
|
||||
import { FavoriteStar } from "@/components/core";
|
||||
import { CycleQuickActions } from "@/components/cycles";
|
||||
// constants
|
||||
import { CYCLE_STATUS } from "@/constants/cycle";
|
||||
|
||||
@@ -6,9 +6,8 @@ import { CalendarCheck2, CalendarClock, MoveRight, Users } from "lucide-react";
|
||||
// types
|
||||
import { ICycle, TCycleGroups } from "@plane/types";
|
||||
// ui
|
||||
import { Avatar, AvatarGroup, Tooltip, setPromiseToast } from "@plane/ui";
|
||||
import { Avatar, AvatarGroup, FavoriteStar, Tooltip, setPromiseToast } from "@plane/ui";
|
||||
// components
|
||||
import { FavoriteStar } from "@/components/core";
|
||||
import { CycleQuickActions } from "@/components/cycles";
|
||||
import { ButtonAvatars } from "@/components/dropdowns/member/avatar";
|
||||
// constants
|
||||
|
||||
@@ -6,9 +6,8 @@ import Link from "next/link";
|
||||
import { useParams, usePathname, useSearchParams } from "next/navigation";
|
||||
import { CalendarCheck2, CalendarClock, Info, MoveRight, SquareUser } from "lucide-react";
|
||||
// ui
|
||||
import { LayersIcon, Tooltip, setPromiseToast } from "@plane/ui";
|
||||
import { FavoriteStar, LayersIcon, Tooltip, setPromiseToast } from "@plane/ui";
|
||||
// components
|
||||
import { FavoriteStar } from "@/components/core";
|
||||
import { ButtonAvatars } from "@/components/dropdowns/member/avatar";
|
||||
import { ModuleQuickActions } from "@/components/modules";
|
||||
// constants
|
||||
|
||||
@@ -8,9 +8,8 @@ import { CalendarCheck2, CalendarClock, MoveRight, SquareUser } from "lucide-rea
|
||||
// types
|
||||
import { IModule } from "@plane/types";
|
||||
// ui
|
||||
import { Tooltip, setPromiseToast } from "@plane/ui";
|
||||
import { FavoriteStar, Tooltip, setPromiseToast } from "@plane/ui";
|
||||
// components
|
||||
import { FavoriteStar } from "@/components/core";
|
||||
import { ModuleQuickActions } from "@/components/modules";
|
||||
// constants
|
||||
import { MODULE_FAVORITED, MODULE_UNFAVORITED } from "@/constants/event-tracker";
|
||||
|
||||
@@ -4,9 +4,8 @@ import React, { FC } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { Earth, Info, Lock, Minus } from "lucide-react";
|
||||
// ui
|
||||
import { Avatar, TOAST_TYPE, Tooltip, setToast } from "@plane/ui";
|
||||
import { Avatar, FavoriteStar, TOAST_TYPE, Tooltip, setToast } from "@plane/ui";
|
||||
// components
|
||||
import { FavoriteStar } from "@/components/core";
|
||||
import { PageQuickActions } from "@/components/pages/dropdowns";
|
||||
// helpers
|
||||
import { renderFormattedDate } from "@/helpers/date-time.helper";
|
||||
|
||||
@@ -18,10 +18,10 @@ import {
|
||||
setPromiseToast,
|
||||
ContextMenu,
|
||||
TContextMenuItem,
|
||||
FavoriteStar,
|
||||
} from "@plane/ui";
|
||||
// components
|
||||
import { Logo } from "@/components/common";
|
||||
import { FavoriteStar } from "@/components/core";
|
||||
import { ArchiveRestoreProjectModal, DeleteProjectModal, JoinProjectModal } from "@/components/project";
|
||||
// constants
|
||||
import { EUserProjectRoles } from "@/constants/project";
|
||||
|
||||
@@ -3,8 +3,9 @@ import { observer } from "mobx-react";
|
||||
import { useParams } from "next/navigation";
|
||||
// types
|
||||
import { IProjectView } from "@plane/types";
|
||||
// ui
|
||||
import { FavoriteStar } from "@plane/ui";
|
||||
// components
|
||||
import { FavoriteStar } from "@/components/core";
|
||||
import { DeleteProjectViewModal, CreateUpdateProjectViewModal, ViewQuickActions } from "@/components/views";
|
||||
// constants
|
||||
import { EUserProjectRoles } from "@/constants/project";
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"develop": "next dev --port 3000",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "eslint . --ext .ts,.tsx,.js,.jsx",
|
||||
"lint": "next lint --dir .",
|
||||
"export": "next export",
|
||||
"clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist"
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user