Merge branch 'preview' of github.com:makeplane/plane into develop
This commit is contained in:
commit
eb344881c2
8 changed files with 70 additions and 40 deletions
|
|
@ -42,7 +42,7 @@ urlpatterns = [
|
||||||
),
|
),
|
||||||
# updated v2 paginated issues
|
# updated v2 paginated issues
|
||||||
path(
|
path(
|
||||||
"workspaces/<str:slug>/v2/issues/",
|
"workspaces/<str:slug>/projects/<uuid:project_id>/v2/issues/",
|
||||||
IssuePaginatedViewSet.as_view({"get": "list"}),
|
IssuePaginatedViewSet.as_view({"get": "list"}),
|
||||||
name="project-issues-paginated",
|
name="project-issues-paginated",
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -741,17 +741,12 @@ class DeletedIssuesListViewSet(BaseAPIView):
|
||||||
class IssuePaginatedViewSet(BaseViewSet):
|
class IssuePaginatedViewSet(BaseViewSet):
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
workspace_slug = self.kwargs.get("slug")
|
workspace_slug = self.kwargs.get("slug")
|
||||||
|
project_id = self.kwargs.get("project_id")
|
||||||
# getting the project_id from the request params
|
|
||||||
project_id = self.request.GET.get("project_id", None)
|
|
||||||
|
|
||||||
issue_queryset = Issue.issue_objects.filter(
|
issue_queryset = Issue.issue_objects.filter(
|
||||||
workspace__slug=workspace_slug
|
workspace__slug=workspace_slug, project_id=project_id
|
||||||
)
|
)
|
||||||
|
|
||||||
if project_id:
|
|
||||||
issue_queryset = issue_queryset.filter(project_id=project_id)
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
issue_queryset.select_related(
|
issue_queryset.select_related(
|
||||||
"workspace", "project", "state", "parent"
|
"workspace", "project", "state", "parent"
|
||||||
|
|
@ -793,8 +788,8 @@ class IssuePaginatedViewSet(BaseViewSet):
|
||||||
|
|
||||||
return paginated_data
|
return paginated_data
|
||||||
|
|
||||||
def list(self, request, slug):
|
@allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST])
|
||||||
project_id = self.request.GET.get("project_id", None)
|
def list(self, request, slug, project_id):
|
||||||
cursor = request.GET.get("cursor", None)
|
cursor = request.GET.get("cursor", None)
|
||||||
is_description_required = request.GET.get("description", False)
|
is_description_required = request.GET.get("description", False)
|
||||||
updated_at = request.GET.get("updated_at__gt", None)
|
updated_at = request.GET.get("updated_at__gt", None)
|
||||||
|
|
@ -833,14 +828,26 @@ class IssuePaginatedViewSet(BaseViewSet):
|
||||||
required_fields.append("description_html")
|
required_fields.append("description_html")
|
||||||
|
|
||||||
# querying issues
|
# querying issues
|
||||||
base_queryset = Issue.issue_objects.filter(workspace__slug=slug)
|
base_queryset = Issue.issue_objects.filter(
|
||||||
|
workspace__slug=slug, project_id=project_id
|
||||||
if project_id:
|
)
|
||||||
base_queryset = base_queryset.filter(project_id=project_id)
|
|
||||||
|
|
||||||
base_queryset = base_queryset.order_by("updated_at")
|
base_queryset = base_queryset.order_by("updated_at")
|
||||||
queryset = self.get_queryset().order_by("updated_at")
|
queryset = self.get_queryset().order_by("updated_at")
|
||||||
|
|
||||||
|
# validation for guest user
|
||||||
|
project = Project.objects.get(pk=project_id, workspace__slug=slug)
|
||||||
|
project_member = ProjectMember.objects.filter(
|
||||||
|
workspace__slug=slug,
|
||||||
|
project_id=project_id,
|
||||||
|
member=request.user,
|
||||||
|
role=5,
|
||||||
|
is_active=True,
|
||||||
|
)
|
||||||
|
if project_member.exists() and not project.guest_view_all_features:
|
||||||
|
base_queryset = base_queryset.filter(created_by=request.user)
|
||||||
|
queryset = queryset.filter(created_by=request.user)
|
||||||
|
|
||||||
# filtering issues by greater then updated_at given by the user
|
# filtering issues by greater then updated_at given by the user
|
||||||
if updated_at:
|
if updated_at:
|
||||||
base_queryset = base_queryset.filter(updated_at__gt=updated_at)
|
base_queryset = base_queryset.filter(updated_at__gt=updated_at)
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import React, { useRef, useState } from "react";
|
import React, { useRef, useState } from "react";
|
||||||
import { usePopper } from "react-popper";
|
import { usePopper } from "react-popper";
|
||||||
import { Combobox } from "@headlessui/react";
|
import { Combobox } from "@headlessui/react";
|
||||||
import { Check, ChevronDown, Search } from "lucide-react";
|
import { Check, ChevronDown, Info, Search } from "lucide-react";
|
||||||
import { createPortal } from "react-dom";
|
import { createPortal } from "react-dom";
|
||||||
// plane helpers
|
// plane helpers
|
||||||
import { useOutsideClickDetector } from "@plane/helpers";
|
import { useOutsideClickDetector } from "@plane/helpers";
|
||||||
|
|
@ -11,6 +11,8 @@ import { useDropdownKeyDown } from "../hooks/use-dropdown-key-down";
|
||||||
import { cn } from "../../helpers";
|
import { cn } from "../../helpers";
|
||||||
// types
|
// types
|
||||||
import { ICustomSearchSelectProps } from "./helper";
|
import { ICustomSearchSelectProps } from "./helper";
|
||||||
|
// local components
|
||||||
|
import { Tooltip } from "../tooltip";
|
||||||
|
|
||||||
export const CustomSearchSelect = (props: ICustomSearchSelectProps) => {
|
export const CustomSearchSelect = (props: ICustomSearchSelectProps) => {
|
||||||
const {
|
const {
|
||||||
|
|
@ -95,11 +97,10 @@ export const CustomSearchSelect = (props: ICustomSearchSelectProps) => {
|
||||||
<button
|
<button
|
||||||
ref={setReferenceElement}
|
ref={setReferenceElement}
|
||||||
type="button"
|
type="button"
|
||||||
className={`flex w-full items-center justify-between gap-1 text-xs ${
|
className={`flex w-full items-center justify-between gap-1 text-xs ${disabled
|
||||||
disabled
|
? "cursor-not-allowed text-custom-text-200"
|
||||||
? "cursor-not-allowed text-custom-text-200"
|
: "cursor-pointer hover:bg-custom-background-80"
|
||||||
: "cursor-pointer hover:bg-custom-background-80"
|
} ${customButtonClassName}`}
|
||||||
} ${customButtonClassName}`}
|
|
||||||
onClick={toggleDropdown}
|
onClick={toggleDropdown}
|
||||||
>
|
>
|
||||||
{customButton}
|
{customButton}
|
||||||
|
|
@ -170,17 +171,32 @@ export const CustomSearchSelect = (props: ICustomSearchSelectProps) => {
|
||||||
"w-full truncate flex items-center justify-between gap-2 rounded px-1 py-1.5 cursor-pointer select-none",
|
"w-full truncate flex items-center justify-between gap-2 rounded px-1 py-1.5 cursor-pointer select-none",
|
||||||
{
|
{
|
||||||
"bg-custom-background-80": active,
|
"bg-custom-background-80": active,
|
||||||
|
"text-custom-text-400 opacity-60 cursor-not-allowed": option.disabled,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (!multiple) closeDropdown();
|
if (!multiple) closeDropdown();
|
||||||
}}
|
}}
|
||||||
|
disabled={option.disabled}
|
||||||
>
|
>
|
||||||
{({ selected }) => (
|
{({ selected }) => (
|
||||||
<>
|
<>
|
||||||
<span className="flex-grow truncate">{option.content}</span>
|
<span className="flex-grow truncate">{option.content}</span>
|
||||||
{selected && <Check className="h-3.5 w-3.5 flex-shrink-0" />}
|
{selected && <Check className="h-3.5 w-3.5 flex-shrink-0" />}
|
||||||
|
{option.tooltip && (
|
||||||
|
<>
|
||||||
|
{
|
||||||
|
typeof option.tooltip === "string" ? (
|
||||||
|
<Tooltip tooltipContent={option.tooltip}>
|
||||||
|
<Info className="h-3.5 w-3.5 flex-shrink-0 cursor-pointer text-custom-text-200" />
|
||||||
|
</Tooltip>
|
||||||
|
) : (
|
||||||
|
option.tooltip
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</Combobox.Option>
|
</Combobox.Option>
|
||||||
|
|
|
||||||
|
|
@ -44,12 +44,14 @@ interface CustomSearchSelectProps {
|
||||||
onChange: any;
|
onChange: any;
|
||||||
onClose?: () => void;
|
onClose?: () => void;
|
||||||
options:
|
options:
|
||||||
| {
|
| {
|
||||||
value: any;
|
value: any;
|
||||||
query: string;
|
query: string;
|
||||||
content: React.ReactNode;
|
content: React.ReactNode;
|
||||||
}[]
|
disabled?: boolean;
|
||||||
| undefined;
|
tooltip?: string | React.ReactNode;
|
||||||
|
}[]
|
||||||
|
| undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SingleValueProps {
|
interface SingleValueProps {
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,23 @@
|
||||||
import { Control } from "react-hook-form";
|
import { Control } from "react-hook-form";
|
||||||
// types
|
// types
|
||||||
import { TIssue } from "@plane/types";
|
import { TBulkIssueProperties, TIssue } from "@plane/types";
|
||||||
|
|
||||||
type TIssueTypeSelectProps = {
|
export type TIssueFields = TIssue & TBulkIssueProperties;
|
||||||
control: Control<TIssue>;
|
|
||||||
|
export type TIssueTypeDropdownVariant = "xs" | "sm";
|
||||||
|
|
||||||
|
export type TIssueTypeSelectProps<T extends Partial<TIssueFields>> = {
|
||||||
|
control: Control<T>;
|
||||||
projectId: string | null;
|
projectId: string | null;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
handleFormChange: () => void;
|
variant?: TIssueTypeDropdownVariant;
|
||||||
|
placeholder?: string;
|
||||||
|
isRequired?: boolean;
|
||||||
|
renderChevron?: boolean;
|
||||||
|
dropDownContainerClassName?: string;
|
||||||
|
showMandatoryFieldInfo?: boolean; // Show info about mandatory fields
|
||||||
|
handleFormChange?: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const IssueTypeSelect: React.FC<TIssueTypeSelectProps> = () => <></>;
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
export const IssueTypeSelect = <T extends Partial<TIssueFields>>(props: TIssueTypeSelectProps<T>) => <></>;
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ export const CyclesList: FC<ICyclesList> = observer((props) => {
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<ActiveCycleRoot />
|
<ActiveCycleRoot workspaceSlug={workspaceSlug} projectId={projectId} />
|
||||||
|
|
||||||
{upcomingCycleIds && (
|
{upcomingCycleIds && (
|
||||||
<Disclosure as="div" className="flex flex-shrink-0 flex-col" defaultOpen>
|
<Disclosure as="div" className="flex flex-shrink-0 flex-col" defaultOpen>
|
||||||
|
|
|
||||||
|
|
@ -280,6 +280,7 @@ export const IssueFormRoot: FC<IssueFormProps> = observer((props) => {
|
||||||
projectId={projectId}
|
projectId={projectId}
|
||||||
disabled={!!data?.sourceIssueId}
|
disabled={!!data?.sourceIssueId}
|
||||||
handleFormChange={handleFormChange}
|
handleFormChange={handleFormChange}
|
||||||
|
renderChevron
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -55,14 +55,7 @@ export class IssueService extends APIService {
|
||||||
queries?: any,
|
queries?: any,
|
||||||
config = {}
|
config = {}
|
||||||
): Promise<TIssuesResponse> {
|
): Promise<TIssuesResponse> {
|
||||||
queries.project_id = projectId;
|
return this.get(`/api/workspaces/${workspaceSlug}/projects/${projectId}/v2/issues/`, { params: queries }, config)
|
||||||
return this.get(
|
|
||||||
`/api/workspaces/${workspaceSlug}/v2/issues/`,
|
|
||||||
{
|
|
||||||
params: queries,
|
|
||||||
},
|
|
||||||
config
|
|
||||||
)
|
|
||||||
.then((response) => response?.data)
|
.then((response) => response?.data)
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
throw error?.response?.data;
|
throw error?.response?.data;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue