mirror of
https://github.com/makeplane/plane.git
synced 2026-07-13 14:01:45 +02:00
@@ -92,6 +92,7 @@ class ServiceApiTokenEndpoint(BaseAPIView):
|
||||
api_token = APIToken.objects.filter(
|
||||
workspace=workspace,
|
||||
is_service=True,
|
||||
user=request.user,
|
||||
).first()
|
||||
|
||||
if api_token:
|
||||
@@ -119,4 +120,3 @@ class ServiceApiTokenEndpoint(BaseAPIView):
|
||||
},
|
||||
status=status.HTTP_201_CREATED,
|
||||
)
|
||||
|
||||
|
||||
@@ -19,11 +19,13 @@ from django.contrib.postgres.aggregates import ArrayAgg
|
||||
from django.contrib.postgres.fields import ArrayField
|
||||
|
||||
# Module Imports
|
||||
from plane.graphql.types.search import ProjectSearchType
|
||||
from plane.db.models import Issue, Project, Page
|
||||
from plane.graphql.types.search import GlobalSearchType
|
||||
from plane.db.models import Issue, Project, Page, Module, Cycle
|
||||
from plane.graphql.types.project import ProjectLiteType
|
||||
from plane.graphql.types.issue import IssueLiteType
|
||||
from plane.graphql.types.page import PageLiteType
|
||||
from plane.graphql.types.module import ModuleLiteType
|
||||
from plane.graphql.types.cycle import CycleLiteType
|
||||
from plane.graphql.permissions.workspace import WorkspaceBasePermission
|
||||
|
||||
|
||||
@@ -106,43 +108,60 @@ async def filter_pages(query: str, slug: str, user) -> list[PageLiteType]:
|
||||
projects__project_projectmember__is_active=True,
|
||||
projects__archived_at__isnull=True,
|
||||
workspace__slug=slug,
|
||||
)
|
||||
.annotate(
|
||||
project_ids=Coalesce(
|
||||
ArrayAgg(
|
||||
"projects__id",
|
||||
distinct=True,
|
||||
filter=~Q(projects__id=True),
|
||||
),
|
||||
Value([], output_field=ArrayField(UUIDField())),
|
||||
),
|
||||
)
|
||||
.annotate(
|
||||
project_identifiers=Coalesce(
|
||||
ArrayAgg(
|
||||
"projects__identifier",
|
||||
distinct=True,
|
||||
filter=~Q(projects__id=True),
|
||||
),
|
||||
Value([], output_field=ArrayField(CharField())),
|
||||
),
|
||||
)
|
||||
.distinct()
|
||||
.values(
|
||||
"name",
|
||||
"id",
|
||||
"project_ids",
|
||||
# "project_identifiers",
|
||||
# "workspace__slug",
|
||||
)
|
||||
).distinct()
|
||||
)
|
||||
)()
|
||||
return [PageLiteType(**page) for page in pages]
|
||||
return pages
|
||||
|
||||
|
||||
async def filter_modules(query: str, slug: str, user) -> list[ModuleLiteType]:
|
||||
fields = ["name"]
|
||||
q = Q()
|
||||
for field in fields:
|
||||
q |= Q(**{f"{field}__icontains": query})
|
||||
|
||||
modules = await sync_to_async(
|
||||
lambda: list(
|
||||
Module.objects.filter(
|
||||
q,
|
||||
project__project_projectmember__member=user,
|
||||
project__project_projectmember__is_active=True,
|
||||
workspace__slug=slug,
|
||||
archived_at__isnull=True,
|
||||
)
|
||||
.distinct()
|
||||
.values("id", "name", "project")
|
||||
)
|
||||
)()
|
||||
|
||||
return [ModuleLiteType(**module) for module in modules]
|
||||
|
||||
|
||||
async def filter_cycles(query: str, slug: str, user) -> list[CycleLiteType]:
|
||||
fields = ["name"]
|
||||
q = Q()
|
||||
for field in fields:
|
||||
q |= Q(**{f"{field}__icontains": query})
|
||||
|
||||
cycles = await sync_to_async(
|
||||
lambda: list(
|
||||
Cycle.objects.filter(
|
||||
q,
|
||||
project__project_projectmember__member=user,
|
||||
project__project_projectmember__is_active=True,
|
||||
archived_at__isnull=True,
|
||||
workspace__slug=slug,
|
||||
)
|
||||
.distinct()
|
||||
.values("id", "name", "project")
|
||||
)
|
||||
)()
|
||||
|
||||
return [CycleLiteType(**cycle) for cycle in cycles]
|
||||
|
||||
|
||||
@strawberry.type
|
||||
class ProjectSearchQuery:
|
||||
|
||||
class GlobalSearchQuery:
|
||||
@strawberry.field(
|
||||
extensions=[
|
||||
PermissionExtension(permissions=[WorkspaceBasePermission()])
|
||||
@@ -153,15 +172,22 @@ class ProjectSearchQuery:
|
||||
info: Info,
|
||||
slug: str,
|
||||
query: Optional[str] = None,
|
||||
) -> ProjectSearchType:
|
||||
|
||||
) -> GlobalSearchType:
|
||||
user = info.context.user
|
||||
if not query:
|
||||
return ProjectSearchType(projects=[], issues=[])
|
||||
return GlobalSearchType(projects=[], issues=[])
|
||||
|
||||
projects = await filter_projects(query, slug, user)
|
||||
issues = await filter_issues(query, slug, user)
|
||||
pages = await filter_pages(query, slug, user)
|
||||
modules = await filter_modules(query, slug, user)
|
||||
cycles = await filter_cycles(query, slug, user)
|
||||
|
||||
# Return the ProjectSearchType with the list of ProjectLiteType objects
|
||||
return ProjectSearchType(projects=projects, issues=issues, pages=pages)
|
||||
# Return the GlobalSearchType with the list of ProjectLiteType objects
|
||||
return GlobalSearchType(
|
||||
projects=projects,
|
||||
issues=issues,
|
||||
pages=pages,
|
||||
modules=modules,
|
||||
cycles=cycles,
|
||||
)
|
||||
|
||||
@@ -38,7 +38,7 @@ from .queries.module import (
|
||||
ModuleIssueQuery,
|
||||
ModuleIssueUserPropertyQuery,
|
||||
)
|
||||
from .queries.search import ProjectSearchQuery
|
||||
from .queries.search import GlobalSearchQuery
|
||||
from .queries.attachment import IssueAttachmentQuery
|
||||
from .queries.link import IssueLinkQuery
|
||||
from .queries.estimate import EstimatePointQuery
|
||||
@@ -94,7 +94,7 @@ class Query(
|
||||
PageQuery,
|
||||
WorkspaceLabelQuery,
|
||||
WorkspaceStateQuery,
|
||||
ProjectSearchQuery,
|
||||
GlobalSearchQuery,
|
||||
IssueAttachmentQuery,
|
||||
CycleQuery,
|
||||
CycleIssueQuery,
|
||||
|
||||
@@ -85,6 +85,13 @@ class CycleType:
|
||||
return issue_assignees_count
|
||||
|
||||
|
||||
@strawberry_django.type(Cycle)
|
||||
class CycleLiteType:
|
||||
id: strawberry.ID
|
||||
name: str
|
||||
project: strawberry.ID
|
||||
|
||||
|
||||
@strawberry_django.type(CycleUserProperties)
|
||||
class CycleUserPropertyType:
|
||||
display_filters: JSON
|
||||
|
||||
@@ -242,10 +242,6 @@ class IssueLiteType:
|
||||
def workspace(self) -> int:
|
||||
return self.workspace_id
|
||||
|
||||
# @strawberry.field
|
||||
# def project(self) -> int:
|
||||
# return self.project_id
|
||||
|
||||
|
||||
@strawberry_django.type(IssueType)
|
||||
class IssueTypesType:
|
||||
@@ -258,7 +254,6 @@ class IssueTypesType:
|
||||
level: int
|
||||
is_active: bool
|
||||
|
||||
|
||||
@strawberry.field
|
||||
def workspace(self) -> int:
|
||||
return self.workspace_id
|
||||
|
||||
@@ -87,6 +87,13 @@ class ModuleType:
|
||||
return issue_assignees_count
|
||||
|
||||
|
||||
@strawberry_django.type(Module)
|
||||
class ModuleLiteType:
|
||||
id: strawberry.ID
|
||||
name: str
|
||||
project: strawberry.ID
|
||||
|
||||
|
||||
@strawberry_django.type(ModuleUserProperties)
|
||||
class ModuleUserPropertyType:
|
||||
display_filters: JSON
|
||||
|
||||
@@ -12,6 +12,7 @@ from strawberry.scalars import JSON
|
||||
|
||||
# Module imports
|
||||
from plane.db.models import Page
|
||||
from plane.graphql.types.project import ProjectLiteType
|
||||
|
||||
|
||||
@strawberry_django.type(Page)
|
||||
@@ -71,5 +72,7 @@ class PageType:
|
||||
class PageLiteType:
|
||||
id: strawberry.ID
|
||||
name: str
|
||||
project_ids: list[strawberry.ID]
|
||||
# owned_by: strawberry.ID
|
||||
|
||||
@strawberry.field
|
||||
def projects(self) -> list[ProjectLiteType]:
|
||||
return self.projects.all()
|
||||
|
||||
@@ -118,10 +118,3 @@ class ProjectLiteType:
|
||||
id: strawberry.ID
|
||||
name: str
|
||||
identifier: str
|
||||
# workspace: str
|
||||
# is_member: bool
|
||||
# is_favorite: bool
|
||||
|
||||
# @strawberry.field
|
||||
# def workspace(self) -> int:
|
||||
# return self.workspace_id
|
||||
|
||||
@@ -5,9 +5,14 @@ import strawberry
|
||||
from plane.graphql.types.project import ProjectLiteType
|
||||
from plane.graphql.types.issue import IssueLiteType
|
||||
from plane.graphql.types.page import PageLiteType
|
||||
from plane.graphql.types.module import ModuleLiteType
|
||||
from plane.graphql.types.cycle import CycleLiteType
|
||||
|
||||
|
||||
@strawberry.type
|
||||
class ProjectSearchType:
|
||||
class GlobalSearchType:
|
||||
projects: list[ProjectLiteType]
|
||||
issues: list[IssueLiteType]
|
||||
pages: list[PageLiteType]
|
||||
modules: list[ModuleLiteType]
|
||||
cycles: list[CycleLiteType]
|
||||
|
||||
@@ -75,10 +75,10 @@ export default function RootLayout({ children }: { children: React.ReactNode })
|
||||
crossOrigin="use-credentials"
|
||||
/>
|
||||
</head>
|
||||
<body className={`h-screen w-screen`}>
|
||||
<body className={`h-screen w-full`}>
|
||||
<AppProvider>
|
||||
<DesktopAppProviderRoot />
|
||||
<div className={`app-container h-full w-full flex flex-col overflow-hidden`}>
|
||||
<div className={`app-container h-full w-full`}>
|
||||
<div id="context-menu-portal" />
|
||||
<div className="flex-shrink-0">
|
||||
{/* free trial banner */}
|
||||
|
||||
@@ -6,6 +6,7 @@ import useSWR from "swr";
|
||||
import { Spinner } from "@plane/ui";
|
||||
import { cn } from "@/helpers/common.helper";
|
||||
import { ServiceAPITokenService } from "@/plane-web/services/api_token.service";
|
||||
import { useUser } from "@/hooks/store";
|
||||
|
||||
interface CustomIframeProps {
|
||||
srcBase: string;
|
||||
@@ -18,6 +19,9 @@ const SiloIframe: React.FC<CustomIframeProps> = ({ srcBase }) => {
|
||||
const ref = useRef<HTMLIFrameElement>(null);
|
||||
const { resolvedTheme } = useTheme();
|
||||
const [isLoaded, setIsLoaded] = useState<boolean>(false);
|
||||
const {
|
||||
userProfile: { data: userProfile },
|
||||
} = useUser();
|
||||
|
||||
const { data: serviceApiToken } = useSWR(
|
||||
workspaceSlug ? `SERVICE_API_TOKEN_${workspaceSlug}` : null,
|
||||
@@ -58,7 +62,7 @@ const SiloIframe: React.FC<CustomIframeProps> = ({ srcBase }) => {
|
||||
<div className="relative h-full w-full">
|
||||
<iframe
|
||||
ref={ref}
|
||||
src={`${srcBase}&token=${serviceApiToken}`}
|
||||
src={`${srcBase}&token=${serviceApiToken}&user=${userProfile.id}`}
|
||||
className={cn("w-full h-full opacity-0 absolute top-0 left-0", {
|
||||
"opacity-100": isLoaded,
|
||||
})}
|
||||
|
||||
@@ -32,7 +32,7 @@ export const FreeTrialBanner: FC = observer(() => {
|
||||
// validating for offline payment
|
||||
if (subscriptionDetail.is_offline_payment || !subscriptionDetail.trial_end_date) return <></>;
|
||||
// if the days left is more than the max days left then don't show the banner
|
||||
if (daysLeft > MAX_DAYS_LEFT) return <></>;
|
||||
if (daysLeft > MAX_DAYS_LEFT || daysLeft < 0) return <></>;
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
Reference in New Issue
Block a user