mirror of
https://github.com/makeplane/plane
synced 2025-08-07 19:59:33 +00:00
Compare commits
4 Commits
refactor/s
...
fix-restri
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f13027d6db | ||
|
|
f011364eb6 | ||
|
|
8dfd5d9c7d | ||
|
|
f2bb944661 |
@@ -277,28 +277,14 @@ class SearchEndpoint(BaseAPIView):
|
||||
for field in fields:
|
||||
q |= Q(**{f"{field}__icontains": query})
|
||||
|
||||
base_filters = Q(
|
||||
q,
|
||||
is_active=True,
|
||||
workspace__slug=slug,
|
||||
member__is_bot=False,
|
||||
project_id=project_id,
|
||||
role__gt=10,
|
||||
)
|
||||
if issue_id:
|
||||
issue_created_by = (
|
||||
Issue.objects.filter(id=issue_id)
|
||||
.values_list("created_by_id", flat=True)
|
||||
.first()
|
||||
)
|
||||
# Add condition to include `issue_created_by` in the query
|
||||
filters = Q(member_id=issue_created_by) | base_filters
|
||||
else:
|
||||
filters = base_filters
|
||||
|
||||
# Query to fetch users
|
||||
users = (
|
||||
ProjectMember.objects.filter(filters)
|
||||
ProjectMember.objects.filter(
|
||||
q,
|
||||
is_active=True,
|
||||
workspace__slug=slug,
|
||||
member__is_bot=False,
|
||||
project_id=project_id,
|
||||
)
|
||||
.annotate(
|
||||
member__avatar_url=Case(
|
||||
When(
|
||||
@@ -318,14 +304,35 @@ class SearchEndpoint(BaseAPIView):
|
||||
)
|
||||
)
|
||||
.order_by("-created_at")
|
||||
.values(
|
||||
"member__avatar_url",
|
||||
"member__display_name",
|
||||
"member__id",
|
||||
)[:count]
|
||||
)
|
||||
|
||||
response_data["user_mention"] = list(users)
|
||||
if issue_id:
|
||||
issue_created_by = (
|
||||
Issue.objects.filter(id=issue_id)
|
||||
.values_list("created_by_id", flat=True)
|
||||
.first()
|
||||
)
|
||||
users = (
|
||||
users.filter(Q(role__gt=10) | Q(member_id=issue_created_by))
|
||||
.distinct()
|
||||
.values(
|
||||
"member__avatar_url",
|
||||
"member__display_name",
|
||||
"member__id",
|
||||
)
|
||||
)
|
||||
else:
|
||||
users = (
|
||||
users.filter(Q(role__gt=10))
|
||||
.distinct()
|
||||
.values(
|
||||
"member__avatar_url",
|
||||
"member__display_name",
|
||||
"member__id",
|
||||
)
|
||||
)
|
||||
|
||||
response_data["user_mention"] = list(users[:count])
|
||||
|
||||
elif query_type == "project":
|
||||
fields = ["name", "identifier"]
|
||||
|
||||
1
packages/types/src/search.d.ts
vendored
1
packages/types/src/search.d.ts
vendored
@@ -79,5 +79,6 @@ export type TSearchEntityRequestPayload = {
|
||||
project_id?: string;
|
||||
query_type: TSearchEntities[];
|
||||
query: string;
|
||||
issue_id?: string;
|
||||
team_id?: string;
|
||||
};
|
||||
|
||||
@@ -16,6 +16,7 @@ import { getFileURL } from "@/helpers/file.helper";
|
||||
// hooks
|
||||
import { useUser, useMember } from "@/hooks/store";
|
||||
import { usePlatformOS } from "@/hooks/use-platform-os";
|
||||
import { EUserPermissions } from "@/plane-web/constants/user-permissions";
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
@@ -36,8 +37,7 @@ export const MemberOptions: React.FC<Props> = observer((props: Props) => {
|
||||
// store hooks
|
||||
const { workspaceSlug } = useParams();
|
||||
const {
|
||||
getUserDetails,
|
||||
project: { getProjectMemberIds, fetchProjectMembers },
|
||||
project: { getProjectMemberIds, fetchProjectMembers, getProjectMemberDetails },
|
||||
workspace: { workspaceMemberIds },
|
||||
} = useMember();
|
||||
const { data: currentUser } = useUser();
|
||||
@@ -59,7 +59,7 @@ export const MemberOptions: React.FC<Props> = observer((props: Props) => {
|
||||
if (isOpen) {
|
||||
onOpen();
|
||||
if (!isMobile) {
|
||||
inputRef.current && inputRef.current.focus();
|
||||
if (inputRef.current) inputRef.current.focus();
|
||||
}
|
||||
}
|
||||
}, [isOpen, isMobile]);
|
||||
@@ -76,23 +76,30 @@ export const MemberOptions: React.FC<Props> = observer((props: Props) => {
|
||||
}
|
||||
};
|
||||
|
||||
const options = memberIds?.map((userId) => {
|
||||
const userDetails = getUserDetails(userId);
|
||||
|
||||
return {
|
||||
value: userId,
|
||||
query: `${userDetails?.display_name} ${userDetails?.first_name} ${userDetails?.last_name}`,
|
||||
content: (
|
||||
<div className="flex items-center gap-2">
|
||||
<Avatar name={userDetails?.display_name} src={getFileURL(userDetails?.avatar_url ?? "")} />
|
||||
<span className="flex-grow truncate">{currentUser?.id === userId ? "You" : userDetails?.display_name}</span>
|
||||
</div>
|
||||
),
|
||||
};
|
||||
});
|
||||
const options = memberIds
|
||||
?.map((userId) => {
|
||||
const userData = getProjectMemberDetails(userId);
|
||||
const memeberDetails = userData?.member;
|
||||
/**Restricting guest users from being shown in the dropdown */
|
||||
const isGuest = (userData?.role ?? EUserPermissions.GUEST) === EUserPermissions.GUEST;
|
||||
if (isGuest) return;
|
||||
return {
|
||||
value: userId,
|
||||
query: `${memeberDetails?.display_name} ${memeberDetails?.first_name} ${memeberDetails?.last_name}`,
|
||||
content: (
|
||||
<div className="flex items-center gap-2">
|
||||
<Avatar name={memeberDetails?.display_name} src={getFileURL(memeberDetails?.avatar_url ?? "")} />
|
||||
<span className="flex-grow truncate">
|
||||
{currentUser?.id === userId ? "You" : memeberDetails?.display_name}
|
||||
</span>
|
||||
</div>
|
||||
),
|
||||
};
|
||||
})
|
||||
.filter((option) => option !== undefined);
|
||||
|
||||
const filteredOptions =
|
||||
query === "" ? options : options?.filter((o) => o.query.toLowerCase().includes(query.toLowerCase()));
|
||||
query === "" ? options : options?.filter((option) => option?.query.toLowerCase().includes(query.toLowerCase()));
|
||||
|
||||
return createPortal(
|
||||
<Combobox.Options data-prevent-outside-click static>
|
||||
@@ -125,8 +132,8 @@ export const MemberOptions: React.FC<Props> = observer((props: Props) => {
|
||||
filteredOptions.length > 0 ? (
|
||||
filteredOptions.map((option) => (
|
||||
<Combobox.Option
|
||||
key={option.value}
|
||||
value={option.value}
|
||||
key={option?.value}
|
||||
value={option?.value}
|
||||
className={({ active, selected }) =>
|
||||
`flex w-full cursor-pointer select-none items-center justify-between gap-2 truncate rounded px-1 py-1.5 ${
|
||||
active ? "bg-custom-background-80" : ""
|
||||
@@ -135,7 +142,7 @@ export const MemberOptions: React.FC<Props> = observer((props: Props) => {
|
||||
>
|
||||
{({ 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" />}
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -30,6 +30,7 @@ interface LiteTextEditorWrapperProps
|
||||
isSubmitting?: boolean;
|
||||
showToolbarInitially?: boolean;
|
||||
uploadFile: (file: File) => Promise<string>;
|
||||
issueId?: string;
|
||||
}
|
||||
|
||||
export const LiteTextEditor = React.forwardRef<EditorRefApi, LiteTextEditorWrapperProps>((props, ref) => {
|
||||
@@ -46,6 +47,7 @@ export const LiteTextEditor = React.forwardRef<EditorRefApi, LiteTextEditorWrapp
|
||||
showToolbarInitially = true,
|
||||
placeholder = "Add comment...",
|
||||
uploadFile,
|
||||
issueId,
|
||||
...rest
|
||||
} = props;
|
||||
// states
|
||||
@@ -58,6 +60,7 @@ export const LiteTextEditor = React.forwardRef<EditorRefApi, LiteTextEditorWrapp
|
||||
await workspaceService.searchEntity(workspaceSlug?.toString() ?? "", {
|
||||
...payload,
|
||||
project_id: projectId?.toString() ?? "",
|
||||
issue_id: issueId,
|
||||
}),
|
||||
});
|
||||
// file size
|
||||
|
||||
@@ -125,6 +125,7 @@ export const IssueDescriptionInput: FC<IssueDescriptionInputProps> = observer((p
|
||||
await workspaceService.searchEntity(workspaceSlug?.toString() ?? "", {
|
||||
...payload,
|
||||
project_id: projectId?.toString() ?? "",
|
||||
issue_id: issueId,
|
||||
})
|
||||
}
|
||||
containerClassName={containerClassName}
|
||||
|
||||
@@ -46,6 +46,7 @@ export const IssueActivityCommentRoot: FC<TIssueActivityCommentRoot> = observer(
|
||||
projectId={projectId}
|
||||
key={activityComment.id}
|
||||
workspaceSlug={workspaceSlug}
|
||||
issueId={issueId}
|
||||
commentId={activityComment.id}
|
||||
activityOperations={activityOperations}
|
||||
ends={index === 0 ? "top" : index === filteredActivityComments.length - 1 ? "bottom" : undefined}
|
||||
|
||||
@@ -28,12 +28,14 @@ type TIssueCommentCard = {
|
||||
ends: "top" | "bottom" | undefined;
|
||||
showAccessSpecifier?: boolean;
|
||||
disabled?: boolean;
|
||||
issueId?: string;
|
||||
};
|
||||
|
||||
export const IssueCommentCard: FC<TIssueCommentCard> = observer((props) => {
|
||||
const {
|
||||
workspaceSlug,
|
||||
projectId,
|
||||
issueId,
|
||||
commentId,
|
||||
activityOperations,
|
||||
ends,
|
||||
@@ -144,6 +146,7 @@ export const IssueCommentCard: FC<TIssueCommentCard> = observer((props) => {
|
||||
<LiteTextEditor
|
||||
workspaceId={workspaceId}
|
||||
projectId={projectId}
|
||||
issueId={issueId}
|
||||
workspaceSlug={workspaceSlug}
|
||||
ref={editorRef}
|
||||
id={comment.id}
|
||||
|
||||
@@ -96,6 +96,7 @@ export const IssueCommentCreate: FC<TIssueCommentCreate> = (props) => {
|
||||
value={"<p></p>"}
|
||||
projectId={projectId}
|
||||
workspaceSlug={workspaceSlug}
|
||||
issueId={issueId}
|
||||
onEnterKeyPress={(e) => {
|
||||
if (!isEmpty && !isSubmitting) {
|
||||
handleSubmit(onSubmit)(e);
|
||||
|
||||
Reference in New Issue
Block a user