Merge pull request #995 from makeplane/uat

release: v1.3.0-rc41
This commit is contained in:
sriram veeraghanta
2024-08-30 18:11:10 +05:30
committed by GitHub
12 changed files with 101 additions and 61 deletions

View File

@@ -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,
)

View File

@@ -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,
)

View File

@@ -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,

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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()

View File

@@ -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

View File

@@ -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]

View File

@@ -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 */}

View File

@@ -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,
})}

View File

@@ -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 (
<>