Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions apiserver/plane/app/views/issue/reaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ def get_queryset(self):
.distinct()
)

@allow_permission(ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST)
@allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST])
def create(self, request, slug, project_id, issue_id):
serializer = IssueReactionSerializer(data=request.data)
if serializer.is_valid():
Expand All @@ -60,7 +60,7 @@ def create(self, request, slug, project_id, issue_id):
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

@allow_permission(ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST)
@allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST])
def destroy(self, request, slug, project_id, issue_id, reaction_code):
issue_reaction = IssueReaction.objects.get(
workspace__slug=slug,
Expand Down
2 changes: 1 addition & 1 deletion web/core/components/cycles/delete-modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export const CycleDeleteModal: React.FC<ICycleDelete> = observer((props) => {
});
})
.catch((errors) => {
const isPermissionError = errors?.error === "Only admin or owner can delete the cycle";
const isPermissionError = errors?.error === "You don't have the required permissions.";
const currentError = isPermissionError
? PROJECT_ERROR_MESSAGES.permissionError
: PROJECT_ERROR_MESSAGES.cycleDeleteError;
Expand Down
5 changes: 4 additions & 1 deletion web/core/components/issues/issue-layouts/list/list-group.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { MutableRefObject, useEffect, useRef, useState } from "react";
import { combine } from "@atlaskit/pragmatic-drag-and-drop/combine";
import { dropTargetForElements } from "@atlaskit/pragmatic-drag-and-drop/element/adapter";
import { observer } from "mobx-react";
import { useParams } from "next/navigation";
import { cn } from "@plane/editor";
// plane packages
import {
Expand Down Expand Up @@ -90,6 +91,7 @@ export const ListGroup = observer((props: Props) => {
const [isExpanded, setIsExpanded] = useState(true);
const groupRef = useRef<HTMLDivElement | null>(null);

const { projectId } = useParams();
const projectState = useProjectState();

const {
Expand Down Expand Up @@ -216,7 +218,8 @@ export const ListGroup = observer((props: Props) => {
);
}, [groupRef?.current, group, orderBy, getGroupIndex, setDragColumnOrientation, setIsDraggingOverColumn]);

const isDragAllowed = !!group_by && DRAG_ALLOWED_GROUPS.includes(group_by);
const isDragAllowed =
!!group_by && DRAG_ALLOWED_GROUPS.includes(group_by) && canEditProperties(projectId?.toString());
const canOverlayBeVisible = orderBy !== "sort_order" || !!group.isDropDisabled;

const isGroupByCreatedBy = group_by === "created_by";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,28 @@ import { ProjectIssueQuickActions } from "@/components/issues";
// components
// types
// constants
import { useUserPermissions } from "@/hooks/store";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
import { BaseListRoot } from "../base-list-root";

export const ListLayout: FC = observer(() => {
const { workspaceSlug, projectId } = useParams();
const { allowPermissions } = useUserPermissions();

if (!workspaceSlug || !projectId) return null;

return <BaseListRoot QuickActions={ProjectIssueQuickActions} />;
const canEditPropertiesBasedOnProject = (projectId: string) =>
allowPermissions(
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
EUserPermissionsLevel.PROJECT,
workspaceSlug.toString(),
projectId
);

return (
<BaseListRoot
QuickActions={ProjectIssueQuickActions}
canEditPropertiesBasedOnProject={canEditPropertiesBasedOnProject}
/>
);
});
13 changes: 12 additions & 1 deletion web/core/components/labels/project-setting-label-group.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,20 @@ type Props = {
droppedLabelId: string | undefined,
dropAtEndOfList: boolean
) => void;
isEditable?: boolean;
};

export const ProjectSettingLabelGroup: React.FC<Props> = observer((props) => {
const { label, labelChildren, handleLabelDelete, isUpdating, setIsUpdating, isLastChild, onDrop } = props;
const {
label,
labelChildren,
handleLabelDelete,
isUpdating,
setIsUpdating,
isLastChild,
onDrop,
isEditable = false,
} = props;

// states
const [isEditLabelForm, setEditLabelForm] = useState(false);
Expand Down Expand Up @@ -123,6 +133,7 @@ export const ProjectSettingLabelGroup: React.FC<Props> = observer((props) => {
isChild
isLastChild={index === labelChildren.length - 1}
onDrop={onDrop}
isEditable={isEditable}
/>
</div>
</div>
Expand Down
2 changes: 2 additions & 0 deletions web/core/components/labels/project-setting-label-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ export const ProjectSettingsLabelList: React.FC = observer(() => {
setIsUpdating={setIsUpdating}
isLastChild={index === projectLabelsTree.length - 1}
onDrop={onDrop}
isEditable={isEditable}
/>
);
}
Expand All @@ -123,6 +124,7 @@ export const ProjectSettingsLabelList: React.FC = observer(() => {
isChild={false}
isLastChild={index === projectLabelsTree.length - 1}
onDrop={onDrop}
isEditable={isEditable}
/>
);
})}
Expand Down
2 changes: 1 addition & 1 deletion web/core/components/modules/delete-module-modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export const DeleteModuleModal: React.FC<Props> = observer((props) => {
});
})
.catch((errors) => {
const isPermissionError = errors?.error === "Only admin or creator can delete the module";
const isPermissionError = errors?.error === "You don't have the required permissions.";
const currentError = isPermissionError
? PROJECT_ERROR_MESSAGES.permissionError
: PROJECT_ERROR_MESSAGES.moduleDeleteError;
Expand Down
2 changes: 1 addition & 1 deletion web/core/components/project/leave-project-modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,10 @@ export const LeaveProjectModal: FC<ILeaveProjectModal> = observer((props) => {
if (data) {
if (data.projectName === project?.name) {
if (data.confirmLeave === "Leave Project") {
router.push(`/${workspaceSlug}/projects`);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Improved user experience, but handle failure scenario gracefully.

The change enhances the user experience by providing immediate feedback through navigation to the projects page, indicating that the leave action is being processed.

However, there is a potential risk of inconsistency if the leaveProject function fails after the user is redirected. The user might be on the projects page while still being a member of the project they intended to leave.

Consider handling the failure scenario gracefully:

  • Display an error message on the projects page if the leaveProject function fails.
  • Provide an option to retry leaving the project.
  • Ensure that the user's membership status is accurately reflected on the projects page.

return leaveProject(workspaceSlug.toString(), project.id)
.then(() => {
handleClose();
router.push(`/${workspaceSlug}/projects`);
captureEvent(PROJECT_MEMBER_LEAVE, {
state: "SUCCESS",
element: "Project settings members page",
Expand Down
2 changes: 1 addition & 1 deletion web/core/components/project/member-list-item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,14 @@ export const ProjectMemberListItem: React.FC<Props> = observer((props) => {
if (!workspaceSlug || !projectId || !memberId) return;

if (memberId === currentUser?.id) {
router.push(`/${workspaceSlug}/projects`);
await leaveProject(workspaceSlug.toString(), projectId.toString())
.then(async () => {
captureEvent(PROJECT_MEMBER_LEAVE, {
state: "SUCCESS",
element: "Project settings members page",
});
await fetchProjects(workspaceSlug.toString());
router.push(`/${workspaceSlug}/projects`);
})
.catch((err) =>
setToast({
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't he be taken to the previous page in case of error?

Expand Down
8 changes: 5 additions & 3 deletions web/core/components/project/settings/member-columns.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,11 @@ export const AccountTypeColumn: React.FC<AccountTypeProps> = observer((props) =>

// derived values
const isCurrentUser = currentUser?.id === rowData.member.id;
const isAdminOrGuest = [EUserPermissions.ADMIN, EUserPermissions.GUEST].includes(rowData.role);
const userWorkspaceRole = getWorkspaceMemberDetails(rowData.member.id)?.role;
const isRoleNonEditable = isCurrentUser || (isAdminOrGuest && userWorkspaceRole !== EUserPermissions.MEMBER);
const isProjectAdminOrGuest = [EUserPermissions.ADMIN, EUserPermissions.GUEST].includes(rowData.role);
const isWorkspaceMember = [EUserPermissions.MEMBER].includes(
Number(getWorkspaceMemberDetails(rowData.member.id)?.role) ?? EUserPermissions.GUEST
);
const isRoleNonEditable = isCurrentUser || (isProjectAdminOrGuest && !isWorkspaceMember);

const checkCurrentOptionWorkspaceRole = (value: string) => {
const currentMemberWorkspaceRole = getWorkspaceMemberDetails(value)?.role as EUserPermissions | undefined;
Expand Down
2 changes: 0 additions & 2 deletions web/core/components/workspace/settings/member-columns.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -114,9 +114,7 @@ export const AccountTypeColumn: React.FC<AccountTypeProps> = observer((props) =>
<CustomSelect
value={value}
onChange={(value: EUserPermissions) => {
console.log({ value, workspaceSlug }, "onChange");
if (!workspaceSlug) return;

updateMember(workspaceSlug.toString(), rowData.member.id, {
role: value as unknown as EUserPermissions, // Cast value to unknown first, then to EUserPermissions
}).catch((err) => {
Expand Down