chore: deactivate user option added (#2841)
* dev: deactivate user option added * chore: new layout for profile settings * fix: build errors * fix: user profile activity
This commit is contained in:
parent
3c89ef8cc3
commit
db75eced0a
53 changed files with 799 additions and 625 deletions
|
|
@ -1,23 +1,32 @@
|
|||
import React, { useState } from "react";
|
||||
// headless ui
|
||||
import { Dialog, Transition } from "@headlessui/react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
// mobx store
|
||||
import { useMobxStore } from "lib/mobx/store-provider";
|
||||
// icons
|
||||
import { AlertTriangle } from "lucide-react";
|
||||
// ui
|
||||
import { Button } from "@plane/ui";
|
||||
// types
|
||||
import { IUserLite } from "types";
|
||||
|
||||
type Props = {
|
||||
data: IUserLite;
|
||||
onSubmit: () => Promise<void>;
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
handleDelete: () => void;
|
||||
data?: any;
|
||||
};
|
||||
|
||||
export const ConfirmProjectMemberRemove: React.FC<Props> = (props) => {
|
||||
const { isOpen, onClose, data, handleDelete } = props;
|
||||
export const ConfirmProjectMemberRemove: React.FC<Props> = observer((props) => {
|
||||
const { data, onSubmit, isOpen, onClose } = props;
|
||||
|
||||
// states
|
||||
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
|
||||
|
||||
const {
|
||||
user: { currentUser },
|
||||
} = useMobxStore();
|
||||
|
||||
const handleClose = () => {
|
||||
onClose();
|
||||
setIsDeleteLoading(false);
|
||||
|
|
@ -25,10 +34,14 @@ export const ConfirmProjectMemberRemove: React.FC<Props> = (props) => {
|
|||
|
||||
const handleDeletion = async () => {
|
||||
setIsDeleteLoading(true);
|
||||
handleDelete();
|
||||
|
||||
await onSubmit();
|
||||
|
||||
handleClose();
|
||||
};
|
||||
|
||||
const isCurrentUser = currentUser?.id === data?.id;
|
||||
|
||||
return (
|
||||
<Transition.Root show={isOpen} as={React.Fragment}>
|
||||
<Dialog as="div" className="relative z-20" onClose={handleClose}>
|
||||
|
|
@ -63,7 +76,7 @@ export const ConfirmProjectMemberRemove: React.FC<Props> = (props) => {
|
|||
</div>
|
||||
<div className="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
|
||||
<Dialog.Title as="h3" className="text-lg font-medium leading-6 text-custom-text-100">
|
||||
Remove {data?.display_name}?
|
||||
{isCurrentUser ? "Leave project?" : `Remove ${data?.display_name}?`}
|
||||
</Dialog.Title>
|
||||
<div className="mt-2">
|
||||
<p className="text-sm text-custom-text-200">
|
||||
|
|
@ -80,7 +93,13 @@ export const ConfirmProjectMemberRemove: React.FC<Props> = (props) => {
|
|||
Cancel
|
||||
</Button>
|
||||
<Button variant="danger" size="sm" tabIndex={1} onClick={handleDeletion} loading={isDeleteLoading}>
|
||||
{isDeleteLoading ? "Removing..." : "Remove"}
|
||||
{isCurrentUser
|
||||
? isDeleteLoading
|
||||
? "Leaving..."
|
||||
: "Leave"
|
||||
: isDeleteLoading
|
||||
? "Removing..."
|
||||
: "Remove"}
|
||||
</Button>
|
||||
</div>
|
||||
</Dialog.Panel>
|
||||
|
|
@ -90,4 +109,4 @@ export const ConfirmProjectMemberRemove: React.FC<Props> = (props) => {
|
|||
</Dialog>
|
||||
</Transition.Root>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ export const JoinProjectModal: React.FC<TJoinProjectModalProps> = (props) => {
|
|||
const [isJoiningLoading, setIsJoiningLoading] = useState(false);
|
||||
// store
|
||||
const {
|
||||
project: { joinProject },
|
||||
user: { joinProject },
|
||||
} = useMobxStore();
|
||||
// router
|
||||
const router = useRouter();
|
||||
|
|
|
|||
|
|
@ -35,7 +35,9 @@ export const LeaveProjectModal: FC<ILeaveProjectModal> = observer((props) => {
|
|||
const router = useRouter();
|
||||
const { workspaceSlug } = router.query;
|
||||
// store
|
||||
const { project: projectStore } = useMobxStore();
|
||||
const {
|
||||
user: { leaveProject },
|
||||
} = useMobxStore();
|
||||
// toast
|
||||
const { setToastAlert } = useToast();
|
||||
|
||||
|
|
@ -57,8 +59,7 @@ export const LeaveProjectModal: FC<ILeaveProjectModal> = observer((props) => {
|
|||
if (data) {
|
||||
if (data.projectName === project?.name) {
|
||||
if (data.confirmLeave === "Leave Project") {
|
||||
return projectStore
|
||||
.leaveProject(workspaceSlug.toString(), project.id)
|
||||
return leaveProject(workspaceSlug.toString(), project.id)
|
||||
.then(() => {
|
||||
handleClose();
|
||||
router.push(`/${workspaceSlug}/projects`);
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import { useState } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import Link from "next/link";
|
||||
import useSWR, { mutate } from "swr";
|
||||
import { observer } from "mobx-react-lite";
|
||||
// mobx store
|
||||
import { useMobxStore } from "lib/mobx/store-provider";
|
||||
// hooks
|
||||
import useToast from "hooks/use-toast";
|
||||
|
|
@ -15,113 +15,95 @@ import { ChevronDown, Dot, XCircle } from "lucide-react";
|
|||
// constants
|
||||
import { ROLE } from "constants/workspace";
|
||||
// types
|
||||
import { TUserProjectRole } from "types";
|
||||
import { IProjectMember, TUserProjectRole } from "types";
|
||||
|
||||
type Props = {
|
||||
member: any;
|
||||
member: IProjectMember;
|
||||
};
|
||||
|
||||
export const ProjectMemberListItem: React.FC<Props> = observer((props) => {
|
||||
const { member } = props;
|
||||
// states
|
||||
const [removeMemberModal, setRemoveMemberModal] = useState(false);
|
||||
// router
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, projectId } = router.query;
|
||||
// states
|
||||
const [selectedRemoveMember, setSelectedRemoveMember] = useState<any | null>(null);
|
||||
const [selectedInviteRemoveMember, setSelectedInviteRemoveMember] = useState<any | null>(null);
|
||||
|
||||
// store
|
||||
const {
|
||||
user: userStore,
|
||||
projectMember: {
|
||||
projectMembers,
|
||||
fetchProjectMembers,
|
||||
removeMemberFromProject,
|
||||
updateMember,
|
||||
deleteProjectInvitation,
|
||||
},
|
||||
user: { currentUser, currentProjectMemberInfo, currentProjectRole, leaveProject },
|
||||
projectMember: { removeMemberFromProject, updateMember },
|
||||
} = useMobxStore();
|
||||
// hooks
|
||||
const { setToastAlert } = useToast();
|
||||
// fetching project members
|
||||
useSWR(
|
||||
workspaceSlug && projectId ? `PROJECT_MEMBERS_${projectId.toString().toUpperCase()}` : null,
|
||||
workspaceSlug && projectId ? () => fetchProjectMembers(workspaceSlug.toString(), projectId.toString()) : null
|
||||
);
|
||||
|
||||
// derived values
|
||||
const user = userStore.currentUser;
|
||||
const { currentProjectMemberInfo, currentProjectRole } = userStore;
|
||||
const isAdmin = currentProjectRole === 20;
|
||||
const currentUser = projectMembers?.find((item) => item.member.id === user?.id);
|
||||
const memberDetails = member.member;
|
||||
|
||||
const handleRemove = async () => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
|
||||
if (memberDetails.id === currentUser?.id) {
|
||||
await leaveProject(workspaceSlug.toString(), projectId.toString())
|
||||
.then(() => router.push(`/${workspaceSlug}/projects`))
|
||||
.catch((err) =>
|
||||
setToastAlert({
|
||||
type: "error",
|
||||
title: "Error",
|
||||
message: err?.error || "Something went wrong. Please try again.",
|
||||
})
|
||||
);
|
||||
} else
|
||||
await removeMemberFromProject(workspaceSlug.toString(), projectId.toString(), member.id).catch((err) =>
|
||||
setToastAlert({
|
||||
type: "error",
|
||||
title: "Error",
|
||||
message: err?.error || "Something went wrong. Please try again.",
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<ConfirmProjectMemberRemove
|
||||
isOpen={Boolean(selectedRemoveMember) || Boolean(selectedInviteRemoveMember)}
|
||||
onClose={() => {
|
||||
setSelectedRemoveMember(null);
|
||||
setSelectedInviteRemoveMember(null);
|
||||
}}
|
||||
data={selectedRemoveMember ?? selectedInviteRemoveMember}
|
||||
handleDelete={async () => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
|
||||
// if the user is a member
|
||||
if (selectedRemoveMember) {
|
||||
await removeMemberFromProject(workspaceSlug.toString(), projectId.toString(), selectedRemoveMember.id);
|
||||
}
|
||||
// if the user is an invite
|
||||
if (selectedInviteRemoveMember) {
|
||||
await deleteProjectInvitation(
|
||||
workspaceSlug.toString(),
|
||||
projectId.toString(),
|
||||
selectedInviteRemoveMember.id
|
||||
);
|
||||
mutate(`PROJECT_INVITATIONS_${projectId.toString()}`);
|
||||
}
|
||||
|
||||
setToastAlert({
|
||||
type: "success",
|
||||
message: "Member removed successfully",
|
||||
title: "Success",
|
||||
});
|
||||
}}
|
||||
isOpen={removeMemberModal}
|
||||
onClose={() => setRemoveMemberModal(false)}
|
||||
data={member.member}
|
||||
onSubmit={handleRemove}
|
||||
/>
|
||||
<div className="group flex items-center justify-between px-3 py-4 hover:bg-custom-background-90">
|
||||
<div className="flex items-center gap-x-4 gap-y-2">
|
||||
{member.avatar && member.avatar !== "" ? (
|
||||
<Link href={`/${workspaceSlug}/profile/${member.memberId}`}>
|
||||
{memberDetails.avatar && memberDetails.avatar !== "" ? (
|
||||
<Link href={`/${workspaceSlug}/profile/${memberDetails.id}`}>
|
||||
<a className="relative flex h-10 w-10 items-center justify-center rounded p-4 capitalize text-white">
|
||||
<img
|
||||
src={member.avatar}
|
||||
alt={member.display_name || member.email}
|
||||
src={memberDetails.avatar}
|
||||
alt={memberDetails.display_name || memberDetails.email}
|
||||
className="absolute top-0 left-0 h-full w-full object-cover rounded"
|
||||
/>
|
||||
</a>
|
||||
</Link>
|
||||
) : (
|
||||
<Link href={`/${workspaceSlug}/profile/${member.memberId}`}>
|
||||
<Link href={`/${workspaceSlug}/profile/${memberDetails.id}`}>
|
||||
<a className="relative flex h-10 w-10 items-center justify-center rounded p-4 capitalize bg-gray-700 text-white">
|
||||
{(member.display_name ?? member.email ?? "?")[0]}
|
||||
{(memberDetails.display_name ?? memberDetails.email ?? "?")[0]}
|
||||
</a>
|
||||
</Link>
|
||||
)}
|
||||
|
||||
<div>
|
||||
{member.member ? (
|
||||
<Link href={`/${workspaceSlug}/profile/${member.memberId}`}>
|
||||
<a className="text-sm font-medium">
|
||||
{member.first_name} {member.last_name}
|
||||
</a>
|
||||
</Link>
|
||||
) : (
|
||||
<h4 className="text-sm cursor-default">{member.display_name || member.email}</h4>
|
||||
)}
|
||||
<Link href={`/${workspaceSlug}/profile/${memberDetails.id}`}>
|
||||
<a className="text-sm font-medium">
|
||||
{memberDetails.first_name} {memberDetails.last_name}
|
||||
</a>
|
||||
</Link>
|
||||
<div className="flex items-center">
|
||||
<p className="text-xs text-custom-text-300">{member.display_name}</p>
|
||||
<p className="text-xs text-custom-text-300">{memberDetails.display_name}</p>
|
||||
{isAdmin && (
|
||||
<>
|
||||
<Dot height={16} width={16} className="text-custom-text-300" />
|
||||
<p className="text-xs text-custom-text-300">{member.email}</p>
|
||||
<p className="text-xs text-custom-text-300">{memberDetails.email}</p>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
|
@ -129,23 +111,17 @@ export const ProjectMemberListItem: React.FC<Props> = observer((props) => {
|
|||
</div>
|
||||
|
||||
<div className="flex items-center gap-2 text-xs">
|
||||
{!member?.status && (
|
||||
<div className="flex items-center justify-center rounded bg-yellow-500/20 px-2.5 py-1 text-center text-xs text-yellow-500 font-medium">
|
||||
<p>Pending</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<CustomSelect
|
||||
customButton={
|
||||
<div className="flex item-center gap-1 px-2 py-0.5 rounded">
|
||||
<span
|
||||
className={`flex items-center text-xs font-medium rounded ${
|
||||
member.memberId !== currentProjectMemberInfo?.id ? "" : "text-custom-sidebar-text-400"
|
||||
memberDetails.id !== currentProjectMemberInfo?.id ? "" : "text-custom-sidebar-text-400"
|
||||
}`}
|
||||
>
|
||||
{ROLE[member.role as keyof typeof ROLE]}
|
||||
</span>
|
||||
{member.memberId !== currentProjectMemberInfo?.id && (
|
||||
{memberDetails.id !== currentProjectMemberInfo?.id && (
|
||||
<span className="grid place-items-center">
|
||||
<ChevronDown className="h-3 w-3" />
|
||||
</span>
|
||||
|
|
@ -170,9 +146,9 @@ export const ProjectMemberListItem: React.FC<Props> = observer((props) => {
|
|||
});
|
||||
}}
|
||||
disabled={
|
||||
member.memberId === user?.id ||
|
||||
memberDetails.id === currentUser?.id ||
|
||||
!member.member ||
|
||||
(currentUser && currentUser.role !== 20 && currentUser.role < member.role)
|
||||
(currentProjectRole && currentProjectRole !== 20 && currentProjectRole < member.role)
|
||||
}
|
||||
placement="bottom-end"
|
||||
>
|
||||
|
|
@ -188,14 +164,13 @@ export const ProjectMemberListItem: React.FC<Props> = observer((props) => {
|
|||
</CustomSelect>
|
||||
{isAdmin && (
|
||||
<Tooltip
|
||||
tooltipContent={member.memberId === currentProjectMemberInfo?.member ? "Leave project" : "Remove member"}
|
||||
tooltipContent={
|
||||
memberDetails.id === currentProjectMemberInfo?.member.id ? "Leave project" : "Remove member"
|
||||
}
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
if (member.member) setSelectedRemoveMember(member);
|
||||
else setSelectedInviteRemoveMember(member);
|
||||
}}
|
||||
onClick={() => setRemoveMemberModal(true)}
|
||||
className="opacity-0 pointer-events-none group-hover:opacity-100 group-hover:pointer-events-auto"
|
||||
>
|
||||
<XCircle className="h-3.5 w-3.5 text-custom-text-400" strokeWidth={2} />
|
||||
|
|
|
|||
|
|
@ -1,12 +1,9 @@
|
|||
import { useState } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import useSWR, { mutate } from "swr";
|
||||
import { mutate } from "swr";
|
||||
import { observer } from "mobx-react-lite";
|
||||
// mobx store
|
||||
import { useMobxStore } from "lib/mobx/store-provider";
|
||||
// services
|
||||
import { ProjectMemberService } from "services/project";
|
||||
// hooks
|
||||
import useUser from "hooks/use-user";
|
||||
// components
|
||||
import { ProjectMemberListItem, SendProjectInvitationModal } from "components/project";
|
||||
// ui
|
||||
|
|
@ -14,9 +11,6 @@ import { Button, Loader } from "@plane/ui";
|
|||
// icons
|
||||
import { Search } from "lucide-react";
|
||||
|
||||
// services
|
||||
const projectInvitationService = new ProjectMemberService();
|
||||
|
||||
export const ProjectMemberList: React.FC = observer(() => {
|
||||
// router
|
||||
const router = useRouter();
|
||||
|
|
@ -29,49 +23,12 @@ export const ProjectMemberList: React.FC = observer(() => {
|
|||
|
||||
// states
|
||||
const [inviteModal, setInviteModal] = useState(false);
|
||||
const [searchQuery, setSearchQuery] = useState<string>("");
|
||||
const [searchQuery, setSearchQuery] = useState("");
|
||||
|
||||
const { user } = useUser();
|
||||
const searchedMembers = (projectMembers ?? []).filter((member) => {
|
||||
const fullName = `${member.member.first_name} ${member.member.last_name}`.toLowerCase();
|
||||
const displayName = member.member.display_name.toLowerCase();
|
||||
|
||||
const { data: projectInvitations } = useSWR(
|
||||
workspaceSlug && projectId ? `PROJECT_INVITATIONS_${projectId.toString()}` : null,
|
||||
workspaceSlug && projectId
|
||||
? () => projectInvitationService.fetchProjectInvitations(workspaceSlug.toString(), projectId.toString())
|
||||
: null
|
||||
);
|
||||
|
||||
// derived values
|
||||
|
||||
const members = [
|
||||
...(projectMembers?.map((item) => ({
|
||||
id: item.id,
|
||||
memberId: item.member?.id,
|
||||
avatar: item.member?.avatar,
|
||||
first_name: item.member?.first_name,
|
||||
last_name: item.member?.last_name,
|
||||
email: item.member?.email,
|
||||
display_name: item.member?.display_name,
|
||||
role: item.role,
|
||||
status: true,
|
||||
member: true,
|
||||
})) || []),
|
||||
...(projectInvitations?.map((item: any) => ({
|
||||
id: item.id,
|
||||
memberId: item.id,
|
||||
avatar: item.avatar ?? "",
|
||||
first_name: item.first_name ?? item.email,
|
||||
last_name: item.last_name ?? "",
|
||||
email: item.email,
|
||||
display_name: item.email,
|
||||
role: item.role,
|
||||
status: item.accepted,
|
||||
member: false,
|
||||
})) || []),
|
||||
];
|
||||
|
||||
const searchedMembers = members?.filter((member) => {
|
||||
const fullName = `${member.first_name} ${member.last_name}`.toLowerCase();
|
||||
const displayName = member.display_name.toLowerCase();
|
||||
return displayName.includes(searchQuery.toLowerCase()) || fullName.includes(searchQuery.toLowerCase());
|
||||
});
|
||||
|
||||
|
|
@ -79,9 +36,8 @@ export const ProjectMemberList: React.FC = observer(() => {
|
|||
<>
|
||||
<SendProjectInvitationModal
|
||||
isOpen={inviteModal}
|
||||
setIsOpen={setInviteModal}
|
||||
members={members}
|
||||
user={user}
|
||||
members={projectMembers ?? []}
|
||||
onClose={() => setInviteModal(false)}
|
||||
onSuccess={() => {
|
||||
mutate(`PROJECT_INVITATIONS_${projectId?.toString()}`);
|
||||
fetchProjectMembers(workspaceSlug?.toString()!, projectId?.toString()!);
|
||||
|
|
@ -104,7 +60,7 @@ export const ProjectMemberList: React.FC = observer(() => {
|
|||
Add Member
|
||||
</Button>
|
||||
</div>
|
||||
{!projectMembers || !projectInvitations ? (
|
||||
{!projectMembers ? (
|
||||
<Loader className="space-y-5">
|
||||
<Loader.Item height="40px" />
|
||||
<Loader.Item height="40px" />
|
||||
|
|
@ -113,7 +69,7 @@ export const ProjectMemberList: React.FC = observer(() => {
|
|||
</Loader>
|
||||
) : (
|
||||
<div className="divide-y divide-custom-border-100">
|
||||
{members.length > 0
|
||||
{projectMembers.length > 0
|
||||
? searchedMembers.map((member) => <ProjectMemberListItem key={member.id} member={member} />)
|
||||
: null}
|
||||
{searchedMembers.length === 0 && (
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import React, { useEffect } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import useSWR from "swr";
|
||||
import { useForm, Controller, useFieldArray } from "react-hook-form";
|
||||
import { Dialog, Transition } from "@headlessui/react";
|
||||
import { ChevronDown, Plus, X } from "lucide-react";
|
||||
|
|
@ -11,22 +10,19 @@ import { useMobxStore } from "lib/mobx/store-provider";
|
|||
import { Avatar, Button, CustomSelect, CustomSearchSelect } from "@plane/ui";
|
||||
// services
|
||||
import { ProjectMemberService } from "services/project";
|
||||
import { WorkspaceService } from "services/workspace.service";
|
||||
// hooks
|
||||
import useToast from "hooks/use-toast";
|
||||
// helpers
|
||||
import { trackEvent } from "helpers/event-tracker.helper";
|
||||
// types
|
||||
import { IUser, TUserProjectRole } from "types";
|
||||
// fetch-keys
|
||||
import { WORKSPACE_MEMBERS } from "constants/fetch-keys";
|
||||
import { IProjectMember, TUserProjectRole } from "types";
|
||||
// constants
|
||||
import { ROLE } from "constants/workspace";
|
||||
import { trackEvent } from "helpers/event-tracker.helper";
|
||||
|
||||
type Props = {
|
||||
isOpen: boolean;
|
||||
setIsOpen: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
members: any[];
|
||||
user: IUser | undefined;
|
||||
members: IProjectMember[];
|
||||
onClose: () => void;
|
||||
onSuccess: () => void;
|
||||
};
|
||||
|
||||
|
|
@ -50,23 +46,19 @@ const defaultValues: FormValues = {
|
|||
|
||||
// services
|
||||
const projectMemberService = new ProjectMemberService();
|
||||
const workspaceService = new WorkspaceService();
|
||||
|
||||
export const SendProjectInvitationModal: React.FC<Props> = observer((props) => {
|
||||
const { isOpen, setIsOpen, members, onSuccess } = props;
|
||||
const { isOpen, members, onClose, onSuccess } = props;
|
||||
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, projectId } = router.query;
|
||||
|
||||
const { setToastAlert } = useToast();
|
||||
|
||||
const { user: userStore } = useMobxStore();
|
||||
const userRole = userStore.currentProjectRole;
|
||||
|
||||
const { data: people } = useSWR(
|
||||
workspaceSlug ? WORKSPACE_MEMBERS(workspaceSlug as string) : null,
|
||||
workspaceSlug ? () => workspaceService.fetchWorkspaceMembers(workspaceSlug as string) : null
|
||||
);
|
||||
const {
|
||||
user: { currentProjectRole },
|
||||
workspaceMember: { workspaceMembers },
|
||||
} = useMobxStore();
|
||||
|
||||
const {
|
||||
formState: { errors, isSubmitting },
|
||||
|
|
@ -80,8 +72,8 @@ export const SendProjectInvitationModal: React.FC<Props> = observer((props) => {
|
|||
name: "members",
|
||||
});
|
||||
|
||||
const uninvitedPeople = people?.filter((person) => {
|
||||
const isInvited = members?.find((member) => member.memberId === person.member.id);
|
||||
const uninvitedPeople = workspaceMembers?.filter((person) => {
|
||||
const isInvited = members?.find((member) => member.member.id === person.member.id);
|
||||
|
||||
return !isInvited;
|
||||
});
|
||||
|
|
@ -93,17 +85,15 @@ export const SendProjectInvitationModal: React.FC<Props> = observer((props) => {
|
|||
|
||||
await projectMemberService
|
||||
.bulkAddMembersToProject(workspaceSlug.toString(), projectId.toString(), payload)
|
||||
.then((res) => {
|
||||
setIsOpen(false);
|
||||
trackEvent(
|
||||
'PROJECT_MEMBER_INVITE',
|
||||
)
|
||||
.then(() => {
|
||||
onSuccess();
|
||||
onClose();
|
||||
trackEvent("PROJECT_MEMBER_INVITE");
|
||||
setToastAlert({
|
||||
title: "Success",
|
||||
type: "success",
|
||||
message: "Member added successfully",
|
||||
});
|
||||
onSuccess();
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error);
|
||||
|
|
@ -114,7 +104,8 @@ export const SendProjectInvitationModal: React.FC<Props> = observer((props) => {
|
|||
};
|
||||
|
||||
const handleClose = () => {
|
||||
setIsOpen(false);
|
||||
onClose();
|
||||
|
||||
const timeout = setTimeout(() => {
|
||||
reset(defaultValues);
|
||||
clearTimeout(timeout);
|
||||
|
|
@ -195,7 +186,7 @@ export const SendProjectInvitationModal: React.FC<Props> = observer((props) => {
|
|||
name={`members.${index}.member_id`}
|
||||
rules={{ required: "Please select a member" }}
|
||||
render={({ field: { value, onChange } }) => {
|
||||
const selectedMember = people?.find((p) => p.member.id === value)?.member;
|
||||
const selectedMember = workspaceMembers?.find((p) => p.member.id === value)?.member;
|
||||
|
||||
return (
|
||||
<CustomSearchSelect
|
||||
|
|
@ -250,7 +241,7 @@ export const SendProjectInvitationModal: React.FC<Props> = observer((props) => {
|
|||
width="w-full"
|
||||
>
|
||||
{Object.entries(ROLE).map(([key, label]) => {
|
||||
if (parseInt(key) > (userRole ?? 5)) return null;
|
||||
if (parseInt(key) > (currentProjectRole ?? 5)) return null;
|
||||
|
||||
return (
|
||||
<CustomSelect.Option key={key} value={key}>
|
||||
|
|
|
|||
|
|
@ -26,7 +26,11 @@ export const ProjectSidebarList: FC = observer(() => {
|
|||
// refs
|
||||
const containerRef = useRef<HTMLDivElement | null>(null);
|
||||
|
||||
const { theme: themeStore, project: projectStore, commandPalette: commandPaletteStore } = useMobxStore();
|
||||
const {
|
||||
theme: { sidebarCollapsed },
|
||||
project: { joinedProjects, favoriteProjects, orderProjectsWithSortOrder, updateProjectView },
|
||||
commandPalette: { toggleCreateProjectModal },
|
||||
} = useMobxStore();
|
||||
// router
|
||||
const router = useRouter();
|
||||
const { workspaceSlug } = router.query;
|
||||
|
|
@ -34,9 +38,6 @@ export const ProjectSidebarList: FC = observer(() => {
|
|||
// toast
|
||||
const { setToastAlert } = useToast();
|
||||
|
||||
const joinedProjects = workspaceSlug && projectStore.joinedProjects;
|
||||
const favoriteProjects = workspaceSlug && projectStore.favoriteProjects;
|
||||
|
||||
const orderedJoinedProjects: IProject[] | undefined = joinedProjects
|
||||
? orderArrayBy(joinedProjects, "sort_order", "ascending")
|
||||
: undefined;
|
||||
|
|
@ -62,20 +63,18 @@ export const ProjectSidebarList: FC = observer(() => {
|
|||
|
||||
if (source.index === destination.index) return;
|
||||
|
||||
const updatedSortOrder = projectStore.orderProjectsWithSortOrder(source.index, destination.index, draggableId);
|
||||
const updatedSortOrder = orderProjectsWithSortOrder(source.index, destination.index, draggableId);
|
||||
|
||||
projectStore
|
||||
.updateProjectView(workspaceSlug.toString(), draggableId, { sort_order: updatedSortOrder })
|
||||
.catch(() => {
|
||||
setToastAlert({
|
||||
type: "error",
|
||||
title: "Error!",
|
||||
message: "Something went wrong. Please try again.",
|
||||
});
|
||||
updateProjectView(workspaceSlug.toString(), draggableId, { sort_order: updatedSortOrder }).catch(() => {
|
||||
setToastAlert({
|
||||
type: "error",
|
||||
title: "Error!",
|
||||
message: "Something went wrong. Please try again.",
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const isCollapsed = themeStore.sidebarCollapsed || false;
|
||||
const isCollapsed = sidebarCollapsed || false;
|
||||
|
||||
/**
|
||||
* Implementing scroll animation styles based on the scroll length of the container
|
||||
|
|
@ -263,7 +262,7 @@ export const ProjectSidebarList: FC = observer(() => {
|
|||
<button
|
||||
type="button"
|
||||
className="flex w-full items-center gap-2 px-3 text-sm text-custom-sidebar-text-200"
|
||||
onClick={() => commandPaletteStore.toggleCreateProjectModal(true)}
|
||||
onClick={() => toggleCreateProjectModal(true)}
|
||||
>
|
||||
<Plus className="h-5 w-5" />
|
||||
{!isCollapsed && "Add Project"}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue