[WEB-2357] fix: update and redefine user roles across the platform (#5466)
* chore: removed viewer role * chore: indentation * chore: remove viewer role * chore: handled user permissions in store * chore: updated the migration file * chore: updated user permissions store * chore: removed the owner key * chore: code refactor * chore: code refactor * chore: code refactor * chore: code refactor * chore: code refactor * fix: build error * chore: updated user permissions store and handled the permissions fetch in workspace and project wrappers * chore: package user enum updated * chore: user permission updated * chore: user permission updated * chore: resolved build errors * chore: resolved build error * chore: resolved build errors * chore: computedFn deep map issue resolved * chore: added back migration * chore: added new field in project table * chore: removed member store in users * chore: private project for admins * chore: workspace notification access validation updated * fix: workspace member edit option * fix: project intake permission validation updated * chore: workspace export settings permission updated * chore: guest_view_all_issues added * chore: guest_view_all_issues added * chore: key changed for guest access * chore: added validation for individual issues * chore: changed the dashboard issues count * chore: added new yarn file * chore: modified yarn file * chore: project page permission updated * chore: project page permission updated * chore: member setting ux updated * chore: build error * fix: yarn lock * fix: build error --------- Co-authored-by: gurusainath <gurusainath007@gmail.com> Co-authored-by: Anmol Singh Bhatia <anmolsinghbhatia@plane.so>
This commit is contained in:
parent
7013a36629
commit
fdcd9a376c
172 changed files with 2057 additions and 1627 deletions
|
|
@ -5,7 +5,7 @@ import { useParams } from "next/navigation";
|
|||
// hooks
|
||||
import { ClipboardList } from "lucide-react";
|
||||
import { Button } from "@plane/ui";
|
||||
import { useProject, useUser } from "@/hooks/store";
|
||||
import { useProject, useUserPermissions } from "@/hooks/store";
|
||||
// ui
|
||||
// icons
|
||||
// images
|
||||
|
|
@ -15,9 +15,7 @@ export const JoinProject: React.FC = () => {
|
|||
// states
|
||||
const [isJoiningProject, setIsJoiningProject] = useState(false);
|
||||
// store hooks
|
||||
const {
|
||||
membership: { joinProject },
|
||||
} = useUser();
|
||||
const { joinProject } = useUserPermissions();
|
||||
const { fetchProjects } = useProject();
|
||||
|
||||
const { workspaceSlug, projectId } = useParams();
|
||||
|
|
@ -27,7 +25,7 @@ export const JoinProject: React.FC = () => {
|
|||
|
||||
setIsJoiningProject(true);
|
||||
|
||||
joinProject(workspaceSlug.toString(), [projectId.toString()])
|
||||
joinProject(workspaceSlug.toString(), projectId.toString())
|
||||
.then(() => fetchProjects(workspaceSlug.toString()))
|
||||
.finally(() => setIsJoiningProject(false));
|
||||
};
|
||||
|
|
|
|||
|
|
@ -10,9 +10,10 @@ import { CustomSelect, Loader, ToggleSwitch } from "@plane/ui";
|
|||
// component
|
||||
import { SelectMonthModal } from "@/components/automation";
|
||||
// constants
|
||||
import { EUserProjectRoles, PROJECT_AUTOMATION_MONTHS } from "@/constants/project";
|
||||
import { PROJECT_AUTOMATION_MONTHS } from "@/constants/project";
|
||||
// hooks
|
||||
import { useProject, useUser } from "@/hooks/store";
|
||||
import { useProject, useUserPermissions } from "@/hooks/store";
|
||||
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||
|
||||
type Props = {
|
||||
handleChange: (formData: Partial<IProject>) => Promise<void>;
|
||||
|
|
@ -25,12 +26,16 @@ export const AutoArchiveAutomation: React.FC<Props> = observer((props) => {
|
|||
// states
|
||||
const [monthModal, setmonthModal] = useState(false);
|
||||
// store hooks
|
||||
const {
|
||||
membership: { currentProjectRole },
|
||||
} = useUser();
|
||||
const { allowPermissions } = useUserPermissions();
|
||||
|
||||
const { currentProjectDetails } = useProject();
|
||||
|
||||
const isAdmin = currentProjectRole === EUserProjectRoles.ADMIN;
|
||||
const isAdmin = allowPermissions(
|
||||
[EUserPermissions.ADMIN],
|
||||
EUserPermissionsLevel.PROJECT,
|
||||
currentProjectDetails?.workspace_detail.slug,
|
||||
currentProjectDetails?.id
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
|
|||
|
|
@ -11,9 +11,10 @@ import { CustomSelect, CustomSearchSelect, ToggleSwitch, StateGroupIcon, DoubleC
|
|||
// component
|
||||
import { SelectMonthModal } from "@/components/automation";
|
||||
// constants
|
||||
import { EUserProjectRoles, PROJECT_AUTOMATION_MONTHS } from "@/constants/project";
|
||||
import { PROJECT_AUTOMATION_MONTHS } from "@/constants/project";
|
||||
// hooks
|
||||
import { useProject, useProjectState, useUser } from "@/hooks/store";
|
||||
import { useProject, useProjectState, useUserPermissions } from "@/hooks/store";
|
||||
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||
|
||||
type Props = {
|
||||
handleChange: (formData: Partial<IProject>) => Promise<void>;
|
||||
|
|
@ -24,11 +25,9 @@ export const AutoCloseAutomation: React.FC<Props> = observer((props) => {
|
|||
// states
|
||||
const [monthModal, setmonthModal] = useState(false);
|
||||
// store hooks
|
||||
const {
|
||||
membership: { currentProjectRole },
|
||||
} = useUser();
|
||||
const { currentProjectDetails } = useProject();
|
||||
const { projectStates } = useProjectState();
|
||||
const { allowPermissions } = useUserPermissions();
|
||||
|
||||
// const stateGroups = projectStateStore.groupedProjectStates ?? undefined;
|
||||
|
||||
|
|
@ -57,7 +56,12 @@ export const AutoCloseAutomation: React.FC<Props> = observer((props) => {
|
|||
default_state: defaultState,
|
||||
};
|
||||
|
||||
const isAdmin = currentProjectRole === EUserProjectRoles.ADMIN;
|
||||
const isAdmin = allowPermissions(
|
||||
[EUserPermissions.ADMIN],
|
||||
EUserPermissionsLevel.PROJECT,
|
||||
currentProjectDetails?.workspace_detail?.slug,
|
||||
currentProjectDetails?.id
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
|
|||
|
|
@ -4,12 +4,12 @@ import { Command } from "cmdk";
|
|||
// hooks
|
||||
import Link from "next/link";
|
||||
import { useParams } from "next/navigation";
|
||||
// constants
|
||||
import { EUserWorkspaceRoles } from "@/constants/workspace";
|
||||
|
||||
// hooks
|
||||
import { useUser } from "@/hooks/store";
|
||||
import { useUserPermissions } from "@/hooks/store";
|
||||
import { useAppRouter } from "@/hooks/use-app-router";
|
||||
// plane wev constants
|
||||
import { EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||
import { WORKSPACE_SETTINGS_LINKS } from "@/plane-web/constants/workspace";
|
||||
// plane web helpers
|
||||
import { shouldRenderSettingLink } from "@/plane-web/helpers/workspace.helper";
|
||||
|
|
@ -25,11 +25,8 @@ export const CommandPaletteWorkspaceSettingsActions: React.FC<Props> = (props) =
|
|||
// router params
|
||||
const { workspaceSlug } = useParams();
|
||||
// mobx store
|
||||
const {
|
||||
membership: { currentWorkspaceRole },
|
||||
} = useUser();
|
||||
const { allowPermissions } = useUserPermissions();
|
||||
// derived values
|
||||
const workspaceMemberInfo = currentWorkspaceRole || EUserWorkspaceRoles.GUEST;
|
||||
|
||||
const redirect = (path: string) => {
|
||||
closePalette();
|
||||
|
|
@ -40,7 +37,7 @@ export const CommandPaletteWorkspaceSettingsActions: React.FC<Props> = (props) =
|
|||
<>
|
||||
{WORKSPACE_SETTINGS_LINKS.map(
|
||||
(setting) =>
|
||||
workspaceMemberInfo >= setting.access &&
|
||||
allowPermissions(setting.access, EUserPermissionsLevel.WORKSPACE, workspaceSlug.toString()) &&
|
||||
shouldRenderSettingLink(setting.key) && (
|
||||
<Command.Item
|
||||
key={setting.key}
|
||||
|
|
|
|||
|
|
@ -20,10 +20,11 @@ import { ISSUE_DETAILS } from "@/constants/fetch-keys";
|
|||
// helpers
|
||||
import { copyTextToClipboard } from "@/helpers/string.helper";
|
||||
// hooks
|
||||
import { useEventTracker, useUser, useAppTheme, useCommandPalette } from "@/hooks/store";
|
||||
import { useEventTracker, useUser, useAppTheme, useCommandPalette, useUserPermissions } from "@/hooks/store";
|
||||
import { useAppRouter } from "@/hooks/use-app-router";
|
||||
import { useIssuesStore } from "@/hooks/use-issue-layout-store";
|
||||
import { usePlatformOS } from "@/hooks/use-platform-os";
|
||||
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||
// services
|
||||
import { IssueService } from "@/services/issue";
|
||||
|
||||
|
|
@ -43,10 +44,10 @@ export const CommandPalette: FC = observer(() => {
|
|||
const { platform } = usePlatformOS();
|
||||
const {
|
||||
data: currentUser,
|
||||
canPerformProjectMemberActions,
|
||||
canPerformWorkspaceMemberActions,
|
||||
// canPerformProjectMemberActions,
|
||||
// canPerformWorkspaceMemberActions,
|
||||
canPerformAnyCreateAction,
|
||||
canPerformProjectAdminActions,
|
||||
// canPerformProjectAdminActions,
|
||||
} = useUser();
|
||||
const {
|
||||
issues: { removeIssue },
|
||||
|
|
@ -73,6 +74,7 @@ export const CommandPalette: FC = observer(() => {
|
|||
toggleDeleteIssueModal,
|
||||
isAnyModalOpen,
|
||||
} = useCommandPalette();
|
||||
const { allowPermissions } = useUserPermissions();
|
||||
|
||||
const { data: issueDetails } = useSWR(
|
||||
workspaceSlug && projectId && issueId ? ISSUE_DETAILS(issueId as string) : null,
|
||||
|
|
@ -81,6 +83,17 @@ export const CommandPalette: FC = observer(() => {
|
|||
: null
|
||||
);
|
||||
|
||||
// derived values
|
||||
const canPerformWorkspaceMemberActions = allowPermissions(
|
||||
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||
EUserPermissionsLevel.WORKSPACE
|
||||
);
|
||||
const canPerformProjectMemberActions = allowPermissions(
|
||||
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||
EUserPermissionsLevel.PROJECT
|
||||
);
|
||||
const canPerformProjectAdminActions = allowPermissions([EUserPermissions.ADMIN], EUserPermissionsLevel.PROJECT);
|
||||
|
||||
const copyIssueUrlToClipboard = useCallback(() => {
|
||||
if (!issueId) return;
|
||||
|
||||
|
|
|
|||
|
|
@ -17,16 +17,16 @@ import { DateRangeDropdown } from "@/components/dropdowns";
|
|||
// constants
|
||||
import { CYCLE_STATUS } from "@/constants/cycle";
|
||||
import { CYCLE_UPDATED } from "@/constants/event-tracker";
|
||||
import { EUserWorkspaceRoles } from "@/constants/workspace";
|
||||
// helpers
|
||||
import { findHowManyDaysLeft, getDate, renderFormattedPayloadDate } from "@/helpers/date-time.helper";
|
||||
import { copyUrlToClipboard } from "@/helpers/string.helper";
|
||||
// hooks
|
||||
import { useEventTracker, useCycle, useUser, useMember, useProjectEstimates } from "@/hooks/store";
|
||||
import { useEventTracker, useCycle, useMember, useProjectEstimates, useUserPermissions } from "@/hooks/store";
|
||||
import { useAppRouter } from "@/hooks/use-app-router";
|
||||
// plane web constants
|
||||
import { EEstimateSystem } from "@/plane-web/constants/estimates";
|
||||
// services
|
||||
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||
import { CycleService } from "@/services/cycle.service";
|
||||
|
||||
type Props = {
|
||||
|
|
@ -55,9 +55,8 @@ export const CycleDetailsSidebar: React.FC<Props> = observer((props) => {
|
|||
// store hooks
|
||||
const { setTrackElement, captureCycleEvent } = useEventTracker();
|
||||
const { areEstimateEnabledByProjectId, currentActiveEstimateId, estimateById } = useProjectEstimates();
|
||||
const {
|
||||
membership: { currentProjectRole },
|
||||
} = useUser();
|
||||
const { allowPermissions } = useUserPermissions();
|
||||
|
||||
const { getCycleById, updateCycleDetails, restoreCycle } = useCycle();
|
||||
const { getUserDetails } = useMember();
|
||||
// derived values
|
||||
|
|
@ -236,7 +235,10 @@ export const CycleDetailsSidebar: React.FC<Props> = observer((props) => {
|
|||
|
||||
const daysLeft = findHowManyDaysLeft(cycleDetails.end_date);
|
||||
|
||||
const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserWorkspaceRoles.MEMBER;
|
||||
const isEditingAllowed = allowPermissions(
|
||||
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||
EUserPermissionsLevel.PROJECT
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="relative">
|
||||
|
|
|
|||
|
|
@ -4,9 +4,10 @@ import { TCycleFilters } from "@plane/types";
|
|||
// hooks
|
||||
import { Tag } from "@plane/ui";
|
||||
import { AppliedDateFilters, AppliedStatusFilters } from "@/components/cycles";
|
||||
import { EUserProjectRoles } from "@/constants/project";
|
||||
import { replaceUnderscoreIfSnakeCase } from "@/helpers/string.helper";
|
||||
import { useUser } from "@/hooks/store";
|
||||
import { useUserPermissions } from "@/hooks/store";
|
||||
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||
|
||||
// components
|
||||
// helpers
|
||||
// types
|
||||
|
|
@ -24,15 +25,15 @@ const DATE_FILTERS = ["start_date", "end_date"];
|
|||
export const CycleAppliedFiltersList: React.FC<Props> = observer((props) => {
|
||||
const { appliedFilters, handleClearAllFilters, handleRemoveFilter, alwaysAllowEditing } = props;
|
||||
// store hooks
|
||||
const {
|
||||
membership: { currentProjectRole },
|
||||
} = useUser();
|
||||
const { allowPermissions } = useUserPermissions();
|
||||
|
||||
if (!appliedFilters) return null;
|
||||
|
||||
if (Object.keys(appliedFilters).length === 0) return null;
|
||||
|
||||
const isEditingAllowed = alwaysAllowEditing || (currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER);
|
||||
const isEditingAllowed =
|
||||
alwaysAllowEditing ||
|
||||
allowPermissions([EUserPermissions.ADMIN, EUserPermissions.MEMBER], EUserPermissionsLevel.PROJECT);
|
||||
|
||||
return (
|
||||
<div className="flex flex-wrap items-stretch gap-2 bg-custom-background-100">
|
||||
|
|
|
|||
|
|
@ -14,14 +14,14 @@ import { CycleQuickActions } from "@/components/cycles";
|
|||
// constants
|
||||
import { CYCLE_STATUS } from "@/constants/cycle";
|
||||
import { CYCLE_FAVORITED, CYCLE_UNFAVORITED } from "@/constants/event-tracker";
|
||||
import { EUserWorkspaceRoles } from "@/constants/workspace";
|
||||
// helpers
|
||||
import { findHowManyDaysLeft, getDate, renderFormattedDate } from "@/helpers/date-time.helper";
|
||||
import { generateQueryParams } from "@/helpers/router.helper";
|
||||
// hooks
|
||||
import { useEventTracker, useCycle, useUser, useMember } from "@/hooks/store";
|
||||
import { useEventTracker, useCycle, useMember, useUserPermissions } from "@/hooks/store";
|
||||
import { useAppRouter } from "@/hooks/use-app-router";
|
||||
import { usePlatformOS } from "@/hooks/use-platform-os";
|
||||
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||
|
||||
export interface ICyclesBoardCard {
|
||||
workspaceSlug: string;
|
||||
|
|
@ -39,9 +39,8 @@ export const CyclesBoardCard: FC<ICyclesBoardCard> = observer((props) => {
|
|||
const pathname = usePathname();
|
||||
// store
|
||||
const { captureEvent } = useEventTracker();
|
||||
const {
|
||||
membership: { currentProjectRole },
|
||||
} = useUser();
|
||||
const { allowPermissions } = useUserPermissions();
|
||||
|
||||
const { addCycleToFavorites, removeCycleFromFavorites, getCycleById } = useCycle();
|
||||
const { getUserDetails } = useMember();
|
||||
// computed
|
||||
|
|
@ -57,7 +56,10 @@ export const CyclesBoardCard: FC<ICyclesBoardCard> = observer((props) => {
|
|||
const startDate = getDate(cycleDetails.start_date);
|
||||
const isDateValid = cycleDetails.start_date || cycleDetails.end_date;
|
||||
|
||||
const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserWorkspaceRoles.MEMBER;
|
||||
const isEditingAllowed = allowPermissions(
|
||||
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||
EUserPermissionsLevel.PROJECT
|
||||
);
|
||||
|
||||
const currentCycle = CYCLE_STATUS.find((status) => status.value === cycleStatus);
|
||||
|
||||
|
|
|
|||
|
|
@ -15,13 +15,14 @@ import { ButtonAvatars } from "@/components/dropdowns/member/avatar";
|
|||
// constants
|
||||
import { CYCLE_STATUS } from "@/constants/cycle";
|
||||
import { CYCLE_FAVORITED, CYCLE_UNFAVORITED } from "@/constants/event-tracker";
|
||||
import { EUserProjectRoles } from "@/constants/project";
|
||||
// helpers
|
||||
import { findHowManyDaysLeft, getDate, renderFormattedPayloadDate } from "@/helpers/date-time.helper";
|
||||
// hooks
|
||||
import { useCycle, useEventTracker, useMember, useUser } from "@/hooks/store";
|
||||
import { useCycle, useEventTracker, useMember, useUserPermissions } from "@/hooks/store";
|
||||
import { usePlatformOS } from "@/hooks/use-platform-os";
|
||||
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||
import { CycleService } from "@/services/cycle.service";
|
||||
|
||||
const cycleService = new CycleService();
|
||||
|
||||
type Props = {
|
||||
|
|
@ -44,9 +45,8 @@ export const CycleListItemAction: FC<Props> = observer((props) => {
|
|||
// store hooks
|
||||
const { addCycleToFavorites, removeCycleFromFavorites, updateCycleDetails } = useCycle();
|
||||
const { captureEvent } = useEventTracker();
|
||||
const {
|
||||
membership: { currentProjectRole },
|
||||
} = useUser();
|
||||
const { allowPermissions } = useUserPermissions();
|
||||
|
||||
const { getUserDetails } = useMember();
|
||||
|
||||
// form
|
||||
|
|
@ -56,7 +56,10 @@ export const CycleListItemAction: FC<Props> = observer((props) => {
|
|||
|
||||
// derived values
|
||||
const cycleStatus = cycleDetails.status ? (cycleDetails.status.toLocaleLowerCase() as TCycleGroups) : "draft";
|
||||
const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER;
|
||||
const isEditingAllowed = allowPermissions(
|
||||
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||
EUserPermissionsLevel.PROJECT
|
||||
);
|
||||
const renderIcon = Boolean(cycleDetails.start_date) || Boolean(cycleDetails.end_date);
|
||||
const currentCycle = CYCLE_STATUS.find((status) => status.value === cycleStatus);
|
||||
const daysLeft = findHowManyDaysLeft(cycleDetails.end_date) ?? 0;
|
||||
|
|
|
|||
|
|
@ -9,14 +9,13 @@ import { ArchiveRestoreIcon, ExternalLink, LinkIcon, Pencil, Trash2 } from "luci
|
|||
import { ArchiveIcon, ContextMenu, CustomMenu, TContextMenuItem, TOAST_TYPE, setToast } from "@plane/ui";
|
||||
// components
|
||||
import { ArchiveCycleModal, CycleCreateUpdateModal, CycleDeleteModal } from "@/components/cycles";
|
||||
// constants
|
||||
import { EUserProjectRoles } from "@/constants/project";
|
||||
// helpers
|
||||
import { cn } from "@/helpers/common.helper";
|
||||
import { copyUrlToClipboard } from "@/helpers/string.helper";
|
||||
// hooks
|
||||
import { useCycle, useEventTracker, useUser } from "@/hooks/store";
|
||||
import { useCycle, useEventTracker, useUserPermissions } from "@/hooks/store";
|
||||
import { useAppRouter } from "@/hooks/use-app-router";
|
||||
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||
|
||||
type Props = {
|
||||
parentRef: React.RefObject<HTMLElement>;
|
||||
|
|
@ -35,17 +34,19 @@ export const CycleQuickActions: React.FC<Props> = observer((props) => {
|
|||
const [deleteModal, setDeleteModal] = useState(false);
|
||||
// store hooks
|
||||
const { setTrackElement } = useEventTracker();
|
||||
const {
|
||||
membership: { currentWorkspaceAllProjectsRole },
|
||||
} = useUser();
|
||||
const { allowPermissions } = useUserPermissions();
|
||||
const { getCycleById, restoreCycle } = useCycle();
|
||||
// derived values
|
||||
const cycleDetails = getCycleById(cycleId);
|
||||
const isArchived = !!cycleDetails?.archived_at;
|
||||
const isCompleted = cycleDetails?.status?.toLowerCase() === "completed";
|
||||
// auth
|
||||
const isEditingAllowed =
|
||||
!!currentWorkspaceAllProjectsRole && currentWorkspaceAllProjectsRole[projectId] >= EUserProjectRoles.MEMBER;
|
||||
const isEditingAllowed = allowPermissions(
|
||||
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||
EUserPermissionsLevel.PROJECT,
|
||||
workspaceSlug,
|
||||
projectId
|
||||
);
|
||||
|
||||
const cycleLink = `${workspaceSlug}/projects/${projectId}/cycles/${cycleId}`;
|
||||
const handleCopyText = () =>
|
||||
|
|
|
|||
|
|
@ -4,10 +4,9 @@ import { observer } from "mobx-react";
|
|||
import Image from "next/image";
|
||||
// ui
|
||||
import { Button } from "@plane/ui";
|
||||
// constants
|
||||
import { EUserWorkspaceRoles } from "@/constants/workspace";
|
||||
// hooks
|
||||
import { useCommandPalette, useEventTracker, useUser } from "@/hooks/store";
|
||||
import { useCommandPalette, useEventTracker, useUserPermissions } from "@/hooks/store";
|
||||
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||
// assets
|
||||
import ProjectEmptyStateImage from "@/public/empty-state/onboarding/dashboard-light.webp";
|
||||
|
||||
|
|
@ -15,11 +14,10 @@ export const DashboardProjectEmptyState = observer(() => {
|
|||
// store hooks
|
||||
const { toggleCreateProjectModal } = useCommandPalette();
|
||||
const { setTrackElement } = useEventTracker();
|
||||
const {
|
||||
membership: { currentWorkspaceRole },
|
||||
} = useUser();
|
||||
const { allowPermissions } = useUserPermissions();
|
||||
|
||||
// derived values
|
||||
const canCreateProject = currentWorkspaceRole === EUserWorkspaceRoles.ADMIN;
|
||||
const canCreateProject = allowPermissions([EUserPermissions.ADMIN], EUserPermissionsLevel.WORKSPACE);
|
||||
|
||||
return (
|
||||
<div className="mx-auto flex h-full flex-col justify-center space-y-4 lg:w-3/5">
|
||||
|
|
|
|||
|
|
@ -14,9 +14,9 @@ import { Logo } from "@/components/common";
|
|||
import { WidgetLoader, WidgetProps } from "@/components/dashboard/widgets";
|
||||
// constants
|
||||
import { PROJECT_BACKGROUND_COLORS } from "@/constants/dashboard";
|
||||
import { EUserWorkspaceRoles } from "@/constants/workspace";
|
||||
// hooks
|
||||
import { useEventTracker, useDashboard, useProject, useUser, useCommandPalette } from "@/hooks/store";
|
||||
import { useEventTracker, useDashboard, useProject, useCommandPalette, useUserPermissions } from "@/hooks/store";
|
||||
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||
|
||||
const WIDGET_KEY = "recent_projects";
|
||||
|
||||
|
|
@ -65,13 +65,14 @@ export const RecentProjectsWidget: React.FC<WidgetProps> = observer((props) => {
|
|||
// store hooks
|
||||
const { toggleCreateProjectModal } = useCommandPalette();
|
||||
const { setTrackElement } = useEventTracker();
|
||||
const {
|
||||
membership: { currentWorkspaceRole },
|
||||
} = useUser();
|
||||
const { allowPermissions } = useUserPermissions();
|
||||
const { fetchWidgetStats, getWidgetStats } = useDashboard();
|
||||
// derived values
|
||||
const widgetStats = getWidgetStats<TRecentProjectsWidgetResponse>(workspaceSlug, dashboardId, WIDGET_KEY);
|
||||
const canCreateProject = currentWorkspaceRole && currentWorkspaceRole >= EUserWorkspaceRoles.MEMBER;
|
||||
const canCreateProject = allowPermissions(
|
||||
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||
EUserPermissionsLevel.WORKSPACE
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
fetchWidgetStats(workspaceSlug, dashboardId, {
|
||||
|
|
|
|||
|
|
@ -13,7 +13,8 @@ import { Button, TButtonVariant } from "@plane/ui";
|
|||
import { EMPTY_STATE_DETAILS, EmptyStateType } from "@/constants/empty-state";
|
||||
// helpers
|
||||
import { cn } from "@/helpers/common.helper";
|
||||
import { useUser } from "@/hooks/store";
|
||||
import { useUserPermissions } from "@/hooks/store";
|
||||
import { EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||
import { ComicBoxButton } from "./comic-box-button";
|
||||
|
||||
export type EmptyStateProps = {
|
||||
|
|
@ -37,9 +38,7 @@ export const EmptyState: React.FC<EmptyStateProps> = observer((props) => {
|
|||
secondaryButtonOnClick,
|
||||
} = props;
|
||||
// store
|
||||
const {
|
||||
membership: { currentWorkspaceRole, currentProjectRole },
|
||||
} = useUser();
|
||||
const { allowPermissions } = useUserPermissions();
|
||||
// theme
|
||||
const { resolvedTheme } = useTheme();
|
||||
|
||||
|
|
@ -53,10 +52,14 @@ export const EmptyState: React.FC<EmptyStateProps> = observer((props) => {
|
|||
const resolvedEmptyStatePath = `${additionalPath && additionalPath !== "" ? `${path}${additionalPath}` : path}-${
|
||||
resolvedTheme === "light" ? "light" : "dark"
|
||||
}.webp`;
|
||||
// current access type
|
||||
const currentAccessType = accessType === "workspace" ? currentWorkspaceRole : currentProjectRole;
|
||||
// permission
|
||||
const isEditingAllowed = currentAccessType && access && currentAccessType >= access;
|
||||
const isEditingAllowed =
|
||||
access &&
|
||||
accessType &&
|
||||
allowPermissions(
|
||||
access,
|
||||
accessType === "workspace" ? EUserPermissionsLevel.WORKSPACE : EUserPermissionsLevel.PROJECT
|
||||
);
|
||||
const anyButton = primaryButton || secondaryButton;
|
||||
|
||||
// primary button
|
||||
|
|
|
|||
|
|
@ -17,10 +17,11 @@ import { ImportExportSettingsLoader } from "@/components/ui";
|
|||
// constants
|
||||
import { EmptyStateType } from "@/constants/empty-state";
|
||||
import { EXPORT_SERVICES_LIST } from "@/constants/fetch-keys";
|
||||
import { EUserWorkspaceRoles, EXPORTERS_LIST } from "@/constants/workspace";
|
||||
import { EXPORTERS_LIST } from "@/constants/workspace";
|
||||
// hooks
|
||||
import { useProject, useUser } from "@/hooks/store";
|
||||
import { useProject, useUser, useUserPermissions } from "@/hooks/store";
|
||||
import { useAppRouter } from "@/hooks/use-app-router";
|
||||
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||
// services
|
||||
import { IntegrationService } from "@/services/integrations";
|
||||
|
||||
|
|
@ -37,11 +38,9 @@ const IntegrationGuide = observer(() => {
|
|||
const searchParams = useSearchParams();
|
||||
const provider = searchParams.get("provider");
|
||||
// store hooks
|
||||
const {
|
||||
data: currentUser,
|
||||
canPerformAnyCreateAction,
|
||||
membership: { currentWorkspaceRole },
|
||||
} = useUser();
|
||||
const { data: currentUser, canPerformAnyCreateAction } = useUser();
|
||||
const { allowPermissions } = useUserPermissions();
|
||||
|
||||
const { workspaceProjectIds } = useProject();
|
||||
|
||||
const { data: exporterServices } = useSWR(
|
||||
|
|
@ -61,7 +60,7 @@ const IntegrationGuide = observer(() => {
|
|||
};
|
||||
|
||||
const hasProjects = workspaceProjectIds && workspaceProjectIds.length > 0;
|
||||
const isAdmin = currentWorkspaceRole === EUserWorkspaceRoles.ADMIN;
|
||||
const isAdmin = allowPermissions([EUserPermissions.ADMIN], EUserPermissionsLevel.WORKSPACE);
|
||||
|
||||
useEffect(() => {
|
||||
const interval = setInterval(() => {
|
||||
|
|
|
|||
|
|
@ -27,15 +27,14 @@ import {
|
|||
SelectDuplicateInboxIssueModal,
|
||||
} from "@/components/inbox";
|
||||
import { IssueUpdateStatus } from "@/components/issues";
|
||||
// constants
|
||||
import { EUserProjectRoles } from "@/constants/project";
|
||||
// helpers
|
||||
import { findHowManyDaysLeft } from "@/helpers/date-time.helper";
|
||||
import { EInboxIssueStatus } from "@/helpers/inbox.helper";
|
||||
import { copyUrlToClipboard } from "@/helpers/string.helper";
|
||||
// hooks
|
||||
import { useUser, useProjectInbox, useProject } from "@/hooks/store";
|
||||
import { useUser, useProjectInbox, useProject, useUserPermissions } from "@/hooks/store";
|
||||
import { useAppRouter } from "@/hooks/use-app-router";
|
||||
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||
// store types
|
||||
import type { IInboxIssueStore } from "@/store/inbox/inbox-issue.store";
|
||||
|
||||
|
|
@ -70,23 +69,26 @@ export const InboxIssueActionsHeader: FC<TInboxIssueActionsHeader> = observer((p
|
|||
// store
|
||||
const { currentTab, deleteInboxIssue, filteredInboxIssueIds } = useProjectInbox();
|
||||
const { data: currentUser } = useUser();
|
||||
const {
|
||||
membership: { currentProjectRoleByProjectId },
|
||||
} = useUser();
|
||||
const { allowPermissions } = useUserPermissions();
|
||||
|
||||
const router = useAppRouter();
|
||||
const { getProjectById } = useProject();
|
||||
|
||||
const issue = inboxIssue?.issue;
|
||||
// derived values
|
||||
const currentProjectRole = currentProjectRoleByProjectId(projectId) || undefined;
|
||||
const isAllowed = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER;
|
||||
const isAllowed = allowPermissions(
|
||||
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||
EUserPermissionsLevel.PROJECT,
|
||||
workspaceSlug,
|
||||
projectId
|
||||
);
|
||||
const canMarkAsDuplicate = isAllowed && (inboxIssue?.status === 0 || inboxIssue?.status === -2);
|
||||
const canMarkAsAccepted = isAllowed && (inboxIssue?.status === 0 || inboxIssue?.status === -2);
|
||||
const canMarkAsDeclined = isAllowed && (inboxIssue?.status === 0 || inboxIssue?.status === -2);
|
||||
// can delete only if admin or is creator of the issue
|
||||
const canDelete =
|
||||
(!!currentProjectRole && currentProjectRole >= EUserProjectRoles.ADMIN) || issue?.created_by === currentUser?.id;
|
||||
allowPermissions([EUserPermissions.ADMIN], EUserPermissionsLevel.PROJECT, workspaceSlug, projectId) ||
|
||||
issue?.created_by === currentUser?.id;
|
||||
const isAcceptedOrDeclined = inboxIssue?.status ? [-1, 1, 2].includes(inboxIssue.status) : undefined;
|
||||
// days left for snooze
|
||||
const numberOfDaysLeft = findHowManyDaysLeft(inboxIssue?.snoozed_till);
|
||||
|
|
|
|||
|
|
@ -4,11 +4,10 @@ import useSWR from "swr";
|
|||
// components
|
||||
import { ContentWrapper } from "@plane/ui";
|
||||
import { InboxIssueActionsHeader, InboxIssueMainContent } from "@/components/inbox";
|
||||
// constants
|
||||
import { EUserProjectRoles } from "@/constants/project";
|
||||
// hooks
|
||||
import { useProjectInbox, useUser } from "@/hooks/store";
|
||||
import { useProjectInbox, useUserPermissions } from "@/hooks/store";
|
||||
import { useAppRouter } from "@/hooks/use-app-router";
|
||||
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||
|
||||
type TInboxContentRoot = {
|
||||
workspaceSlug: string;
|
||||
|
|
@ -37,9 +36,8 @@ export const InboxContentRoot: FC<TInboxContentRoot> = observer((props) => {
|
|||
// hooks
|
||||
const { currentTab, fetchInboxIssueById, getIssueInboxByIssueId, getIsIssueAvailable } = useProjectInbox();
|
||||
const inboxIssue = getIssueInboxByIssueId(inboxIssueId);
|
||||
const {
|
||||
membership: { currentProjectRole },
|
||||
} = useUser();
|
||||
const { allowPermissions } = useUserPermissions();
|
||||
|
||||
// derived values
|
||||
const isIssueAvailable = getIsIssueAvailable(inboxIssueId?.toString() || "");
|
||||
|
||||
|
|
@ -63,7 +61,7 @@ export const InboxContentRoot: FC<TInboxContentRoot> = observer((props) => {
|
|||
}
|
||||
);
|
||||
|
||||
const isEditable = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER;
|
||||
const isEditable = allowPermissions([EUserPermissions.ADMIN, EUserPermissions.MEMBER], EUserPermissionsLevel.PROJECT);
|
||||
|
||||
if (!inboxIssue) return <></>;
|
||||
|
||||
|
|
|
|||
|
|
@ -12,9 +12,10 @@ import { Button, Loader, Tooltip, TOAST_TYPE, setToast } from "@plane/ui";
|
|||
// constants
|
||||
import { WORKSPACE_INTEGRATIONS } from "@/constants/fetch-keys";
|
||||
// hooks
|
||||
import { useUser, useInstance } from "@/hooks/store";
|
||||
import { useInstance, useUserPermissions } from "@/hooks/store";
|
||||
import useIntegrationPopup from "@/hooks/use-integration-popup";
|
||||
import { usePlatformOS } from "@/hooks/use-platform-os";
|
||||
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||
// services
|
||||
// icons
|
||||
import GithubLogo from "@/public/services/github.png";
|
||||
|
|
@ -48,11 +49,9 @@ export const SingleIntegrationCard: React.FC<Props> = observer(({ integration })
|
|||
const { workspaceSlug } = useParams();
|
||||
// store hooks
|
||||
const { config } = useInstance();
|
||||
const {
|
||||
membership: { currentWorkspaceRole },
|
||||
} = useUser();
|
||||
const { allowPermissions } = useUserPermissions();
|
||||
|
||||
const isUserAdmin = currentWorkspaceRole === 20;
|
||||
const isUserAdmin = allowPermissions([EUserPermissions.ADMIN], EUserPermissionsLevel.WORKSPACE);
|
||||
const { isMobile } = usePlatformOS();
|
||||
const { startAuth, isConnecting: isInstalling } = useIntegrationPopup({
|
||||
provider: integration.provider,
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ import { AlertModalCore, TOAST_TYPE, setToast } from "@plane/ui";
|
|||
// constants
|
||||
import { PROJECT_ERROR_MESSAGES } from "@/constants/project";
|
||||
// hooks
|
||||
import { useIssues, useProject, useUser } from "@/hooks/store";
|
||||
|
||||
import { useIssues, useProject, useUser, useUserPermissions } from "@/hooks/store";
|
||||
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||
type Props = {
|
||||
isOpen: boolean;
|
||||
handleClose: () => void;
|
||||
|
|
@ -26,7 +26,12 @@ export const DeleteIssueModal: React.FC<Props> = (props) => {
|
|||
// store hooks
|
||||
const { issueMap } = useIssues();
|
||||
const { getProjectById } = useProject();
|
||||
const { data: currentUser, canPerformProjectAdminActions } = useUser();
|
||||
const { allowPermissions } = useUserPermissions();
|
||||
|
||||
const { data: currentUser } = useUser();
|
||||
|
||||
// derived values
|
||||
const canPerformProjectAdminActions = allowPermissions([EUserPermissions.ADMIN], EUserPermissionsLevel.PROJECT);
|
||||
|
||||
useEffect(() => {
|
||||
setIsDeleting(false);
|
||||
|
|
|
|||
|
|
@ -10,15 +10,22 @@ import { ArchiveIssueModal, DeleteIssueModal, IssueSubscription } from "@/compon
|
|||
// constants
|
||||
import { ISSUE_ARCHIVED, ISSUE_DELETED } from "@/constants/event-tracker";
|
||||
import { EIssuesStoreType } from "@/constants/issue";
|
||||
import { EUserProjectRoles } from "@/constants/project";
|
||||
import { ARCHIVABLE_STATE_GROUPS } from "@/constants/state";
|
||||
// helpers
|
||||
import { cn } from "@/helpers/common.helper";
|
||||
import { copyTextToClipboard } from "@/helpers/string.helper";
|
||||
// hooks
|
||||
import { useEventTracker, useIssueDetail, useIssues, useProjectState, useUser } from "@/hooks/store";
|
||||
import {
|
||||
useEventTracker,
|
||||
useIssueDetail,
|
||||
useIssues,
|
||||
useProjectState,
|
||||
useUser,
|
||||
useUserPermissions,
|
||||
} from "@/hooks/store";
|
||||
import { useAppRouter } from "@/hooks/use-app-router";
|
||||
import { usePlatformOS } from "@/hooks/use-platform-os";
|
||||
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||
|
||||
type Props = {
|
||||
workspaceSlug: string;
|
||||
|
|
@ -38,10 +45,8 @@ export const IssueDetailQuickActions: FC<Props> = observer((props) => {
|
|||
const router = useAppRouter();
|
||||
|
||||
// hooks
|
||||
const {
|
||||
data: currentUser,
|
||||
membership: { currentProjectRole },
|
||||
} = useUser();
|
||||
const { data: currentUser } = useUser();
|
||||
const { allowPermissions } = useUserPermissions();
|
||||
const { isMobile } = usePlatformOS();
|
||||
const { getStateById } = useProjectState();
|
||||
const {
|
||||
|
|
@ -149,8 +154,11 @@ export const IssueDetailQuickActions: FC<Props> = observer((props) => {
|
|||
};
|
||||
|
||||
// auth
|
||||
const isEditable = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER;
|
||||
const canRestoreIssue = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER;
|
||||
const isEditable = allowPermissions([EUserPermissions.ADMIN, EUserPermissions.MEMBER], EUserPermissionsLevel.PROJECT);
|
||||
const canRestoreIssue = allowPermissions(
|
||||
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||
EUserPermissionsLevel.PROJECT
|
||||
);
|
||||
const isArchivingAllowed = !issue?.archived_at && isEditable;
|
||||
const isInArchivableGroup = !!stateDetails && ARCHIVABLE_STATE_GROUPS.includes(stateDetails?.group);
|
||||
|
||||
|
|
|
|||
|
|
@ -13,10 +13,10 @@ import { IssuePeekOverview } from "@/components/issues";
|
|||
// constants
|
||||
import { ISSUE_UPDATED, ISSUE_DELETED, ISSUE_ARCHIVED } from "@/constants/event-tracker";
|
||||
import { EIssuesStoreType } from "@/constants/issue";
|
||||
import { EUserProjectRoles } from "@/constants/project";
|
||||
// hooks
|
||||
import { useAppTheme, useEventTracker, useIssueDetail, useIssues, useUser } from "@/hooks/store";
|
||||
import { useAppTheme, useEventTracker, useIssueDetail, useIssues, useUserPermissions } from "@/hooks/store";
|
||||
import { useAppRouter } from "@/hooks/use-app-router";
|
||||
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||
// images
|
||||
import emptyIssue from "@/public/empty-state/issue.svg";
|
||||
// local components
|
||||
|
|
@ -77,9 +77,7 @@ export const IssueDetailRoot: FC<TIssueDetailRoot> = observer((props) => {
|
|||
issues: { removeIssue: removeArchivedIssue },
|
||||
} = useIssues(EIssuesStoreType.ARCHIVED);
|
||||
const { captureIssueEvent } = useEventTracker();
|
||||
const {
|
||||
membership: { currentProjectRole },
|
||||
} = useUser();
|
||||
const { allowPermissions } = useUserPermissions();
|
||||
const { issueDetailSidebarCollapsed } = useAppTheme();
|
||||
|
||||
const issueOperations: TIssueOperations = useMemo(
|
||||
|
|
@ -332,7 +330,7 @@ export const IssueDetailRoot: FC<TIssueDetailRoot> = observer((props) => {
|
|||
// issue details
|
||||
const issue = getIssueById(issueId);
|
||||
// checking if issue is editable, based on user role
|
||||
const isEditable = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER;
|
||||
const isEditable = allowPermissions([EUserPermissions.ADMIN, EUserPermissions.MEMBER], EUserPermissionsLevel.PROJECT);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
|
|||
|
|
@ -10,11 +10,11 @@ import { TOAST_TYPE, setToast } from "@plane/ui";
|
|||
import { CalendarChart } from "@/components/issues";
|
||||
//constants
|
||||
import { EIssuesStoreType } from "@/constants/issue";
|
||||
import { EUserProjectRoles } from "@/constants/project";
|
||||
// hooks
|
||||
import { useIssues, useUser, useCalendarView } from "@/hooks/store";
|
||||
import { useIssues, useCalendarView, useUserPermissions } from "@/hooks/store";
|
||||
import { useIssueStoreType } from "@/hooks/use-issue-layout-store";
|
||||
import { useIssuesActions } from "@/hooks/use-issues-actions";
|
||||
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||
// types
|
||||
import { IQuickActionProps } from "../list/list-view-types";
|
||||
import { handleDragDrop } from "./utils";
|
||||
|
|
@ -40,9 +40,7 @@ export const BaseCalendarRoot = observer((props: IBaseCalendarRoot) => {
|
|||
|
||||
// hooks
|
||||
const storeType = useIssueStoreType() as CalendarStoreType;
|
||||
const {
|
||||
membership: { currentProjectRole },
|
||||
} = useUser();
|
||||
const { allowPermissions } = useUserPermissions();
|
||||
const { issues, issuesFilter, issueMap } = useIssues(storeType);
|
||||
const {
|
||||
fetchIssues,
|
||||
|
|
@ -58,7 +56,10 @@ export const BaseCalendarRoot = observer((props: IBaseCalendarRoot) => {
|
|||
|
||||
const issueCalendarView = useCalendarView();
|
||||
|
||||
const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER;
|
||||
const isEditingAllowed = allowPermissions(
|
||||
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||
EUserPermissionsLevel.PROJECT
|
||||
);
|
||||
|
||||
const displayFilters = issuesFilter.issueFilters?.displayFilters;
|
||||
|
||||
|
|
|
|||
|
|
@ -22,13 +22,13 @@ import { CalendarHeader, CalendarIssueBlocks, CalendarWeekDays, CalendarWeekHead
|
|||
// constants
|
||||
import { MONTHS_LIST } from "@/constants/calendar";
|
||||
import { EIssueFilterType, EIssueLayoutTypes, EIssuesStoreType } from "@/constants/issue";
|
||||
import { EUserProjectRoles } from "@/constants/project";
|
||||
// helpers
|
||||
import { cn } from "@/helpers/common.helper";
|
||||
import { renderFormattedPayloadDate } from "@/helpers/date-time.helper";
|
||||
// hooks
|
||||
import { useIssues, useUser } from "@/hooks/store";
|
||||
import { useIssues, useUserPermissions } from "@/hooks/store";
|
||||
import useSize from "@/hooks/use-window-size";
|
||||
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||
// store
|
||||
import { ICycleIssuesFilter } from "@/store/issue/cycle";
|
||||
import { ICalendarStore } from "@/store/issue/issue_calendar_view.store";
|
||||
|
|
@ -91,14 +91,15 @@ export const CalendarChart: React.FC<Props> = observer((props) => {
|
|||
const {
|
||||
issues: { viewFlags },
|
||||
} = useIssues(EIssuesStoreType.PROJECT);
|
||||
const { allowPermissions } = useUserPermissions();
|
||||
|
||||
const {
|
||||
membership: { currentProjectRole },
|
||||
} = useUser();
|
||||
const [windowWidth] = useSize();
|
||||
|
||||
const { enableIssueCreation } = viewFlags || {};
|
||||
const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER;
|
||||
const isEditingAllowed = allowPermissions(
|
||||
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||
EUserPermissionsLevel.PROJECT
|
||||
);
|
||||
|
||||
const calendarPayload = issueCalendarView.calendarPayload;
|
||||
|
||||
|
|
|
|||
|
|
@ -4,9 +4,9 @@ import { PlusIcon } from "lucide-react";
|
|||
import { EmptyState } from "@/components/common";
|
||||
// constants
|
||||
import { EIssuesStoreType } from "@/constants/issue";
|
||||
import { EUserProjectRoles } from "@/constants/project";
|
||||
// hooks
|
||||
import { useCommandPalette, useEventTracker, useUser } from "@/hooks/store";
|
||||
import { useCommandPalette, useEventTracker, useUserPermissions } from "@/hooks/store";
|
||||
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||
// assets
|
||||
import emptyIssue from "@/public/empty-state/issue.svg";
|
||||
|
||||
|
|
@ -14,11 +14,13 @@ export const ProjectViewEmptyState: React.FC = observer(() => {
|
|||
// store hooks
|
||||
const { toggleCreateIssueModal } = useCommandPalette();
|
||||
const { setTrackElement } = useEventTracker();
|
||||
const {
|
||||
membership: { currentProjectRole },
|
||||
} = useUser();
|
||||
const { allowPermissions } = useUserPermissions();
|
||||
|
||||
// auth
|
||||
const isCreatingIssueAllowed = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER;
|
||||
const isCreatingIssueAllowed = allowPermissions(
|
||||
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||
EUserPermissionsLevel.PROJECT
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="grid h-full w-full place-items-center">
|
||||
|
|
|
|||
|
|
@ -16,13 +16,13 @@ import {
|
|||
AppliedStateGroupFilters,
|
||||
} from "@/components/issues";
|
||||
// constants
|
||||
import { EUserProjectRoles } from "@/constants/project";
|
||||
// helpers
|
||||
import { replaceUnderscoreIfSnakeCase } from "@/helpers/string.helper";
|
||||
// hooks
|
||||
import { useUser } from "@/hooks/store";
|
||||
import { useUserPermissions } from "@/hooks/store";
|
||||
// plane web components
|
||||
import { AppliedIssueTypeFilters } from "@/plane-web/components/issues";
|
||||
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||
|
||||
type Props = {
|
||||
appliedFilters: IIssueFilterOptions;
|
||||
|
|
@ -48,16 +48,16 @@ export const AppliedFiltersList: React.FC<Props> = observer((props) => {
|
|||
disableEditing = false,
|
||||
} = props;
|
||||
// store hooks
|
||||
const {
|
||||
membership: { currentProjectRole },
|
||||
} = useUser();
|
||||
const { allowPermissions } = useUserPermissions();
|
||||
|
||||
if (!appliedFilters) return null;
|
||||
|
||||
if (Object.keys(appliedFilters).length === 0) return null;
|
||||
|
||||
const isEditingAllowed =
|
||||
!disableEditing && (alwaysAllowEditing || (currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER));
|
||||
!disableEditing &&
|
||||
(alwaysAllowEditing ||
|
||||
allowPermissions([EUserPermissions.ADMIN, EUserPermissions.MEMBER], EUserPermissionsLevel.PROJECT));
|
||||
|
||||
return (
|
||||
<div className="flex flex-wrap items-stretch gap-2 bg-custom-background-100 truncate my-auto">
|
||||
|
|
|
|||
|
|
@ -17,9 +17,10 @@ import { CreateUpdateWorkspaceViewModal } from "@/components/workspace";
|
|||
import { GLOBAL_VIEW_UPDATED } from "@/constants/event-tracker";
|
||||
import { EIssueFilterType, EIssuesStoreType } from "@/constants/issue";
|
||||
import { EViewAccess } from "@/constants/views";
|
||||
import { DEFAULT_GLOBAL_VIEWS_LIST, EUserWorkspaceRoles } from "@/constants/workspace";
|
||||
import { DEFAULT_GLOBAL_VIEWS_LIST } from "@/constants/workspace";
|
||||
// hooks
|
||||
import { useEventTracker, useGlobalView, useIssues, useLabel, useUser } from "@/hooks/store";
|
||||
import { useEventTracker, useGlobalView, useIssues, useLabel, useUser, useUserPermissions } from "@/hooks/store";
|
||||
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||
import { getAreFiltersEqual } from "../../../utils";
|
||||
|
||||
type Props = {
|
||||
|
|
@ -37,10 +38,8 @@ export const GlobalViewsAppliedFiltersRoot = observer((props: Props) => {
|
|||
const { workspaceLabels } = useLabel();
|
||||
const { globalViewMap, updateGlobalView } = useGlobalView();
|
||||
const { captureEvent } = useEventTracker();
|
||||
const {
|
||||
data,
|
||||
membership: { currentWorkspaceRole },
|
||||
} = useUser();
|
||||
const { data } = useUser();
|
||||
const { allowPermissions } = useUserPermissions();
|
||||
|
||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||
|
||||
|
|
@ -120,7 +119,10 @@ export const GlobalViewsAppliedFiltersRoot = observer((props: Props) => {
|
|||
// add a placeholder object instead of appliedFilters if it is undefined
|
||||
const areFiltersEqual = getAreFiltersEqual(appliedFilters ?? {}, issueFilters, viewDetails);
|
||||
|
||||
const isAuthorizedUser = !!currentWorkspaceRole && currentWorkspaceRole >= EUserWorkspaceRoles.MEMBER;
|
||||
const isAuthorizedUser = allowPermissions(
|
||||
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||
EUserPermissionsLevel.WORKSPACE
|
||||
);
|
||||
|
||||
const isDefaultView = DEFAULT_GLOBAL_VIEWS_LIST.map((view) => view.key).includes(globalViewId as TStaticViewTypes);
|
||||
|
||||
|
|
|
|||
|
|
@ -7,9 +7,10 @@ import { Header, EHeaderVariant } from "@plane/ui";
|
|||
import { AppliedFiltersList, SaveFilterView } from "@/components/issues";
|
||||
// constants
|
||||
import { EIssueFilterType, EIssuesStoreType } from "@/constants/issue";
|
||||
import { EUserProjectRoles } from "@/constants/project";
|
||||
import { useLabel, useProjectState, useUser } from "@/hooks/store";
|
||||
import { useLabel, useProjectState, useUserPermissions } from "@/hooks/store";
|
||||
import { useIssues } from "@/hooks/store/use-issues";
|
||||
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||
|
||||
// types
|
||||
|
||||
export const ProjectAppliedFiltersRoot: React.FC = observer(() => {
|
||||
|
|
@ -23,12 +24,14 @@ export const ProjectAppliedFiltersRoot: React.FC = observer(() => {
|
|||
const {
|
||||
issuesFilter: { issueFilters, updateFilters },
|
||||
} = useIssues(EIssuesStoreType.PROJECT);
|
||||
const {
|
||||
membership: { currentProjectRole },
|
||||
} = useUser();
|
||||
const { allowPermissions } = useUserPermissions();
|
||||
|
||||
const { projectStates } = useProjectState();
|
||||
// derived values
|
||||
const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER;
|
||||
const isEditingAllowed = allowPermissions(
|
||||
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||
EUserPermissionsLevel.PROJECT
|
||||
);
|
||||
const userFilters = issueFilters?.filters;
|
||||
// filters whose value not null or empty array
|
||||
const appliedFilters: IIssueFilterOptions = {};
|
||||
|
|
|
|||
|
|
@ -15,9 +15,9 @@ import { UpdateViewComponent } from "@/components/views/update-view-component";
|
|||
// constants
|
||||
import { EIssueFilterType, EIssuesStoreType } from "@/constants/issue";
|
||||
import { EViewAccess } from "@/constants/views";
|
||||
import { EUserWorkspaceRoles } from "@/constants/workspace";
|
||||
// hooks
|
||||
import { useIssues, useLabel, useProjectState, useProjectView, useUser } from "@/hooks/store";
|
||||
import { useIssues, useLabel, useProjectState, useProjectView, useUser, useUserPermissions } from "@/hooks/store";
|
||||
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||
import { getAreFiltersEqual } from "../../../utils";
|
||||
|
||||
export const ProjectViewAppliedFiltersRoot: React.FC = observer(() => {
|
||||
|
|
@ -30,10 +30,8 @@ export const ProjectViewAppliedFiltersRoot: React.FC = observer(() => {
|
|||
const { projectLabels } = useLabel();
|
||||
const { projectStates } = useProjectState();
|
||||
const { viewMap, updateView } = useProjectView();
|
||||
const {
|
||||
data,
|
||||
membership: { currentWorkspaceRole },
|
||||
} = useUser();
|
||||
const { data } = useUser();
|
||||
const { allowPermissions } = useUserPermissions();
|
||||
|
||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||
// derived values
|
||||
|
|
@ -108,7 +106,10 @@ export const ProjectViewAppliedFiltersRoot: React.FC = observer(() => {
|
|||
updateView(workspaceSlug.toString(), projectId.toString(), viewId.toString(), viewFilters);
|
||||
};
|
||||
|
||||
const isAuthorizedUser = !!currentWorkspaceRole && currentWorkspaceRole >= EUserWorkspaceRoles.MEMBER;
|
||||
const isAuthorizedUser = allowPermissions(
|
||||
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||
EUserPermissionsLevel.WORKSPACE
|
||||
);
|
||||
|
||||
const isLocked = !!viewDetails?.is_locked;
|
||||
const isOwner = viewDetails?.owned_by === data?.id;
|
||||
|
|
|
|||
|
|
@ -10,15 +10,15 @@ import { getMonthChartItemPositionWidthInMonth } from "@/components/gantt-chart/
|
|||
import { QuickAddIssueRoot, IssueGanttBlock, GanttQuickAddIssueButton } from "@/components/issues";
|
||||
//constants
|
||||
import { EIssueLayoutTypes, EIssuesStoreType } from "@/constants/issue";
|
||||
import { EUserProjectRoles } from "@/constants/project";
|
||||
// helpers
|
||||
import { renderFormattedPayloadDate } from "@/helpers/date-time.helper";
|
||||
import { getIssueBlocksStructure } from "@/helpers/issue.helper";
|
||||
//hooks
|
||||
import { useIssues, useUser } from "@/hooks/store";
|
||||
import { useIssues, useUserPermissions } from "@/hooks/store";
|
||||
import { useIssueStoreType } from "@/hooks/use-issue-layout-store";
|
||||
import { useIssuesActions } from "@/hooks/use-issues-actions";
|
||||
// plane web hooks
|
||||
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||
import { useBulkOperationStatus } from "@/plane-web/hooks/use-bulk-operation-status";
|
||||
|
||||
import { IssueLayoutHOC } from "../issue-layout-HOC";
|
||||
|
|
@ -43,9 +43,8 @@ export const BaseGanttRoot: React.FC<IBaseGanttRoot> = observer((props: IBaseGan
|
|||
const { issues, issuesFilter, issueMap } = useIssues(storeType);
|
||||
const { fetchIssues, fetchNextIssues, updateIssue, quickAddIssue } = useIssuesActions(storeType);
|
||||
// store hooks
|
||||
const {
|
||||
membership: { currentProjectRole },
|
||||
} = useUser();
|
||||
const { allowPermissions } = useUserPermissions();
|
||||
|
||||
const appliedDisplayFilters = issuesFilter.issueFilters?.displayFilters;
|
||||
// plane web hooks
|
||||
const isBulkOperationsEnabled = useBulkOperationStatus();
|
||||
|
|
@ -90,7 +89,7 @@ export const BaseGanttRoot: React.FC<IBaseGanttRoot> = observer((props: IBaseGan
|
|||
updateIssue && (await updateIssue(issue.project_id, issue.id, payload));
|
||||
};
|
||||
|
||||
const isAllowed = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER;
|
||||
const isAllowed = allowPermissions([EUserPermissions.ADMIN, EUserPermissions.MEMBER], EUserPermissionsLevel.PROJECT);
|
||||
|
||||
const quickAdd =
|
||||
enableIssueCreation && isAllowed && !isCompletedCycle ? (
|
||||
|
|
|
|||
|
|
@ -11,12 +11,12 @@ import { DeleteIssueModal } from "@/components/issues";
|
|||
//constants
|
||||
import { ISSUE_DELETED } from "@/constants/event-tracker";
|
||||
import { EIssueFilterType, EIssueLayoutTypes, EIssuesStoreType } from "@/constants/issue";
|
||||
import { EUserProjectRoles } from "@/constants/project";
|
||||
//hooks
|
||||
import { useEventTracker, useIssueDetail, useIssues, useKanbanView, useUser } from "@/hooks/store";
|
||||
import { useEventTracker, useIssueDetail, useIssues, useKanbanView, useUserPermissions } from "@/hooks/store";
|
||||
import { useGroupIssuesDragNDrop } from "@/hooks/use-group-dragndrop";
|
||||
import { useIssueStoreType } from "@/hooks/use-issue-layout-store";
|
||||
import { useIssuesActions } from "@/hooks/use-issues-actions";
|
||||
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||
// store
|
||||
// ui
|
||||
// types
|
||||
|
|
@ -49,9 +49,7 @@ export const BaseKanBanRoot: React.FC<IBaseKanBanLayout> = observer((props: IBas
|
|||
const pathname = usePathname();
|
||||
// store hooks
|
||||
const storeType = useIssueStoreType() as KanbanStoreType;
|
||||
const {
|
||||
membership: { currentProjectRole },
|
||||
} = useUser();
|
||||
const { allowPermissions } = useUserPermissions();
|
||||
const { captureIssueEvent } = useEventTracker();
|
||||
const { issueMap, issuesFilter, issues } = useIssues(storeType);
|
||||
const {
|
||||
|
|
@ -115,7 +113,10 @@ export const BaseKanBanRoot: React.FC<IBaseKanBanLayout> = observer((props: IBas
|
|||
const [draggedIssueId, setDraggedIssueId] = useState<string | undefined>(undefined);
|
||||
const [deleteIssueModal, setDeleteIssueModal] = useState(false);
|
||||
|
||||
const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER;
|
||||
const isEditingAllowed = allowPermissions(
|
||||
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||
EUserPermissionsLevel.PROJECT
|
||||
);
|
||||
|
||||
const handleOnDrop = useGroupIssuesDragNDrop(storeType, orderBy, group_by, sub_group_by);
|
||||
|
||||
|
|
|
|||
|
|
@ -5,9 +5,9 @@ import { useParams } from "next/navigation";
|
|||
import { CycleIssueQuickActions } from "@/components/issues";
|
||||
// constants
|
||||
import { EIssuesStoreType } from "@/constants/issue";
|
||||
import { EUserProjectRoles } from "@/constants/project";
|
||||
// hooks
|
||||
import { useCycle, useIssues, useUser } from "@/hooks/store";
|
||||
import { useCycle, useIssues, useUserPermissions } from "@/hooks/store";
|
||||
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||
// components
|
||||
import { BaseKanBanRoot } from "../base-kanban-root";
|
||||
|
||||
|
|
@ -19,13 +19,14 @@ export const CycleKanBanLayout: React.FC = observer(() => {
|
|||
// store
|
||||
const { issues } = useIssues(EIssuesStoreType.CYCLE);
|
||||
const { currentProjectCompletedCycleIds } = useCycle();
|
||||
const {
|
||||
membership: { currentProjectRole },
|
||||
} = useUser();
|
||||
const { allowPermissions } = useUserPermissions();
|
||||
|
||||
const isCompletedCycle =
|
||||
cycleId && currentProjectCompletedCycleIds ? currentProjectCompletedCycleIds.includes(cycleId.toString()) : false;
|
||||
const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER;
|
||||
const isEditingAllowed = allowPermissions(
|
||||
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||
EUserPermissionsLevel.PROJECT
|
||||
);
|
||||
|
||||
const canEditIssueProperties = useCallback(
|
||||
() => !isCompletedCycle && isEditingAllowed,
|
||||
|
|
|
|||
|
|
@ -2,8 +2,9 @@ import { observer } from "mobx-react";
|
|||
// hooks
|
||||
import { useParams } from "next/navigation";
|
||||
import { ProjectIssueQuickActions } from "@/components/issues";
|
||||
import { EUserProjectRoles } from "@/constants/project";
|
||||
import { useUser } from "@/hooks/store";
|
||||
import { useUserPermissions } from "@/hooks/store";
|
||||
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||
|
||||
// components
|
||||
// types
|
||||
// constants
|
||||
|
|
@ -11,16 +12,16 @@ import { BaseKanBanRoot } from "../base-kanban-root";
|
|||
|
||||
export const ProfileIssuesKanBanLayout: React.FC = observer(() => {
|
||||
// router
|
||||
const { profileViewId } = useParams();
|
||||
const {
|
||||
membership: { currentWorkspaceAllProjectsRole },
|
||||
} = useUser();
|
||||
const { workspaceSlug, profileViewId } = useParams();
|
||||
const { allowPermissions } = useUserPermissions();
|
||||
|
||||
const canEditPropertiesBasedOnProject = (projectId: string) => {
|
||||
const currentProjectRole = currentWorkspaceAllProjectsRole && currentWorkspaceAllProjectsRole[projectId];
|
||||
|
||||
return !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER;
|
||||
};
|
||||
const canEditPropertiesBasedOnProject = (projectId: string) =>
|
||||
allowPermissions(
|
||||
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||
EUserPermissionsLevel.PROJECT,
|
||||
workspaceSlug.toString(),
|
||||
projectId
|
||||
);
|
||||
|
||||
return (
|
||||
<BaseKanBanRoot
|
||||
|
|
|
|||
|
|
@ -4,13 +4,13 @@ import { observer } from "mobx-react";
|
|||
import { GroupByColumnTypes, TGroupedIssues } from "@plane/types";
|
||||
// constants
|
||||
import { EIssueLayoutTypes, EIssuesStoreType } from "@/constants/issue";
|
||||
import { EUserProjectRoles } from "@/constants/project";
|
||||
// hooks
|
||||
import { useIssues, useUser } from "@/hooks/store";
|
||||
import { useIssues, useUserPermissions } from "@/hooks/store";
|
||||
// hooks
|
||||
import { useGroupIssuesDragNDrop } from "@/hooks/use-group-dragndrop";
|
||||
import { useIssueStoreType } from "@/hooks/use-issue-layout-store";
|
||||
import { useIssuesActions } from "@/hooks/use-issues-actions";
|
||||
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||
// components
|
||||
import { IssueLayoutHOC } from "../issue-layout-HOC";
|
||||
import { List } from "./default";
|
||||
|
|
@ -49,9 +49,7 @@ export const BaseListRoot = observer((props: IBaseListRoot) => {
|
|||
restoreIssue,
|
||||
} = useIssuesActions(storeType);
|
||||
// mobx store
|
||||
const {
|
||||
membership: { currentProjectRole },
|
||||
} = useUser();
|
||||
const { allowPermissions } = useUserPermissions();
|
||||
const { issueMap } = useIssues();
|
||||
|
||||
const displayFilters = issuesFilter?.issueFilters?.displayFilters;
|
||||
|
|
@ -67,7 +65,10 @@ export const BaseListRoot = observer((props: IBaseListRoot) => {
|
|||
|
||||
const groupedIssueIds = issues?.groupedIssueIds as TGroupedIssues | undefined;
|
||||
// auth
|
||||
const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER;
|
||||
const isEditingAllowed = allowPermissions(
|
||||
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||
EUserPermissionsLevel.PROJECT
|
||||
);
|
||||
const { enableInlineEditing, enableQuickAdd, enableIssueCreation } = issues?.viewFlags || {};
|
||||
|
||||
const canEditProperties = useCallback(
|
||||
|
|
|
|||
|
|
@ -5,9 +5,9 @@ import { useParams } from "next/navigation";
|
|||
import { CycleIssueQuickActions } from "@/components/issues";
|
||||
// constants
|
||||
import { EIssuesStoreType } from "@/constants/issue";
|
||||
import { EUserProjectRoles } from "@/constants/project";
|
||||
// hooks
|
||||
import { useCycle, useIssues, useUser } from "@/hooks/store";
|
||||
import { useCycle, useIssues, useUserPermissions } from "@/hooks/store";
|
||||
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||
// types
|
||||
import { BaseListRoot } from "../base-list-root";
|
||||
|
||||
|
|
@ -18,13 +18,14 @@ export const CycleListLayout: React.FC = observer(() => {
|
|||
// store
|
||||
const { issues } = useIssues(EIssuesStoreType.CYCLE);
|
||||
const { currentProjectCompletedCycleIds } = useCycle(); // mobx store
|
||||
const {
|
||||
membership: { currentProjectRole },
|
||||
} = useUser();
|
||||
const { allowPermissions } = useUserPermissions();
|
||||
|
||||
const isCompletedCycle =
|
||||
cycleId && currentProjectCompletedCycleIds ? currentProjectCompletedCycleIds.includes(cycleId.toString()) : false;
|
||||
const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER;
|
||||
const isEditingAllowed = allowPermissions(
|
||||
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||
EUserPermissionsLevel.PROJECT
|
||||
);
|
||||
|
||||
const canEditIssueProperties = useCallback(
|
||||
() => !isCompletedCycle && isEditingAllowed,
|
||||
|
|
|
|||
|
|
@ -3,8 +3,9 @@ import { observer } from "mobx-react";
|
|||
// hooks
|
||||
import { useParams } from "next/navigation";
|
||||
import { ProjectIssueQuickActions } from "@/components/issues";
|
||||
import { EUserProjectRoles } from "@/constants/project";
|
||||
import { useUser } from "@/hooks/store";
|
||||
import { useUserPermissions } from "@/hooks/store";
|
||||
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||
|
||||
// components
|
||||
// types
|
||||
// constants
|
||||
|
|
@ -12,17 +13,17 @@ import { BaseListRoot } from "../base-list-root";
|
|||
|
||||
export const ProfileIssuesListLayout: FC = observer(() => {
|
||||
// router
|
||||
const { profileViewId } = useParams();
|
||||
const { workspaceSlug, profileViewId } = useParams();
|
||||
// store
|
||||
const {
|
||||
membership: { currentWorkspaceAllProjectsRole },
|
||||
} = useUser();
|
||||
const { allowPermissions } = useUserPermissions();
|
||||
|
||||
const canEditPropertiesBasedOnProject = (projectId: string) => {
|
||||
const currentProjectRole = currentWorkspaceAllProjectsRole && currentWorkspaceAllProjectsRole[projectId];
|
||||
|
||||
return !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER;
|
||||
};
|
||||
const canEditPropertiesBasedOnProject = (projectId: string) =>
|
||||
allowPermissions(
|
||||
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||
EUserPermissionsLevel.PROJECT,
|
||||
workspaceSlug.toString(),
|
||||
projectId
|
||||
);
|
||||
|
||||
return (
|
||||
<BaseListRoot
|
||||
|
|
|
|||
|
|
@ -11,12 +11,12 @@ import { ContextMenu, CustomMenu, TContextMenuItem, TOAST_TYPE, setToast } from
|
|||
import { DeleteIssueModal } from "@/components/issues";
|
||||
// constants
|
||||
import { EIssuesStoreType } from "@/constants/issue";
|
||||
import { EUserProjectRoles } from "@/constants/project";
|
||||
// helpers
|
||||
import { cn } from "@/helpers/common.helper";
|
||||
import { copyUrlToClipboard } from "@/helpers/string.helper";
|
||||
// hooks
|
||||
import { useEventTracker, useIssues, useUser } from "@/hooks/store";
|
||||
import { useEventTracker, useIssues, useUserPermissions } from "@/hooks/store";
|
||||
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||
// types
|
||||
import { IQuickActionProps } from "../list/list-view-types";
|
||||
|
||||
|
|
@ -36,15 +36,15 @@ export const ArchivedIssueQuickActions: React.FC<IQuickActionProps> = observer((
|
|||
// router
|
||||
const { workspaceSlug } = useParams();
|
||||
// store hooks
|
||||
const {
|
||||
membership: { currentProjectRole },
|
||||
} = useUser();
|
||||
const { allowPermissions } = useUserPermissions();
|
||||
|
||||
const { setTrackElement } = useEventTracker();
|
||||
const { issuesFilter } = useIssues(EIssuesStoreType.ARCHIVED);
|
||||
// derived values
|
||||
const activeLayout = `${issuesFilter.issueFilters?.displayFilters?.layout} layout`;
|
||||
// auth
|
||||
const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER && !readOnly;
|
||||
const isEditingAllowed =
|
||||
allowPermissions([EUserPermissions.ADMIN, EUserPermissions.MEMBER], EUserPermissionsLevel.PROJECT) && !readOnly;
|
||||
const isRestoringAllowed = handleRestore && isEditingAllowed;
|
||||
|
||||
const issueLink = `${workspaceSlug}/projects/${issue.project_id}/archives/issues/${issue.id}`;
|
||||
|
|
|
|||
|
|
@ -13,13 +13,13 @@ import { ArchiveIcon, ContextMenu, CustomMenu, TContextMenuItem, TOAST_TYPE, set
|
|||
import { ArchiveIssueModal, CreateUpdateIssueModal, DeleteIssueModal } from "@/components/issues";
|
||||
// constants
|
||||
import { EIssuesStoreType } from "@/constants/issue";
|
||||
import { EUserProjectRoles } from "@/constants/project";
|
||||
import { ARCHIVABLE_STATE_GROUPS } from "@/constants/state";
|
||||
// helpers
|
||||
import { cn } from "@/helpers/common.helper";
|
||||
import { copyUrlToClipboard } from "@/helpers/string.helper";
|
||||
// hooks
|
||||
import { useEventTracker, useIssues, useProjectState, useUser } from "@/hooks/store";
|
||||
import { useEventTracker, useIssues, useProjectState, useUserPermissions } from "@/hooks/store";
|
||||
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||
// types
|
||||
import { IQuickActionProps } from "../list/list-view-types";
|
||||
|
||||
|
|
@ -46,14 +46,13 @@ export const CycleIssueQuickActions: React.FC<IQuickActionProps> = observer((pro
|
|||
// store hooks
|
||||
const { setTrackElement } = useEventTracker();
|
||||
const { issuesFilter } = useIssues(EIssuesStoreType.CYCLE);
|
||||
const {
|
||||
membership: { currentProjectRole },
|
||||
} = useUser();
|
||||
const { allowPermissions } = useUserPermissions();
|
||||
const { getStateById } = useProjectState();
|
||||
// derived values
|
||||
const stateDetails = getStateById(issue.state_id);
|
||||
// auth
|
||||
const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER && !readOnly;
|
||||
const isEditingAllowed =
|
||||
allowPermissions([EUserPermissions.ADMIN, EUserPermissions.MEMBER], EUserPermissionsLevel.PROJECT) && !readOnly;
|
||||
const isArchivingAllowed = handleArchive && isEditingAllowed;
|
||||
const isInArchivableGroup = !!stateDetails && ARCHIVABLE_STATE_GROUPS.includes(stateDetails?.group);
|
||||
const isDeletingAllowed = isEditingAllowed;
|
||||
|
|
|
|||
|
|
@ -13,11 +13,11 @@ import { ContextMenu, CustomMenu, TContextMenuItem } from "@plane/ui";
|
|||
import { CreateUpdateIssueModal, DeleteIssueModal } from "@/components/issues";
|
||||
// constant
|
||||
import { EIssuesStoreType } from "@/constants/issue";
|
||||
import { EUserProjectRoles } from "@/constants/project";
|
||||
// helpers
|
||||
import { cn } from "@/helpers/common.helper";
|
||||
// hooks
|
||||
import { useEventTracker, useIssues, useUser } from "@/hooks/store";
|
||||
import { useEventTracker, useIssues, useUserPermissions } from "@/hooks/store";
|
||||
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||
// types
|
||||
import { IQuickActionProps } from "../list/list-view-types";
|
||||
|
||||
|
|
@ -37,15 +37,14 @@ export const DraftIssueQuickActions: React.FC<IQuickActionProps> = observer((pro
|
|||
const [issueToEdit, setIssueToEdit] = useState<TIssue | undefined>(undefined);
|
||||
const [deleteIssueModal, setDeleteIssueModal] = useState(false);
|
||||
// store hooks
|
||||
const {
|
||||
membership: { currentProjectRole },
|
||||
} = useUser();
|
||||
const { allowPermissions } = useUserPermissions();
|
||||
const { setTrackElement } = useEventTracker();
|
||||
const { issuesFilter } = useIssues(EIssuesStoreType.PROJECT);
|
||||
// derived values
|
||||
const activeLayout = `${issuesFilter.issueFilters?.displayFilters?.layout} layout`;
|
||||
// auth
|
||||
const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER && !readOnly;
|
||||
const isEditingAllowed =
|
||||
allowPermissions([EUserPermissions.ADMIN, EUserPermissions.MEMBER], EUserPermissionsLevel.PROJECT) && !readOnly;
|
||||
const isDeletingAllowed = isEditingAllowed;
|
||||
|
||||
const duplicateIssuePayload = omit(
|
||||
|
|
|
|||
|
|
@ -13,13 +13,13 @@ import { ArchiveIcon, ContextMenu, CustomMenu, TContextMenuItem, TOAST_TYPE, set
|
|||
import { ArchiveIssueModal, CreateUpdateIssueModal, DeleteIssueModal } from "@/components/issues";
|
||||
// constants
|
||||
import { EIssuesStoreType } from "@/constants/issue";
|
||||
import { EUserProjectRoles } from "@/constants/project";
|
||||
import { ARCHIVABLE_STATE_GROUPS } from "@/constants/state";
|
||||
// helpers
|
||||
import { cn } from "@/helpers/common.helper";
|
||||
import { copyUrlToClipboard } from "@/helpers/string.helper";
|
||||
// hooks
|
||||
import { useIssues, useEventTracker, useUser, useProjectState } from "@/hooks/store";
|
||||
import { useIssues, useEventTracker, useProjectState, useUserPermissions } from "@/hooks/store";
|
||||
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||
// types
|
||||
import { IQuickActionProps } from "../list/list-view-types";
|
||||
|
||||
|
|
@ -46,14 +46,13 @@ export const ModuleIssueQuickActions: React.FC<IQuickActionProps> = observer((pr
|
|||
// store hooks
|
||||
const { setTrackElement } = useEventTracker();
|
||||
const { issuesFilter } = useIssues(EIssuesStoreType.MODULE);
|
||||
const {
|
||||
membership: { currentProjectRole },
|
||||
} = useUser();
|
||||
const { allowPermissions } = useUserPermissions();
|
||||
const { getStateById } = useProjectState();
|
||||
// derived values
|
||||
const stateDetails = getStateById(issue.state_id);
|
||||
// auth
|
||||
const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER && !readOnly;
|
||||
const isEditingAllowed =
|
||||
allowPermissions([EUserPermissions.ADMIN, EUserPermissions.MEMBER], EUserPermissionsLevel.PROJECT) && !readOnly;
|
||||
const isArchivingAllowed = handleArchive && isEditingAllowed;
|
||||
const isInArchivableGroup = !!stateDetails && ARCHIVABLE_STATE_GROUPS.includes(stateDetails?.group);
|
||||
const isDeletingAllowed = isEditingAllowed;
|
||||
|
|
|
|||
|
|
@ -13,13 +13,13 @@ import { ArchiveIcon, ContextMenu, CustomMenu, TContextMenuItem, TOAST_TYPE, set
|
|||
import { ArchiveIssueModal, CreateUpdateIssueModal, DeleteIssueModal } from "@/components/issues";
|
||||
// constants
|
||||
import { EIssuesStoreType } from "@/constants/issue";
|
||||
import { EUserProjectRoles } from "@/constants/project";
|
||||
import { ARCHIVABLE_STATE_GROUPS } from "@/constants/state";
|
||||
// helpers
|
||||
import { cn } from "@/helpers/common.helper";
|
||||
import { copyUrlToClipboard } from "@/helpers/string.helper";
|
||||
// hooks
|
||||
import { useEventTracker, useIssues, useProjectState, useUser } from "@/hooks/store";
|
||||
import { useEventTracker, useIssues, useProjectState, useUserPermissions } from "@/hooks/store";
|
||||
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||
// types
|
||||
import { IQuickActionProps } from "../list/list-view-types";
|
||||
|
||||
|
|
@ -44,9 +44,7 @@ export const ProjectIssueQuickActions: React.FC<IQuickActionProps> = observer((p
|
|||
const [deleteIssueModal, setDeleteIssueModal] = useState(false);
|
||||
const [archiveIssueModal, setArchiveIssueModal] = useState(false);
|
||||
// store hooks
|
||||
const {
|
||||
membership: { currentProjectRole },
|
||||
} = useUser();
|
||||
const { allowPermissions } = useUserPermissions();
|
||||
const { setTrackElement } = useEventTracker();
|
||||
const { issuesFilter } = useIssues(EIssuesStoreType.PROJECT);
|
||||
const { getStateById } = useProjectState();
|
||||
|
|
@ -54,7 +52,8 @@ export const ProjectIssueQuickActions: React.FC<IQuickActionProps> = observer((p
|
|||
const activeLayout = `${issuesFilter.issueFilters?.displayFilters?.layout} layout`;
|
||||
const stateDetails = getStateById(issue.state_id);
|
||||
// auth
|
||||
const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER && !readOnly;
|
||||
const isEditingAllowed =
|
||||
allowPermissions([EUserPermissions.ADMIN, EUserPermissions.MEMBER], EUserPermissionsLevel.PROJECT) && !readOnly;
|
||||
const isArchivingAllowed = handleArchive && isEditingAllowed;
|
||||
const isInArchivableGroup = !!stateDetails && ARCHIVABLE_STATE_GROUPS.includes(stateDetails?.group);
|
||||
const isDeletingAllowed = isEditingAllowed;
|
||||
|
|
|
|||
|
|
@ -19,13 +19,13 @@ import {
|
|||
EIssuesStoreType,
|
||||
ISSUE_DISPLAY_FILTERS_BY_LAYOUT,
|
||||
} from "@/constants/issue";
|
||||
import { EUserProjectRoles } from "@/constants/project";
|
||||
// hooks
|
||||
import { useGlobalView, useIssues, useUser } from "@/hooks/store";
|
||||
import { useGlobalView, useIssues, useUserPermissions } from "@/hooks/store";
|
||||
import { useAppRouter } from "@/hooks/use-app-router";
|
||||
import { IssuesStoreContext } from "@/hooks/use-issue-layout-store";
|
||||
import { useIssuesActions } from "@/hooks/use-issues-actions";
|
||||
import { useWorkspaceIssueProperties } from "@/hooks/use-workspace-issue-properties";
|
||||
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||
// store
|
||||
import emptyView from "@/public/empty-state/view.svg";
|
||||
import { IssuePeekOverview } from "../../peek-overview";
|
||||
|
|
@ -57,9 +57,8 @@ export const AllIssueLayoutRoot: React.FC<Props> = observer((props: Props) => {
|
|||
} = useIssues(EIssuesStoreType.GLOBAL);
|
||||
const { updateIssue, removeIssue, archiveIssue } = useIssuesActions(EIssuesStoreType.GLOBAL);
|
||||
|
||||
const {
|
||||
membership: { currentWorkspaceAllProjectsRole },
|
||||
} = useUser();
|
||||
const { allowPermissions } = useUserPermissions();
|
||||
|
||||
const { fetchAllGlobalViews, getViewDetailsById } = useGlobalView();
|
||||
|
||||
const viewDetails = getViewDetailsById(globalViewId?.toString());
|
||||
|
|
@ -132,12 +131,14 @@ export const AllIssueLayoutRoot: React.FC<Props> = observer((props: Props) => {
|
|||
const canEditProperties = useCallback(
|
||||
(projectId: string | undefined) => {
|
||||
if (!projectId) return false;
|
||||
|
||||
const currentProjectRole = currentWorkspaceAllProjectsRole && currentWorkspaceAllProjectsRole[projectId];
|
||||
|
||||
return !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER;
|
||||
return allowPermissions(
|
||||
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||
EUserPermissionsLevel.PROJECT,
|
||||
workspaceSlug.toString(),
|
||||
projectId
|
||||
);
|
||||
},
|
||||
[currentWorkspaceAllProjectsRole]
|
||||
[workspaceSlug]
|
||||
);
|
||||
|
||||
const issueFilters = globalViewId ? filters?.[globalViewId.toString()] : undefined;
|
||||
|
|
|
|||
|
|
@ -6,11 +6,11 @@ import { ALL_ISSUES } from "@plane/constants";
|
|||
import { IIssueDisplayFilterOptions } from "@plane/types";
|
||||
// hooks
|
||||
import { EIssueFilterType, EIssueLayoutTypes, EIssuesStoreType } from "@/constants/issue";
|
||||
import { EUserProjectRoles } from "@/constants/project";
|
||||
// hooks
|
||||
import { useIssues, useUser } from "@/hooks/store";
|
||||
import { useIssues, useUserPermissions } from "@/hooks/store";
|
||||
import { useIssueStoreType } from "@/hooks/use-issue-layout-store";
|
||||
import { useIssuesActions } from "@/hooks/use-issues-actions";
|
||||
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||
// views
|
||||
// stores
|
||||
// components
|
||||
|
|
@ -38,9 +38,7 @@ export const BaseSpreadsheetRoot = observer((props: IBaseSpreadsheetRoot) => {
|
|||
const { projectId } = useParams();
|
||||
// store hooks
|
||||
const storeType = useIssueStoreType() as SpreadsheetStoreType;
|
||||
const {
|
||||
membership: { currentProjectRole },
|
||||
} = useUser();
|
||||
const { allowPermissions } = useUserPermissions();
|
||||
const { issues, issuesFilter } = useIssues(storeType);
|
||||
const {
|
||||
fetchIssues,
|
||||
|
|
@ -56,7 +54,10 @@ export const BaseSpreadsheetRoot = observer((props: IBaseSpreadsheetRoot) => {
|
|||
// derived values
|
||||
const { enableInlineEditing, enableQuickAdd, enableIssueCreation } = issues?.viewFlags || {};
|
||||
// user role validation
|
||||
const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER;
|
||||
const isEditingAllowed = allowPermissions(
|
||||
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||
EUserPermissionsLevel.PROJECT
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
fetchIssues("init-loader", { canGroup: false, perPageCount: 100 }, viewId);
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
import React, { useCallback } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { useParams } from "next/navigation";
|
||||
// constants
|
||||
import { EUserProjectRoles } from "@/constants/project";
|
||||
// hooks
|
||||
import { useCycle, useUser } from "@/hooks/store";
|
||||
import { useCycle, useUserPermissions } from "@/hooks/store";
|
||||
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||
// components
|
||||
import { CycleIssueQuickActions } from "../../quick-action-dropdowns";
|
||||
import { BaseSpreadsheetRoot } from "../base-spreadsheet-root";
|
||||
|
|
@ -14,13 +13,14 @@ export const CycleSpreadsheetLayout: React.FC = observer(() => {
|
|||
const { cycleId } = useParams();
|
||||
// store hooks
|
||||
const { currentProjectCompletedCycleIds } = useCycle();
|
||||
const {
|
||||
membership: { currentProjectRole },
|
||||
} = useUser();
|
||||
const { allowPermissions } = useUserPermissions();
|
||||
// auth
|
||||
const isCompletedCycle =
|
||||
cycleId && currentProjectCompletedCycleIds ? currentProjectCompletedCycleIds.includes(cycleId.toString()) : false;
|
||||
const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER;
|
||||
const isEditingAllowed = allowPermissions(
|
||||
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||
EUserPermissionsLevel.PROJECT
|
||||
);
|
||||
|
||||
const canEditIssueProperties = useCallback(
|
||||
() => !isCompletedCycle && isEditingAllowed,
|
||||
|
|
|
|||
|
|
@ -10,10 +10,10 @@ import { IssueView, TIssueOperations } from "@/components/issues";
|
|||
// constants
|
||||
import { ISSUE_UPDATED, ISSUE_DELETED, ISSUE_ARCHIVED, ISSUE_RESTORED } from "@/constants/event-tracker";
|
||||
import { EIssuesStoreType } from "@/constants/issue";
|
||||
import { EUserProjectRoles } from "@/constants/project";
|
||||
// hooks
|
||||
import { useEventTracker, useIssueDetail, useIssues, useUser } from "@/hooks/store";
|
||||
import { useEventTracker, useIssueDetail, useIssues, useUserPermissions } from "@/hooks/store";
|
||||
import { useIssuesStore } from "@/hooks/use-issue-layout-store";
|
||||
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||
|
||||
interface IIssuePeekOverview {
|
||||
embedIssue?: boolean;
|
||||
|
|
@ -26,9 +26,9 @@ export const IssuePeekOverview: FC<IIssuePeekOverview> = observer((props) => {
|
|||
const { embedIssue = false, embedRemoveCurrentNotification, is_archived = false, is_draft = false } = props;
|
||||
// router
|
||||
const pathname = usePathname();
|
||||
const {
|
||||
membership: { currentWorkspaceAllProjectsRole },
|
||||
} = useUser();
|
||||
// store hook
|
||||
const { allowPermissions } = useUserPermissions();
|
||||
|
||||
const {
|
||||
issues: { restoreIssue },
|
||||
} = useIssues(EIssuesStoreType.ARCHIVED);
|
||||
|
|
@ -335,9 +335,13 @@ export const IssuePeekOverview: FC<IIssuePeekOverview> = observer((props) => {
|
|||
|
||||
if (!peekIssue?.workspaceSlug || !peekIssue?.projectId || !peekIssue?.issueId) return <></>;
|
||||
|
||||
const currentProjectRole = currentWorkspaceAllProjectsRole?.[peekIssue?.projectId];
|
||||
// Check if issue is editable, based on user role
|
||||
const isEditable = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER;
|
||||
const isEditable = allowPermissions(
|
||||
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||
EUserPermissionsLevel.PROJECT,
|
||||
peekIssue?.workspaceSlug,
|
||||
peekIssue?.projectId
|
||||
);
|
||||
|
||||
return (
|
||||
<IssueView
|
||||
|
|
|
|||
|
|
@ -47,15 +47,15 @@ import {
|
|||
MODULE_UPDATED,
|
||||
} from "@/constants/event-tracker";
|
||||
import { MODULE_STATUS } from "@/constants/module";
|
||||
import { EUserProjectRoles } from "@/constants/project";
|
||||
// helpers
|
||||
import { getDate, renderFormattedPayloadDate } from "@/helpers/date-time.helper";
|
||||
import { copyUrlToClipboard } from "@/helpers/string.helper";
|
||||
// hooks
|
||||
import { useModule, useUser, useEventTracker, useProjectEstimates } from "@/hooks/store";
|
||||
import { useModule, useEventTracker, useProjectEstimates, useUserPermissions } from "@/hooks/store";
|
||||
import { useAppRouter } from "@/hooks/use-app-router";
|
||||
// plane web constants
|
||||
import { EEstimateSystem } from "@/plane-web/constants/estimates";
|
||||
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||
|
||||
const defaultValues: Partial<IModule> = {
|
||||
lead_id: "",
|
||||
|
|
@ -84,9 +84,9 @@ export const ModuleAnalyticsSidebar: React.FC<Props> = observer((props) => {
|
|||
const { workspaceSlug, projectId } = useParams();
|
||||
|
||||
// store hooks
|
||||
const {
|
||||
membership: { currentProjectRole },
|
||||
} = useUser();
|
||||
|
||||
const { allowPermissions } = useUserPermissions();
|
||||
|
||||
const { getModuleById, updateModuleDetails, createModuleLink, updateModuleLink, deleteModuleLink, restoreModule } =
|
||||
useModule();
|
||||
const { setTrackElement, captureModuleEvent, captureEvent } = useEventTracker();
|
||||
|
|
@ -264,7 +264,10 @@ export const ModuleAnalyticsSidebar: React.FC<Props> = observer((props) => {
|
|||
? "0 Issue"
|
||||
: `${moduleDetails.completed_estimate_points}/${moduleDetails.total_estimate_points}`;
|
||||
|
||||
const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER;
|
||||
const isEditingAllowed = allowPermissions(
|
||||
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||
EUserPermissionsLevel.PROJECT
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="relative">
|
||||
|
|
@ -550,7 +553,7 @@ export const ModuleAnalyticsSidebar: React.FC<Props> = observer((props) => {
|
|||
<Transition show={open}>
|
||||
<Disclosure.Panel>
|
||||
<div className="mt-2 flex min-h-72 w-full flex-col space-y-3 overflow-y-auto">
|
||||
{currentProjectRole && moduleDetails.link_module && moduleDetails.link_module.length > 0 ? (
|
||||
{isEditingAllowed && moduleDetails.link_module && moduleDetails.link_module.length > 0 ? (
|
||||
<>
|
||||
{isEditingAllowed && !isArchived && (
|
||||
<div className="flex w-full items-center justify-end">
|
||||
|
|
@ -569,13 +572,7 @@ export const ModuleAnalyticsSidebar: React.FC<Props> = observer((props) => {
|
|||
moduleId={moduleId}
|
||||
handleEditLink={handleEditLink}
|
||||
handleDeleteLink={handleDeleteLink}
|
||||
userAuth={{
|
||||
isGuest: currentProjectRole === EUserProjectRoles.GUEST,
|
||||
isViewer: currentProjectRole === EUserProjectRoles.VIEWER,
|
||||
isMember: currentProjectRole === EUserProjectRoles.MEMBER,
|
||||
isOwner: currentProjectRole === EUserProjectRoles.ADMIN,
|
||||
}}
|
||||
disabled={isArchived}
|
||||
disabled={!isEditingAllowed || isArchived}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
import { useCallback } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
// plane types
|
||||
import { ILinkDetails, UserAuth } from "@plane/types";
|
||||
import { ILinkDetails } from "@plane/types";
|
||||
// components
|
||||
import { ModulesLinksListItem } from "@/components/modules";
|
||||
// hooks
|
||||
|
|
@ -13,11 +13,10 @@ type Props = {
|
|||
handleDeleteLink: (linkId: string) => void;
|
||||
handleEditLink: (link: ILinkDetails) => void;
|
||||
moduleId: string;
|
||||
userAuth: UserAuth;
|
||||
};
|
||||
|
||||
export const ModuleLinksList: React.FC<Props> = observer((props) => {
|
||||
const { moduleId, handleDeleteLink, handleEditLink, userAuth, disabled } = props;
|
||||
const { moduleId, handleDeleteLink, handleEditLink, disabled } = props;
|
||||
// store hooks
|
||||
const { getModuleById } = useModule();
|
||||
// derived values
|
||||
|
|
@ -36,7 +35,7 @@ export const ModuleLinksList: React.FC<Props> = observer((props) => {
|
|||
key={link.id}
|
||||
handleDeleteLink={() => memoizedDeleteLink(link.id)}
|
||||
handleEditLink={() => memoizedEditLink(link)}
|
||||
isEditingAllowed={(userAuth.isMember || userAuth.isOwner) && !disabled}
|
||||
isEditingAllowed={!disabled}
|
||||
link={link}
|
||||
/>
|
||||
))}
|
||||
|
|
|
|||
|
|
@ -17,16 +17,16 @@ import { ModuleStatusDropdown } from "@/components/modules/module-status-dropdo
|
|||
import { PROGRESS_STATE_GROUPS_DETAILS } from "@/constants/common";
|
||||
import { MODULE_FAVORITED, MODULE_UNFAVORITED } from "@/constants/event-tracker";
|
||||
import { MODULE_STATUS } from "@/constants/module";
|
||||
import { EUserProjectRoles } from "@/constants/project";
|
||||
// helpers
|
||||
import { getDate, renderFormattedPayloadDate } from "@/helpers/date-time.helper";
|
||||
import { generateQueryParams } from "@/helpers/router.helper";
|
||||
// hooks
|
||||
import { useEventTracker, useMember, useModule, useProjectEstimates, useUser } from "@/hooks/store";
|
||||
import { useEventTracker, useMember, useModule, useProjectEstimates, useUserPermissions } from "@/hooks/store";
|
||||
import { useAppRouter } from "@/hooks/use-app-router";
|
||||
import { usePlatformOS } from "@/hooks/use-platform-os";
|
||||
// plane web constants
|
||||
import { EEstimateSystem } from "@/plane-web/constants/estimates";
|
||||
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||
|
||||
type Props = {
|
||||
moduleId: string;
|
||||
|
|
@ -42,9 +42,7 @@ export const ModuleCardItem: React.FC<Props> = observer((props) => {
|
|||
const searchParams = useSearchParams();
|
||||
const pathname = usePathname();
|
||||
// store hooks
|
||||
const {
|
||||
membership: { currentProjectRole },
|
||||
} = useUser();
|
||||
const { allowPermissions } = useUserPermissions();
|
||||
const { getModuleById, addModuleToFavorites, removeModuleFromFavorites, updateModuleDetails } = useModule();
|
||||
const { getUserDetails } = useMember();
|
||||
const { captureEvent } = useEventTracker();
|
||||
|
|
@ -52,7 +50,10 @@ export const ModuleCardItem: React.FC<Props> = observer((props) => {
|
|||
|
||||
// derived values
|
||||
const moduleDetails = getModuleById(moduleId);
|
||||
const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER;
|
||||
const isEditingAllowed = allowPermissions(
|
||||
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||
EUserPermissionsLevel.PROJECT
|
||||
);
|
||||
const isDisabled = !isEditingAllowed || !!moduleDetails?.archived_at;
|
||||
const renderIcon = Boolean(moduleDetails?.start_date) || Boolean(moduleDetails?.target_date);
|
||||
|
||||
|
|
|
|||
|
|
@ -12,14 +12,14 @@ import { FavoriteStar, TOAST_TYPE, Tooltip, setPromiseToast, setToast } from "@p
|
|||
// components
|
||||
import { DateRangeDropdown } from "@/components/dropdowns";
|
||||
import { ModuleQuickActions } from "@/components/modules";
|
||||
import { ModuleStatusDropdown } from "@/components/modules/module-status-dropdown";
|
||||
import { ModuleStatusDropdown } from "@/components/modules/module-status-dropdown";
|
||||
// constants
|
||||
import { MODULE_FAVORITED, MODULE_UNFAVORITED } from "@/constants/event-tracker";
|
||||
import { MODULE_STATUS } from "@/constants/module";
|
||||
import { EUserProjectRoles } from "@/constants/project";
|
||||
// hooks
|
||||
import { renderFormattedPayloadDate, getDate } from "@/helpers/date-time.helper";
|
||||
import { useEventTracker, useMember, useModule, useUser } from "@/hooks/store";
|
||||
import { useEventTracker, useMember, useModule, useUserPermissions } from "@/hooks/store";
|
||||
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||
import { ButtonAvatars } from "../dropdowns/member/avatar";
|
||||
|
||||
type Props = {
|
||||
|
|
@ -33,9 +33,7 @@ export const ModuleListItemAction: FC<Props> = observer((props) => {
|
|||
// router
|
||||
const { workspaceSlug, projectId } = useParams();
|
||||
// store hooks
|
||||
const {
|
||||
membership: { currentProjectRole },
|
||||
} = useUser();
|
||||
const { allowPermissions } = useUserPermissions();
|
||||
const { addModuleToFavorites, removeModuleFromFavorites, updateModuleDetails } = useModule();
|
||||
const { getUserDetails } = useMember();
|
||||
const { captureEvent } = useEventTracker();
|
||||
|
|
@ -43,7 +41,10 @@ export const ModuleListItemAction: FC<Props> = observer((props) => {
|
|||
// derived values
|
||||
|
||||
const moduleStatus = MODULE_STATUS.find((status) => status.value === moduleDetails.status);
|
||||
const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER;
|
||||
const isEditingAllowed = allowPermissions(
|
||||
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||
EUserPermissionsLevel.PROJECT
|
||||
);
|
||||
const isDisabled = !isEditingAllowed || !!moduleDetails?.archived_at;
|
||||
const renderIcon = Boolean(moduleDetails.start_date) || Boolean(moduleDetails.target_date);
|
||||
|
||||
|
|
@ -140,9 +141,9 @@ export const ModuleListItemAction: FC<Props> = observer((props) => {
|
|||
}}
|
||||
onSelect={(val) => {
|
||||
handleModuleDetailsChange({
|
||||
start_date: (val?.from ? renderFormattedPayloadDate(val.from) : null),
|
||||
target_date: (val?.to ? renderFormattedPayloadDate(val.to) : null)
|
||||
})
|
||||
start_date: val?.from ? renderFormattedPayloadDate(val.from) : null,
|
||||
target_date: val?.to ? renderFormattedPayloadDate(val.to) : null,
|
||||
});
|
||||
}}
|
||||
placeholder={{
|
||||
from: "Start date",
|
||||
|
|
@ -154,9 +155,9 @@ export const ModuleListItemAction: FC<Props> = observer((props) => {
|
|||
|
||||
{moduleStatus && (
|
||||
<ModuleStatusDropdown
|
||||
isDisabled={isDisabled}
|
||||
moduleDetails={moduleDetails}
|
||||
handleModuleDetailsChange={handleModuleDetailsChange}
|
||||
isDisabled={isDisabled}
|
||||
moduleDetails={moduleDetails}
|
||||
handleModuleDetailsChange={handleModuleDetailsChange}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
|
@ -191,4 +192,4 @@ export const ModuleListItemAction: FC<Props> = observer((props) => {
|
|||
)}
|
||||
</>
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -9,14 +9,13 @@ import { ArchiveRestoreIcon, ExternalLink, LinkIcon, Pencil, Trash2 } from "luci
|
|||
import { ArchiveIcon, ContextMenu, CustomMenu, TContextMenuItem, TOAST_TYPE, setToast } from "@plane/ui";
|
||||
// components
|
||||
import { ArchiveModuleModal, CreateUpdateModuleModal, DeleteModuleModal } from "@/components/modules";
|
||||
// constants
|
||||
import { EUserProjectRoles } from "@/constants/project";
|
||||
// helpers
|
||||
import { cn } from "@/helpers/common.helper";
|
||||
import { copyUrlToClipboard } from "@/helpers/string.helper";
|
||||
// hooks
|
||||
import { useModule, useEventTracker, useUser } from "@/hooks/store";
|
||||
import { useModule, useEventTracker, useUserPermissions } from "@/hooks/store";
|
||||
import { useAppRouter } from "@/hooks/use-app-router";
|
||||
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||
|
||||
type Props = {
|
||||
parentRef: React.RefObject<HTMLDivElement>;
|
||||
|
|
@ -35,16 +34,19 @@ export const ModuleQuickActions: React.FC<Props> = observer((props) => {
|
|||
const [deleteModal, setDeleteModal] = useState(false);
|
||||
// store hooks
|
||||
const { setTrackElement } = useEventTracker();
|
||||
const {
|
||||
membership: { currentWorkspaceAllProjectsRole },
|
||||
} = useUser();
|
||||
const { allowPermissions } = useUserPermissions();
|
||||
|
||||
const { getModuleById, restoreModule } = useModule();
|
||||
// derived values
|
||||
const moduleDetails = getModuleById(moduleId);
|
||||
const isArchived = !!moduleDetails?.archived_at;
|
||||
// auth
|
||||
const isEditingAllowed =
|
||||
!!currentWorkspaceAllProjectsRole && currentWorkspaceAllProjectsRole[projectId] >= EUserProjectRoles.MEMBER;
|
||||
const isEditingAllowed = allowPermissions(
|
||||
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||
EUserPermissionsLevel.PROJECT,
|
||||
workspaceSlug,
|
||||
projectId
|
||||
);
|
||||
|
||||
const moduleState = moduleDetails?.status?.toLocaleLowerCase();
|
||||
const isInArchivableGroup = !!moduleState && ["completed", "cancelled"].includes(moduleState);
|
||||
|
|
|
|||
|
|
@ -23,12 +23,14 @@ import { IUser, IWorkspace } from "@plane/types";
|
|||
import { Button, Input, Spinner, TOAST_TYPE, setToast } from "@plane/ui";
|
||||
// constants
|
||||
import { MEMBER_INVITED } from "@/constants/event-tracker";
|
||||
import { EUserWorkspaceRoles, ROLE, ROLE_DETAILS } from "@/constants/workspace";
|
||||
import { ROLE, ROLE_DETAILS } from "@/constants/workspace";
|
||||
// helpers
|
||||
import { getUserRole } from "@/helpers/user.helper";
|
||||
// hooks
|
||||
import { useEventTracker } from "@/hooks/store";
|
||||
import useDynamicDropdownPosition from "@/hooks/use-dynamic-dropdown";
|
||||
// plane web constants
|
||||
import { EUserPermissions } from "@/plane-web/constants/user-permissions";
|
||||
// services
|
||||
import { WorkspaceService } from "@/plane-web/services";
|
||||
// assets
|
||||
|
|
@ -47,7 +49,7 @@ type Props = {
|
|||
|
||||
type EmailRole = {
|
||||
email: string;
|
||||
role: EUserWorkspaceRoles;
|
||||
role: EUserPermissions;
|
||||
role_active: boolean;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@ import {
|
|||
import { Logo } from "@/components/common";
|
||||
import { ArchiveRestoreProjectModal, DeleteProjectModal, JoinProjectModal } from "@/components/project";
|
||||
// constants
|
||||
import { EUserProjectRoles } from "@/constants/project";
|
||||
// helpers
|
||||
import { cn } from "@/helpers/common.helper";
|
||||
import { renderFormattedDate } from "@/helpers/date-time.helper";
|
||||
|
|
@ -33,6 +32,8 @@ import { copyUrlToClipboard } from "@/helpers/string.helper";
|
|||
import { useProject } from "@/hooks/store";
|
||||
import { useAppRouter } from "@/hooks/use-app-router";
|
||||
import { usePlatformOS } from "@/hooks/use-platform-os";
|
||||
// plane-web constants
|
||||
import { EUserPermissions } from "@/plane-web/constants/user-permissions";
|
||||
|
||||
type Props = {
|
||||
project: IProject;
|
||||
|
|
@ -57,8 +58,8 @@ export const ProjectCard: React.FC<Props> = observer((props) => {
|
|||
// derived values
|
||||
const projectMembersIds = project.members?.map((member) => member.member_id);
|
||||
// auth
|
||||
const isOwner = project.member_role === EUserProjectRoles.ADMIN;
|
||||
const isMember = project.member_role === EUserProjectRoles.MEMBER;
|
||||
const isOwner = project.member_role === EUserPermissions.ADMIN;
|
||||
const isMember = project.member_role === EUserPermissions.MEMBER;
|
||||
// archive
|
||||
const isArchived = !!project.archived_at;
|
||||
|
||||
|
|
|
|||
|
|
@ -8,13 +8,12 @@ import { Search, Briefcase, X } from "lucide-react";
|
|||
import { Breadcrumbs, Button, Header } from "@plane/ui";
|
||||
// components
|
||||
import { BreadcrumbLink } from "@/components/common";
|
||||
// constants
|
||||
import { EUserWorkspaceRoles } from "@/constants/workspace";
|
||||
// helpers
|
||||
import { cn } from "@/helpers/common.helper";
|
||||
// hooks
|
||||
import { useCommandPalette, useEventTracker, useProjectFilter, useUser } from "@/hooks/store";
|
||||
import { useCommandPalette, useEventTracker, useProjectFilter, useUserPermissions } from "@/hooks/store";
|
||||
import useOutsideClickDetector from "@/hooks/use-outside-click-detector";
|
||||
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||
import HeaderFilters from "./filters";
|
||||
|
||||
export const ProjectsBaseHeader = observer(() => {
|
||||
|
|
@ -25,9 +24,8 @@ export const ProjectsBaseHeader = observer(() => {
|
|||
// store hooks
|
||||
const { toggleCreateProjectModal } = useCommandPalette();
|
||||
const { setTrackElement } = useEventTracker();
|
||||
const {
|
||||
membership: { currentWorkspaceRole },
|
||||
} = useUser();
|
||||
const { allowPermissions } = useUserPermissions();
|
||||
|
||||
const pathname = usePathname();
|
||||
|
||||
const { searchQuery, updateSearchQuery } = useProjectFilter();
|
||||
|
|
@ -37,7 +35,10 @@ export const ProjectsBaseHeader = observer(() => {
|
|||
if (isSearchOpen && searchQuery.trim() === "") setIsSearchOpen(false);
|
||||
});
|
||||
// auth
|
||||
const isAuthorizedUser = !!currentWorkspaceRole && currentWorkspaceRole >= EUserWorkspaceRoles.MEMBER;
|
||||
const isAuthorizedUser = allowPermissions(
|
||||
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||
EUserPermissionsLevel.WORKSPACE
|
||||
);
|
||||
const isArchived = pathname.includes("/archives");
|
||||
|
||||
const handleInputKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import type { IProject } from "@plane/types";
|
|||
// ui
|
||||
import { Button } from "@plane/ui";
|
||||
// hooks
|
||||
import { useProject, useUser } from "@/hooks/store";
|
||||
import { useProject, useUserPermissions } from "@/hooks/store";
|
||||
import { useAppRouter } from "@/hooks/use-app-router";
|
||||
|
||||
// type
|
||||
|
|
@ -24,9 +24,7 @@ export const JoinProjectModal: React.FC<TJoinProjectModalProps> = (props) => {
|
|||
// states
|
||||
const [isJoiningLoading, setIsJoiningLoading] = useState(false);
|
||||
// store hooks
|
||||
const {
|
||||
membership: { joinProject },
|
||||
} = useUser();
|
||||
const { joinProject } = useUserPermissions();
|
||||
const { fetchProjects } = useProject();
|
||||
// router
|
||||
const router = useAppRouter();
|
||||
|
|
@ -34,7 +32,7 @@ export const JoinProjectModal: React.FC<TJoinProjectModalProps> = (props) => {
|
|||
const handleJoin = () => {
|
||||
setIsJoiningLoading(true);
|
||||
|
||||
joinProject(workspaceSlug, [project.id])
|
||||
joinProject(workspaceSlug, project.id)
|
||||
.then(() => {
|
||||
router.push(`/${workspaceSlug}/projects/${project.id}/issues`);
|
||||
fetchProjects(workspaceSlug);
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import { Button, Input, TOAST_TYPE, setToast } from "@plane/ui";
|
|||
// constants
|
||||
import { PROJECT_MEMBER_LEAVE } from "@/constants/event-tracker";
|
||||
// hooks
|
||||
import { useEventTracker, useUser } from "@/hooks/store";
|
||||
import { useEventTracker, useUserPermissions } from "@/hooks/store";
|
||||
import { useAppRouter } from "@/hooks/use-app-router";
|
||||
|
||||
type FormData = {
|
||||
|
|
@ -40,9 +40,7 @@ export const LeaveProjectModal: FC<ILeaveProjectModal> = observer((props) => {
|
|||
const { workspaceSlug } = useParams();
|
||||
// store hooks
|
||||
const { captureEvent } = useEventTracker();
|
||||
const {
|
||||
membership: { leaveProject },
|
||||
} = useUser();
|
||||
const { leaveProject } = useUserPermissions();
|
||||
|
||||
const {
|
||||
control,
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import { ConfirmProjectMemberRemove } from "@/components/project";
|
|||
import { PROJECT_MEMBER_LEAVE } from "@/constants/event-tracker";
|
||||
|
||||
// hooks
|
||||
import { useEventTracker, useMember, useProject, useUser } from "@/hooks/store";
|
||||
import { useEventTracker, useMember, useProject, useUser, useUserPermissions } from "@/hooks/store";
|
||||
import { useAppRouter } from "@/hooks/use-app-router";
|
||||
import { useProjectColumns } from "@/plane-web/components/projects/settings/useProjectColumns";
|
||||
import { IProjectMemberDetails } from "@/store/member/project-member.store";
|
||||
|
|
@ -25,9 +25,7 @@ export const ProjectMemberListItem: React.FC<Props> = observer((props) => {
|
|||
// router
|
||||
const router = useAppRouter();
|
||||
// store hooks
|
||||
const {
|
||||
membership: { leaveProject },
|
||||
} = useUser();
|
||||
const { leaveProject } = useUserPermissions();
|
||||
const { data: currentUser } = useUser();
|
||||
const { fetchProjects } = useProject();
|
||||
const {
|
||||
|
|
|
|||
|
|
@ -9,8 +9,9 @@ import { Button } from "@plane/ui";
|
|||
import { ProjectMemberListItem, SendProjectInvitationModal } from "@/components/project";
|
||||
// ui
|
||||
import { MembersSettingsLoader } from "@/components/ui";
|
||||
import { EUserProjectRoles } from "@/constants/project";
|
||||
import { useEventTracker, useMember, useUser } from "@/hooks/store";
|
||||
import { useEventTracker, useMember, useUserPermissions } from "@/hooks/store";
|
||||
// plane-web constants
|
||||
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||
|
||||
export const ProjectMemberList: React.FC = observer(() => {
|
||||
// states
|
||||
|
|
@ -21,9 +22,7 @@ export const ProjectMemberList: React.FC = observer(() => {
|
|||
const {
|
||||
project: { projectMemberIds, getProjectMemberDetails },
|
||||
} = useMember();
|
||||
const {
|
||||
membership: { currentProjectRole },
|
||||
} = useUser();
|
||||
const { allowPermissions } = useUserPermissions();
|
||||
const searchedMembers = (projectMemberIds ?? []).filter((userId) => {
|
||||
const memberDetails = getProjectMemberDetails(userId);
|
||||
|
||||
|
|
@ -36,6 +35,8 @@ export const ProjectMemberList: React.FC = observer(() => {
|
|||
});
|
||||
const memberDetails = searchedMembers?.map((memberId) => getProjectMemberDetails(memberId));
|
||||
|
||||
const isAdmin = allowPermissions([EUserPermissions.ADMIN], EUserPermissionsLevel.PROJECT);
|
||||
|
||||
return (
|
||||
<>
|
||||
<SendProjectInvitationModal isOpen={inviteModal} onClose={() => setInviteModal(false)} />
|
||||
|
|
@ -52,7 +53,7 @@ export const ProjectMemberList: React.FC = observer(() => {
|
|||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
{currentProjectRole === EUserProjectRoles.ADMIN && (
|
||||
{isAdmin && (
|
||||
<Button
|
||||
variant="primary"
|
||||
onClick={() => {
|
||||
|
|
|
|||
|
|
@ -7,14 +7,15 @@ import { Controller, useForm } from "react-hook-form";
|
|||
import useSWR from "swr";
|
||||
import { IProject, IUserLite, IWorkspace } from "@plane/types";
|
||||
// ui
|
||||
import { Loader, TOAST_TYPE, setToast } from "@plane/ui";
|
||||
import { Loader, TOAST_TYPE, ToggleSwitch, setToast } from "@plane/ui";
|
||||
// components
|
||||
import { MemberSelect } from "@/components/project";
|
||||
// constants
|
||||
import { PROJECT_MEMBERS } from "@/constants/fetch-keys";
|
||||
import { EUserProjectRoles } from "@/constants/project";
|
||||
// hooks
|
||||
import { useProject, useUser } from "@/hooks/store";
|
||||
import { useProject, useUserPermissions } from "@/hooks/store";
|
||||
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||
|
||||
// types
|
||||
|
||||
const defaultValues: Partial<IProject> = {
|
||||
|
|
@ -26,12 +27,16 @@ export const ProjectSettingsMemberDefaults: React.FC = observer(() => {
|
|||
// router
|
||||
const { workspaceSlug, projectId } = useParams();
|
||||
// store hooks
|
||||
const {
|
||||
membership: { currentProjectRole },
|
||||
} = useUser();
|
||||
const { allowPermissions } = useUserPermissions();
|
||||
|
||||
const { currentProjectDetails, fetchProjectDetails, updateProject } = useProject();
|
||||
// derived values
|
||||
const isAdmin = currentProjectRole === EUserProjectRoles.ADMIN;
|
||||
const isAdmin = allowPermissions(
|
||||
[EUserPermissions.ADMIN],
|
||||
EUserPermissionsLevel.PROJECT,
|
||||
currentProjectDetails?.workspace_detail?.slug,
|
||||
currentProjectDetails?.id
|
||||
);
|
||||
// form info
|
||||
const { reset, control } = useForm<IProject>({ defaultValues });
|
||||
// fetching user members
|
||||
|
|
@ -83,6 +88,24 @@ export const ProjectSettingsMemberDefaults: React.FC = observer(() => {
|
|||
});
|
||||
};
|
||||
|
||||
const toggleGuestViewAllIssues = async (value: boolean) => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
|
||||
updateProject(workspaceSlug.toString(), projectId.toString(), {
|
||||
guest_view_all_features: value,
|
||||
})
|
||||
.then(() => {
|
||||
setToast({
|
||||
title: "Success!",
|
||||
type: TOAST_TYPE.SUCCESS,
|
||||
message: "Project updated successfully",
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex items-center border-b border-custom-border-100 pb-3.5">
|
||||
|
|
@ -142,6 +165,24 @@ export const ProjectSettingsMemberDefaults: React.FC = observer(() => {
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{currentProjectDetails && (
|
||||
<div className="relative pb-4 flex justify-between items-center gap-3">
|
||||
<div className="space-y-1">
|
||||
<h3 className="text-lg font-medium text-custom-text-100">
|
||||
Grant view access to all issues for guest users:
|
||||
</h3>
|
||||
<p className="text-sm text-custom-text-200">
|
||||
This will allow guests to have view access to all the project issues.
|
||||
</p>
|
||||
</div>
|
||||
<ToggleSwitch
|
||||
value={currentProjectDetails?.guest_view_all_features}
|
||||
onChange={() => toggleGuestViewAllIssues(!currentProjectDetails?.guest_view_all_features)}
|
||||
disabled={!isAdmin}
|
||||
size="md"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -6,15 +6,15 @@ import { useParams } from "next/navigation";
|
|||
import { useForm, Controller, useFieldArray } from "react-hook-form";
|
||||
import { ChevronDown, Plus, X } from "lucide-react";
|
||||
import { Dialog, Transition } from "@headlessui/react";
|
||||
// hooks
|
||||
// ui
|
||||
import { Avatar, Button, CustomSelect, CustomSearchSelect, TOAST_TYPE, setToast } from "@plane/ui";
|
||||
// helpers
|
||||
import { PROJECT_MEMBER_ADDED } from "@/constants/event-tracker";
|
||||
import { EUserProjectRoles } from "@/constants/project";
|
||||
import { EUserWorkspaceRoles, ROLE } from "@/constants/workspace";
|
||||
import { useEventTracker, useMember, useUser } from "@/hooks/store";
|
||||
// constants
|
||||
import { ROLE } from "@/constants/workspace";
|
||||
// hooks
|
||||
import { useEventTracker, useMember, useUserPermissions } from "@/hooks/store";
|
||||
// plane-web constants
|
||||
import { EUserPermissions } from "@/plane-web/constants/user-permissions";
|
||||
|
||||
type Props = {
|
||||
isOpen: boolean;
|
||||
|
|
@ -23,7 +23,7 @@ type Props = {
|
|||
};
|
||||
|
||||
type member = {
|
||||
role: EUserProjectRoles;
|
||||
role: EUserPermissions;
|
||||
member_id: string;
|
||||
};
|
||||
|
||||
|
|
@ -46,9 +46,7 @@ export const SendProjectInvitationModal: React.FC<Props> = observer((props) => {
|
|||
const { workspaceSlug, projectId } = useParams();
|
||||
// store hooks
|
||||
const { captureEvent } = useEventTracker();
|
||||
const {
|
||||
membership: { currentProjectRole },
|
||||
} = useUser();
|
||||
const { projectUserInfo } = useUserPermissions();
|
||||
const {
|
||||
project: { projectMemberIds, bulkAddMembersToProject },
|
||||
workspace: { workspaceMemberIds, getWorkspaceMemberDetails },
|
||||
|
|
@ -68,6 +66,8 @@ export const SendProjectInvitationModal: React.FC<Props> = observer((props) => {
|
|||
name: "members",
|
||||
});
|
||||
|
||||
const currentProjectRole = projectUserInfo?.[workspaceSlug?.toString()]?.[projectId?.toString()]?.role;
|
||||
|
||||
const uninvitedPeople = workspaceMemberIds?.filter((userId) => {
|
||||
const isInvited = projectMemberIds?.find((u) => u === userId);
|
||||
|
||||
|
|
@ -173,12 +173,10 @@ export const SendProjectInvitationModal: React.FC<Props> = observer((props) => {
|
|||
const currentMemberWorkspaceRole = getWorkspaceMemberDetails(value)?.role;
|
||||
if (!value || !currentMemberWorkspaceRole) return ROLE;
|
||||
|
||||
const isGuestOrViewer = [EUserWorkspaceRoles.GUEST, EUserWorkspaceRoles.VIEWER].includes(
|
||||
currentMemberWorkspaceRole
|
||||
);
|
||||
const isGuest = [EUserPermissions.GUEST].includes(currentMemberWorkspaceRole);
|
||||
|
||||
return Object.fromEntries(
|
||||
Object.entries(ROLE).filter(([key]) => !isGuestOrViewer || [5, 10].includes(parseInt(key)))
|
||||
Object.entries(ROLE).filter(([key]) => !isGuest || [5].includes(parseInt(key)))
|
||||
);
|
||||
};
|
||||
|
||||
|
|
@ -258,7 +256,7 @@ export const SendProjectInvitationModal: React.FC<Props> = observer((props) => {
|
|||
const newValue = ROLE[workspaceRole].toUpperCase();
|
||||
setValue(
|
||||
`members.${index}.role`,
|
||||
EUserProjectRoles[newValue as keyof typeof EUserProjectRoles]
|
||||
EUserPermissions[newValue as keyof typeof EUserPermissions]
|
||||
);
|
||||
}}
|
||||
options={options}
|
||||
|
|
@ -297,7 +295,7 @@ export const SendProjectInvitationModal: React.FC<Props> = observer((props) => {
|
|||
{Object.entries(
|
||||
checkCurrentOptionWorkspaceRole(watch(`members.${index}.member_id`))
|
||||
).map(([key, label]) => {
|
||||
if (parseInt(key) > (currentProjectRole ?? EUserProjectRoles.GUEST)) return null;
|
||||
if (parseInt(key) > (currentProjectRole ?? EUserPermissions.GUEST)) return null;
|
||||
|
||||
return (
|
||||
<CustomSelect.Option key={key} value={key}>
|
||||
|
|
|
|||
|
|
@ -5,13 +5,13 @@ import { Trash2 } from "lucide-react";
|
|||
import { Disclosure } from "@headlessui/react";
|
||||
import { IUser, IWorkspaceMember } from "@plane/types";
|
||||
import { CustomSelect, PopoverMenu, TOAST_TYPE, setToast } from "@plane/ui";
|
||||
import { EUserProjectRoles } from "@/constants/project";
|
||||
import { EUserWorkspaceRoles, ROLE } from "@/constants/workspace";
|
||||
import { ROLE } from "@/constants/workspace";
|
||||
import { useMember, useUser } from "@/hooks/store";
|
||||
import { EUserPermissions } from "@/plane-web/constants/user-permissions";
|
||||
|
||||
export interface RowData {
|
||||
member: IWorkspaceMember;
|
||||
role: EUserWorkspaceRoles;
|
||||
role: EUserPermissions;
|
||||
}
|
||||
|
||||
type NameProps = {
|
||||
|
|
@ -24,7 +24,7 @@ type NameProps = {
|
|||
|
||||
type AccountTypeProps = {
|
||||
rowData: RowData;
|
||||
currentProjectRole: EUserProjectRoles | undefined;
|
||||
currentProjectRole: EUserPermissions | undefined;
|
||||
workspaceSlug: string;
|
||||
projectId: string;
|
||||
};
|
||||
|
|
@ -97,16 +97,14 @@ export const AccountTypeColumn: React.FC<AccountTypeProps> = observer((props) =>
|
|||
|
||||
// derived values
|
||||
const isCurrentUser = currentUser?.id === rowData.member.id;
|
||||
const isAdminRole = currentProjectRole === EUserProjectRoles.ADMIN;
|
||||
const isAdminRole = currentProjectRole === EUserPermissions.ADMIN;
|
||||
const isRoleNonEditable = isCurrentUser || !isAdminRole;
|
||||
|
||||
const checkCurrentOptionWorkspaceRole = (value: string) => {
|
||||
const currentMemberWorkspaceRole = getWorkspaceMemberDetails(value)?.role;
|
||||
const currentMemberWorkspaceRole = getWorkspaceMemberDetails(value)?.role as EUserPermissions | undefined;
|
||||
if (!value || !currentMemberWorkspaceRole) return ROLE;
|
||||
|
||||
const isGuestOrViewer = [EUserWorkspaceRoles.GUEST, EUserWorkspaceRoles.VIEWER].includes(
|
||||
currentMemberWorkspaceRole
|
||||
);
|
||||
const isGuestOrViewer = [EUserPermissions.GUEST].includes(currentMemberWorkspaceRole);
|
||||
|
||||
return Object.fromEntries(
|
||||
Object.entries(ROLE).filter(([key]) => !isGuestOrViewer || [5, 10].includes(parseInt(key)))
|
||||
|
|
@ -127,11 +125,11 @@ export const AccountTypeColumn: React.FC<AccountTypeProps> = observer((props) =>
|
|||
render={({ field: { value } }) => (
|
||||
<CustomSelect
|
||||
value={value}
|
||||
onChange={(value: EUserProjectRoles) => {
|
||||
onChange={(value: EUserPermissions) => {
|
||||
if (!workspaceSlug) return;
|
||||
|
||||
updateMember(workspaceSlug.toString(), projectId.toString(), rowData.member.id, {
|
||||
role: value as unknown as EUserProjectRoles, // Cast value to unknown first, then to EUserWorkspaceRoles
|
||||
role: value as unknown as EUserPermissions, // Cast value to unknown first, then to EUserPermissions
|
||||
}).catch((err) => {
|
||||
console.log(err, "err");
|
||||
const error = err.error;
|
||||
|
|
@ -155,7 +153,7 @@ export const AccountTypeColumn: React.FC<AccountTypeProps> = observer((props) =>
|
|||
input
|
||||
>
|
||||
{Object.entries(checkCurrentOptionWorkspaceRole(rowData.member.id)).map(([key, label]) => {
|
||||
if (parseInt(key) > (currentProjectRole ?? EUserProjectRoles.GUEST)) return null;
|
||||
if (parseInt(key) > (currentProjectRole ?? EUserPermissions.GUEST)) return null;
|
||||
return (
|
||||
<CustomSelect.Option key={key} value={key}>
|
||||
{label}
|
||||
|
|
|
|||
|
|
@ -9,14 +9,13 @@ import { IProjectView } from "@plane/types";
|
|||
import { ContextMenu, CustomMenu, TContextMenuItem, TOAST_TYPE, setToast } from "@plane/ui";
|
||||
// components
|
||||
import { CreateUpdateProjectViewModal, DeleteProjectViewModal } from "@/components/views";
|
||||
// constants
|
||||
import { EUserProjectRoles } from "@/constants/project";
|
||||
// helpers
|
||||
import { cn } from "@/helpers/common.helper";
|
||||
import { copyUrlToClipboard } from "@/helpers/string.helper";
|
||||
// hooks
|
||||
import { useUser } from "@/hooks/store";
|
||||
import { useUser, useUserPermissions } from "@/hooks/store";
|
||||
import { PublishViewModal, useViewPublish } from "@/plane-web/components/views/publish";
|
||||
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||
|
||||
type Props = {
|
||||
parentRef: React.RefObject<HTMLElement>;
|
||||
|
|
@ -31,13 +30,11 @@ export const ViewQuickActions: React.FC<Props> = observer((props) => {
|
|||
const [createUpdateViewModal, setCreateUpdateViewModal] = useState(false);
|
||||
const [deleteViewModal, setDeleteViewModal] = useState(false);
|
||||
// store hooks
|
||||
const {
|
||||
membership: { currentProjectRole },
|
||||
data,
|
||||
} = useUser();
|
||||
const { data } = useUser();
|
||||
const { allowPermissions } = useUserPermissions();
|
||||
// auth
|
||||
const isOwner = view?.owned_by === data?.id;
|
||||
const isAdmin = !!currentProjectRole && currentProjectRole == EUserProjectRoles.ADMIN;
|
||||
const isAdmin = allowPermissions([EUserPermissions.ADMIN], EUserPermissionsLevel.PROJECT, workspaceSlug, projectId);
|
||||
|
||||
const { isPublishModalOpen, setPublishModalOpen, publishContextMenu } = useViewPublish(
|
||||
!!view.anchor,
|
||||
|
|
|
|||
|
|
@ -9,12 +9,12 @@ import { Tooltip, FavoriteStar } from "@plane/ui";
|
|||
// components
|
||||
import { DeleteProjectViewModal, CreateUpdateProjectViewModal, ViewQuickActions } from "@/components/views";
|
||||
// constants
|
||||
import { EUserProjectRoles } from "@/constants/project";
|
||||
import { EViewAccess } from "@/constants/views";
|
||||
// helpers
|
||||
import { calculateTotalFilters } from "@/helpers/filter.helper";
|
||||
// hooks
|
||||
import { useMember, useProjectView, useUser } from "@/hooks/store";
|
||||
import { useMember, useProjectView, useUserPermissions } from "@/hooks/store";
|
||||
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||
import { ButtonAvatars } from "../dropdowns/member/avatar";
|
||||
|
||||
type Props = {
|
||||
|
|
@ -30,14 +30,16 @@ export const ViewListItemAction: FC<Props> = observer((props) => {
|
|||
// router
|
||||
const { workspaceSlug, projectId } = useParams();
|
||||
// store
|
||||
const {
|
||||
membership: { currentProjectRole },
|
||||
} = useUser();
|
||||
const { allowPermissions } = useUserPermissions();
|
||||
|
||||
const { addViewToFavorites, removeViewFromFavorites } = useProjectView();
|
||||
const { getUserDetails } = useMember();
|
||||
|
||||
// derived values
|
||||
const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER;
|
||||
const isEditingAllowed = allowPermissions(
|
||||
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||
EUserPermissionsLevel.PROJECT
|
||||
);
|
||||
|
||||
const totalFilters = calculateTotalFilters(view.filters ?? {});
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
import React, { useEffect } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { useParams } from "next/navigation";
|
||||
import { Controller, useFieldArray, useForm } from "react-hook-form";
|
||||
import { Plus, X } from "lucide-react";
|
||||
import { Dialog, Transition } from "@headlessui/react";
|
||||
|
|
@ -9,9 +10,10 @@ import { IWorkspaceBulkInviteFormData } from "@plane/types";
|
|||
// ui
|
||||
import { Button, CustomSelect, Input } from "@plane/ui";
|
||||
// constants
|
||||
import { EUserWorkspaceRoles, ROLE } from "@/constants/workspace";
|
||||
import { ROLE } from "@/constants/workspace";
|
||||
// hooks
|
||||
import { useUser } from "@/hooks/store";
|
||||
import { useUserPermissions } from "@/hooks/store";
|
||||
import { EUserPermissions } from "@/plane-web/constants/user-permissions";
|
||||
// types
|
||||
|
||||
type Props = {
|
||||
|
|
@ -22,7 +24,7 @@ type Props = {
|
|||
|
||||
type EmailRole = {
|
||||
email: string;
|
||||
role: EUserWorkspaceRoles;
|
||||
role: EUserPermissions;
|
||||
};
|
||||
|
||||
type FormValues = {
|
||||
|
|
@ -40,10 +42,10 @@ const defaultValues: FormValues = {
|
|||
|
||||
export const SendWorkspaceInvitationModal: React.FC<Props> = observer((props) => {
|
||||
const { isOpen, onClose, onSubmit } = props;
|
||||
// mobx store
|
||||
const {
|
||||
membership: { currentWorkspaceRole },
|
||||
} = useUser();
|
||||
// store hooks
|
||||
const { workspaceInfoBySlug } = useUserPermissions();
|
||||
// router
|
||||
const { workspaceSlug } = useParams();
|
||||
// form info
|
||||
const {
|
||||
control,
|
||||
|
|
@ -57,6 +59,8 @@ export const SendWorkspaceInvitationModal: React.FC<Props> = observer((props) =>
|
|||
name: "emails",
|
||||
});
|
||||
|
||||
const currentWorkspaceRole = workspaceInfoBySlug(workspaceSlug.toString())?.role;
|
||||
|
||||
const handleClose = () => {
|
||||
onClose();
|
||||
|
||||
|
|
|
|||
|
|
@ -9,10 +9,11 @@ import { CustomSelect, Tooltip, TOAST_TYPE, setToast } from "@plane/ui";
|
|||
// components
|
||||
import { ConfirmWorkspaceMemberRemove } from "@/components/workspace";
|
||||
// constants
|
||||
import { EUserWorkspaceRoles, ROLE } from "@/constants/workspace";
|
||||
import { ROLE } from "@/constants/workspace";
|
||||
// hooks
|
||||
import { useMember, useUser } from "@/hooks/store";
|
||||
import { useMember, useUserPermissions } from "@/hooks/store";
|
||||
import { usePlatformOS } from "@/hooks/use-platform-os";
|
||||
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||
|
||||
type Props = {
|
||||
invitationId: string;
|
||||
|
|
@ -25,15 +26,16 @@ export const WorkspaceInvitationsListItem: FC<Props> = observer((props) => {
|
|||
// router
|
||||
const { workspaceSlug } = useParams();
|
||||
// store hooks
|
||||
const {
|
||||
membership: { currentWorkspaceMemberInfo, currentWorkspaceRole },
|
||||
} = useUser();
|
||||
const { allowPermissions, workspaceInfoBySlug } = useUserPermissions();
|
||||
|
||||
const {
|
||||
workspace: { updateMemberInvitation, deleteMemberInvitation, getWorkspaceInvitationDetails },
|
||||
} = useMember();
|
||||
const { isMobile } = usePlatformOS();
|
||||
// derived values
|
||||
const invitationDetails = getWorkspaceInvitationDetails(invitationId);
|
||||
const currentWorkspaceMemberInfo = workspaceInfoBySlug(workspaceSlug.toString());
|
||||
const currentWorkspaceRole = currentWorkspaceMemberInfo?.role;
|
||||
|
||||
const handleRemoveInvitation = async () => {
|
||||
if (!workspaceSlug || !invitationDetails) return;
|
||||
|
|
@ -58,13 +60,16 @@ export const WorkspaceInvitationsListItem: FC<Props> = observer((props) => {
|
|||
if (!invitationDetails) return null;
|
||||
|
||||
// is the current logged in user admin
|
||||
const isAdmin = currentWorkspaceRole === EUserWorkspaceRoles.ADMIN;
|
||||
const isAdmin = allowPermissions([EUserPermissions.ADMIN], EUserPermissionsLevel.WORKSPACE);
|
||||
|
||||
// role change access-
|
||||
// 1. user cannot change their own role
|
||||
// 2. only admin or member can change role
|
||||
// 3. user cannot change role of higher role
|
||||
const hasRoleChangeAccess =
|
||||
currentWorkspaceRole && [EUserWorkspaceRoles.ADMIN, EUserWorkspaceRoles.MEMBER].includes(currentWorkspaceRole);
|
||||
const hasRoleChangeAccess = allowPermissions(
|
||||
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||
EUserPermissionsLevel.WORKSPACE
|
||||
);
|
||||
|
||||
if (!currentWorkspaceMemberInfo) return null;
|
||||
|
||||
|
|
@ -110,7 +115,7 @@ export const WorkspaceInvitationsListItem: FC<Props> = observer((props) => {
|
|||
</div>
|
||||
}
|
||||
value={invitationDetails.role}
|
||||
onChange={(value: EUserWorkspaceRoles) => {
|
||||
onChange={(value: EUserPermissions) => {
|
||||
if (!workspaceSlug || !value) return;
|
||||
|
||||
updateMemberInvitation(workspaceSlug.toString(), invitationDetails.id, {
|
||||
|
|
|
|||
|
|
@ -5,13 +5,13 @@ import { Trash2 } from "lucide-react";
|
|||
import { Disclosure } from "@headlessui/react";
|
||||
import { IUser, IWorkspaceMember } from "@plane/types";
|
||||
import { CustomSelect, PopoverMenu, TOAST_TYPE, setToast } from "@plane/ui";
|
||||
import { EUserProjectRoles } from "@/constants/project";
|
||||
import { EUserWorkspaceRoles, ROLE } from "@/constants/workspace";
|
||||
import { useMember, useUser } from "@/hooks/store";
|
||||
import { ROLE } from "@/constants/workspace";
|
||||
import { useMember, useUser, useUserPermissions } from "@/hooks/store";
|
||||
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||
|
||||
export interface RowData {
|
||||
member: IWorkspaceMember;
|
||||
role: EUserWorkspaceRoles;
|
||||
role: EUserPermissions;
|
||||
}
|
||||
|
||||
type NameProps = {
|
||||
|
|
@ -24,7 +24,6 @@ type NameProps = {
|
|||
|
||||
type AccountTypeProps = {
|
||||
rowData: RowData;
|
||||
currentWorkspaceRole: EUserWorkspaceRoles | undefined;
|
||||
workspaceSlug: string;
|
||||
};
|
||||
|
||||
|
|
@ -81,13 +80,15 @@ export const NameColumn: React.FC<NameProps> = (props) => {
|
|||
};
|
||||
|
||||
export const AccountTypeColumn: React.FC<AccountTypeProps> = observer((props) => {
|
||||
const { rowData, currentWorkspaceRole, workspaceSlug } = props;
|
||||
const { rowData, workspaceSlug } = props;
|
||||
// form info
|
||||
const {
|
||||
control,
|
||||
formState: { errors },
|
||||
} = useForm();
|
||||
// store hooks
|
||||
const { allowPermissions } = useUserPermissions();
|
||||
|
||||
const {
|
||||
workspace: { updateMember },
|
||||
} = useMember();
|
||||
|
|
@ -95,7 +96,7 @@ export const AccountTypeColumn: React.FC<AccountTypeProps> = observer((props) =>
|
|||
|
||||
// derived values
|
||||
const isCurrentUser = currentUser?.id === rowData.member.id;
|
||||
const isAdminRole = currentWorkspaceRole === EUserWorkspaceRoles.ADMIN;
|
||||
const isAdminRole = allowPermissions([EUserPermissions.ADMIN], EUserPermissionsLevel.WORKSPACE);
|
||||
const isRoleNonEditable = isCurrentUser || !isAdminRole;
|
||||
|
||||
return (
|
||||
|
|
@ -112,12 +113,12 @@ export const AccountTypeColumn: React.FC<AccountTypeProps> = observer((props) =>
|
|||
render={({ field: { value } }) => (
|
||||
<CustomSelect
|
||||
value={value}
|
||||
onChange={(value: EUserProjectRoles) => {
|
||||
onChange={(value: EUserPermissions) => {
|
||||
console.log({ value, workspaceSlug }, "onChange");
|
||||
if (!workspaceSlug) return;
|
||||
|
||||
updateMember(workspaceSlug.toString(), rowData.member.id, {
|
||||
role: value as unknown as EUserWorkspaceRoles, // Cast value to unknown first, then to EUserWorkspaceRoles
|
||||
role: value as unknown as EUserPermissions, // Cast value to unknown first, then to EUserPermissions
|
||||
}).catch((err) => {
|
||||
console.log(err, "err");
|
||||
const error = err.error;
|
||||
|
|
@ -141,7 +142,7 @@ export const AccountTypeColumn: React.FC<AccountTypeProps> = observer((props) =>
|
|||
input
|
||||
>
|
||||
{Object.keys(ROLE).map((item) => (
|
||||
<CustomSelect.Option key={item} value={item as unknown as EUserProjectRoles}>
|
||||
<CustomSelect.Option key={item} value={item as unknown as EUserPermissions}>
|
||||
{ROLE[item as unknown as keyof typeof ROLE]}
|
||||
</CustomSelect.Option>
|
||||
))}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import { ConfirmWorkspaceMemberRemove } from "@/components/workspace";
|
|||
// constants
|
||||
import { WORKSPACE_MEMBER_LEAVE } from "@/constants/event-tracker";
|
||||
// hooks
|
||||
import { useEventTracker, useMember, useUser } from "@/hooks/store";
|
||||
import { useEventTracker, useMember, useUser, useUserPermissions } from "@/hooks/store";
|
||||
import { useAppRouter } from "@/hooks/use-app-router";
|
||||
import { useMemberColumns } from "@/plane-web/components/workspace/settings/useMemberColumns";
|
||||
|
||||
|
|
@ -26,13 +26,11 @@ export const WorkspaceMembersListItem: FC<Props> = observer((props) => {
|
|||
// router
|
||||
const router = useAppRouter();
|
||||
// store hooks
|
||||
const {
|
||||
membership: { leaveWorkspace },
|
||||
} = useUser();
|
||||
const { data: currentUser } = useUser();
|
||||
const {
|
||||
workspace: { removeMemberFromWorkspace },
|
||||
} = useMember();
|
||||
const { leaveWorkspace } = useUserPermissions();
|
||||
const { captureEvent } = useEventTracker();
|
||||
// derived values
|
||||
|
||||
|
|
|
|||
|
|
@ -12,13 +12,14 @@ import { LogoSpinner } from "@/components/common";
|
|||
import { WorkspaceImageUploadModal } from "@/components/core";
|
||||
// constants
|
||||
import { WORKSPACE_UPDATED } from "@/constants/event-tracker";
|
||||
import { EUserWorkspaceRoles, ORGANIZATION_SIZE } from "@/constants/workspace";
|
||||
import { ORGANIZATION_SIZE } from "@/constants/workspace";
|
||||
// helpers
|
||||
import { copyUrlToClipboard } from "@/helpers/string.helper";
|
||||
// hooks
|
||||
import { useEventTracker, useUser, useWorkspace } from "@/hooks/store";
|
||||
import { useEventTracker, useUserPermissions, useWorkspace } from "@/hooks/store";
|
||||
// plane web components
|
||||
import { DeleteWorkspaceSection } from "@/plane-web/components/workspace";
|
||||
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||
// services
|
||||
import { FileService } from "@/services/file.service";
|
||||
|
||||
|
|
@ -39,10 +40,9 @@ export const WorkspaceDetails: FC = observer(() => {
|
|||
const [isImageUploadModalOpen, setIsImageUploadModalOpen] = useState(false);
|
||||
// store hooks
|
||||
const { captureWorkspaceEvent } = useEventTracker();
|
||||
const {
|
||||
membership: { currentWorkspaceRole },
|
||||
} = useUser();
|
||||
const { currentWorkspace, updateWorkspace } = useWorkspace();
|
||||
const { allowPermissions } = useUserPermissions();
|
||||
|
||||
// form info
|
||||
const {
|
||||
handleSubmit,
|
||||
|
|
@ -141,7 +141,7 @@ export const WorkspaceDetails: FC = observer(() => {
|
|||
if (currentWorkspace) reset({ ...currentWorkspace });
|
||||
}, [currentWorkspace, reset]);
|
||||
|
||||
const isAdmin = currentWorkspaceRole === EUserWorkspaceRoles.ADMIN;
|
||||
const isAdmin = allowPermissions([EUserPermissions.ADMIN], EUserPermissionsLevel.WORKSPACE);
|
||||
|
||||
if (!currentWorkspace)
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -41,15 +41,14 @@ import {
|
|||
import { Logo } from "@/components/common";
|
||||
import { LeaveProjectModal, PublishProjectModal } from "@/components/project";
|
||||
import { SidebarNavItem } from "@/components/sidebar";
|
||||
// constants
|
||||
import { EUserProjectRoles } from "@/constants/project";
|
||||
// helpers
|
||||
import { cn } from "@/helpers/common.helper";
|
||||
// hooks
|
||||
import { useAppTheme, useEventTracker, useProject, useUser } from "@/hooks/store";
|
||||
import { useAppTheme, useEventTracker, useProject, useUserPermissions } from "@/hooks/store";
|
||||
import useOutsideClickDetector from "@/hooks/use-outside-click-detector";
|
||||
import { usePlatformOS } from "@/hooks/use-platform-os";
|
||||
// constants
|
||||
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||
import { HIGHLIGHT_CLASS, highlightIssueOnDrop } from "../../issues/issue-layouts/utils";
|
||||
|
||||
type Props = {
|
||||
|
|
@ -71,37 +70,37 @@ const navigation = (workspaceSlug: string, projectId: string) => [
|
|||
name: "Issues",
|
||||
href: `/${workspaceSlug}/projects/${projectId}/issues`,
|
||||
Icon: LayersIcon,
|
||||
access: EUserProjectRoles.GUEST,
|
||||
access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER, EUserPermissions.GUEST],
|
||||
},
|
||||
{
|
||||
name: "Cycles",
|
||||
href: `/${workspaceSlug}/projects/${projectId}/cycles`,
|
||||
Icon: ContrastIcon,
|
||||
access: EUserProjectRoles.VIEWER,
|
||||
access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||
},
|
||||
{
|
||||
name: "Modules",
|
||||
href: `/${workspaceSlug}/projects/${projectId}/modules`,
|
||||
Icon: DiceIcon,
|
||||
access: EUserProjectRoles.VIEWER,
|
||||
access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||
},
|
||||
{
|
||||
name: "Views",
|
||||
href: `/${workspaceSlug}/projects/${projectId}/views`,
|
||||
Icon: Layers,
|
||||
access: EUserProjectRoles.GUEST,
|
||||
access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER, EUserPermissions.GUEST],
|
||||
},
|
||||
{
|
||||
name: "Pages",
|
||||
href: `/${workspaceSlug}/projects/${projectId}/pages`,
|
||||
Icon: FileText,
|
||||
access: EUserProjectRoles.VIEWER,
|
||||
access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER, EUserPermissions.GUEST],
|
||||
},
|
||||
{
|
||||
name: "Intake",
|
||||
href: `/${workspaceSlug}/projects/${projectId}/inbox`,
|
||||
Icon: Intake,
|
||||
access: EUserProjectRoles.GUEST,
|
||||
access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER, EUserPermissions.GUEST],
|
||||
},
|
||||
];
|
||||
|
||||
|
|
@ -113,9 +112,7 @@ export const SidebarProjectsListItem: React.FC<Props> = observer((props) => {
|
|||
const { setTrackElement } = useEventTracker();
|
||||
const { addProjectToFavorites, removeProjectFromFavorites, getProjectById } = useProject();
|
||||
const { isMobile } = usePlatformOS();
|
||||
const {
|
||||
membership: { currentWorkspaceAllProjectsRole },
|
||||
} = useUser();
|
||||
const { allowPermissions } = useUserPermissions();
|
||||
// states
|
||||
const [leaveProjectModalOpen, setLeaveProjectModal] = useState(false);
|
||||
const [publishModalOpen, setPublishModal] = useState(false);
|
||||
|
|
@ -135,9 +132,18 @@ export const SidebarProjectsListItem: React.FC<Props> = observer((props) => {
|
|||
// derived values
|
||||
const project = getProjectById(projectId);
|
||||
// auth
|
||||
const isAdmin = project?.member_role === EUserProjectRoles.ADMIN;
|
||||
const isViewerOrGuest =
|
||||
project?.member_role && [EUserProjectRoles.VIEWER, EUserProjectRoles.GUEST].includes(project.member_role);
|
||||
const isAdmin = allowPermissions(
|
||||
[EUserPermissions.ADMIN],
|
||||
EUserPermissionsLevel.PROJECT,
|
||||
workspaceSlug.toString(),
|
||||
project?.id
|
||||
);
|
||||
const isAuthorized = allowPermissions(
|
||||
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||
EUserPermissionsLevel.PROJECT,
|
||||
workspaceSlug.toString(),
|
||||
project?.id
|
||||
);
|
||||
|
||||
const handleAddToFavorites = () => {
|
||||
if (!workspaceSlug || !project) return;
|
||||
|
|
@ -395,7 +401,7 @@ export const SidebarProjectsListItem: React.FC<Props> = observer((props) => {
|
|||
placement="bottom-start"
|
||||
useCaptureForOutsideClick
|
||||
>
|
||||
{!isViewerOrGuest && (
|
||||
{isAuthorized && (
|
||||
<CustomMenu.MenuItem
|
||||
onClick={project.is_favorite ? handleRemoveFromFavorites : handleAddToFavorites}
|
||||
>
|
||||
|
|
@ -421,7 +427,7 @@ export const SidebarProjectsListItem: React.FC<Props> = observer((props) => {
|
|||
</div>
|
||||
</CustomMenu.MenuItem>
|
||||
)}
|
||||
{!isViewerOrGuest && (
|
||||
{isAuthorized && (
|
||||
<CustomMenu.MenuItem>
|
||||
<Link href={`/${workspaceSlug}/projects/${project?.id}/draft-issues/`}>
|
||||
<div className="flex items-center justify-start gap-2">
|
||||
|
|
@ -437,7 +443,7 @@ export const SidebarProjectsListItem: React.FC<Props> = observer((props) => {
|
|||
<span>Copy link</span>
|
||||
</span>
|
||||
</CustomMenu.MenuItem>
|
||||
{!isViewerOrGuest && (
|
||||
{isAuthorized && (
|
||||
<CustomMenu.MenuItem>
|
||||
<Link href={`/${workspaceSlug}/projects/${project?.id}/archives/issues`}>
|
||||
<div className="flex items-center justify-start gap-2">
|
||||
|
|
@ -456,7 +462,7 @@ export const SidebarProjectsListItem: React.FC<Props> = observer((props) => {
|
|||
</Link>
|
||||
</CustomMenu.MenuItem>
|
||||
{/* leave project */}
|
||||
{isViewerOrGuest && (
|
||||
{!isAuthorized && (
|
||||
<CustomMenu.MenuItem onClick={handleLeaveProject}>
|
||||
<div className="flex items-center justify-start gap-2">
|
||||
<LogOut className="h-3.5 w-3.5 stroke-[1.5]" />
|
||||
|
|
@ -505,12 +511,14 @@ export const SidebarProjectsListItem: React.FC<Props> = observer((props) => {
|
|||
(item.name === "Intake" && !project.inbox_view)
|
||||
)
|
||||
return;
|
||||
const currentRole = currentWorkspaceAllProjectsRole
|
||||
? currentWorkspaceAllProjectsRole[projectId]
|
||||
: undefined;
|
||||
return (
|
||||
<>
|
||||
{currentRole >= item.access && (
|
||||
{allowPermissions(
|
||||
item.access,
|
||||
EUserPermissionsLevel.PROJECT,
|
||||
workspaceSlug.toString(),
|
||||
project.id
|
||||
) && (
|
||||
<Tooltip
|
||||
key={item.name}
|
||||
isMobile={isMobile}
|
||||
|
|
|
|||
|
|
@ -14,14 +14,13 @@ import { TOAST_TYPE, Tooltip, setToast } from "@plane/ui";
|
|||
// components
|
||||
import { CreateProjectModal } from "@/components/project";
|
||||
import { SidebarProjectsListItem } from "@/components/workspace";
|
||||
// constants
|
||||
import { EUserWorkspaceRoles } from "@/constants/workspace";
|
||||
// helpers
|
||||
import { cn } from "@/helpers/common.helper";
|
||||
import { orderJoinedProjects } from "@/helpers/project.helper";
|
||||
import { copyUrlToClipboard } from "@/helpers/string.helper";
|
||||
// hooks
|
||||
import { useAppTheme, useCommandPalette, useEventTracker, useProject, useUser } from "@/hooks/store";
|
||||
import { useAppTheme, useCommandPalette, useEventTracker, useProject, useUserPermissions } from "@/hooks/store";
|
||||
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||
|
||||
export const SidebarProjectsList: FC = observer(() => {
|
||||
// get local storage data for isFavoriteProjectsListOpen and isAllProjectsListOpen
|
||||
|
|
@ -37,16 +36,18 @@ export const SidebarProjectsList: FC = observer(() => {
|
|||
const { toggleCreateProjectModal } = useCommandPalette();
|
||||
const { sidebarCollapsed } = useAppTheme();
|
||||
const { setTrackElement } = useEventTracker();
|
||||
const {
|
||||
membership: { currentWorkspaceRole },
|
||||
} = useUser();
|
||||
const { allowPermissions } = useUserPermissions();
|
||||
|
||||
const { getProjectById, joinedProjectIds: joinedProjects, updateProjectView } = useProject();
|
||||
// router params
|
||||
const { workspaceSlug } = useParams();
|
||||
const pathname = usePathname();
|
||||
|
||||
// auth
|
||||
const isAuthorizedUser = !!currentWorkspaceRole && currentWorkspaceRole >= EUserWorkspaceRoles.MEMBER;
|
||||
const isAuthorizedUser = allowPermissions(
|
||||
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||
EUserPermissionsLevel.WORKSPACE
|
||||
);
|
||||
|
||||
const handleCopyText = (projectId: string) => {
|
||||
copyUrlToClipboard(`${workspaceSlug}/projects/${projectId}/issues`).then(() => {
|
||||
|
|
|
|||
|
|
@ -11,28 +11,25 @@ import { NotificationAppSidebarOption } from "@/components/workspace-notificatio
|
|||
// constants
|
||||
import { SIDEBAR_USER_MENU_ITEMS } from "@/constants/dashboard";
|
||||
import { SIDEBAR_CLICKED } from "@/constants/event-tracker";
|
||||
import { EUserWorkspaceRoles } from "@/constants/workspace";
|
||||
// helpers
|
||||
import { cn } from "@/helpers/common.helper";
|
||||
// hooks
|
||||
import { useAppTheme, useEventTracker, useUser } from "@/hooks/store";
|
||||
import { useAppTheme, useEventTracker, useUser, useUserPermissions } from "@/hooks/store";
|
||||
import { usePlatformOS } from "@/hooks/use-platform-os";
|
||||
import { EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||
|
||||
export const SidebarUserMenu = observer(() => {
|
||||
// store hooks
|
||||
const { toggleSidebar, sidebarCollapsed } = useAppTheme();
|
||||
const { captureEvent } = useEventTracker();
|
||||
const { isMobile } = usePlatformOS();
|
||||
const {
|
||||
membership: { currentWorkspaceRole },
|
||||
data: currentUser,
|
||||
} = useUser();
|
||||
const { data: currentUser } = useUser();
|
||||
const { allowPermissions } = useUserPermissions();
|
||||
// router params
|
||||
const { workspaceSlug } = useParams();
|
||||
// pathname
|
||||
const pathname = usePathname();
|
||||
// computed
|
||||
const workspaceMemberInfo = currentWorkspaceRole || EUserWorkspaceRoles.GUEST;
|
||||
|
||||
const getHref = (link: any) =>
|
||||
`/${workspaceSlug}${link.href}${link.key === "your-work" ? `/${currentUser?.id}` : ""}`;
|
||||
|
|
@ -61,7 +58,7 @@ export const SidebarUserMenu = observer(() => {
|
|||
>
|
||||
{SIDEBAR_USER_MENU_ITEMS.map(
|
||||
(link) =>
|
||||
workspaceMemberInfo >= link.access && (
|
||||
allowPermissions(link.access, EUserPermissionsLevel.WORKSPACE, workspaceSlug.toString()) && (
|
||||
<Tooltip
|
||||
key={link.key}
|
||||
tooltipContent={link.label}
|
||||
|
|
|
|||
|
|
@ -13,16 +13,16 @@ import { SidebarNavItem } from "@/components/sidebar";
|
|||
// constants
|
||||
import { SIDEBAR_WORKSPACE_MENU_ITEMS } from "@/constants/dashboard";
|
||||
import { SIDEBAR_CLICKED } from "@/constants/event-tracker";
|
||||
import { EUserWorkspaceRoles } from "@/constants/workspace";
|
||||
// helpers
|
||||
import { cn } from "@/helpers/common.helper";
|
||||
// hooks
|
||||
import { useAppTheme, useEventTracker, useUser } from "@/hooks/store";
|
||||
import { useAppTheme, useEventTracker, useUserPermissions } from "@/hooks/store";
|
||||
import useLocalStorage from "@/hooks/use-local-storage";
|
||||
import useOutsideClickDetector from "@/hooks/use-outside-click-detector";
|
||||
import { usePlatformOS } from "@/hooks/use-platform-os";
|
||||
// plane web components
|
||||
import { UpgradeBadge } from "@/plane-web/components/workspace";
|
||||
import { EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||
|
||||
export const SidebarWorkspaceMenu = observer(() => {
|
||||
// state
|
||||
|
|
@ -33,9 +33,7 @@ export const SidebarWorkspaceMenu = observer(() => {
|
|||
const { toggleSidebar, sidebarCollapsed } = useAppTheme();
|
||||
const { captureEvent } = useEventTracker();
|
||||
const { isMobile } = usePlatformOS();
|
||||
const {
|
||||
membership: { currentWorkspaceRole },
|
||||
} = useUser();
|
||||
const { allowPermissions } = useUserPermissions();
|
||||
// router params
|
||||
const { workspaceSlug } = useParams();
|
||||
// pathname
|
||||
|
|
@ -44,8 +42,6 @@ export const SidebarWorkspaceMenu = observer(() => {
|
|||
const { setValue: toggleWorkspaceMenu, storedValue } = useLocalStorage<boolean>("is_workspace_menu_open", true);
|
||||
// derived values
|
||||
const isWorkspaceMenuOpen = !!storedValue;
|
||||
// auth
|
||||
const workspaceMemberInfo = currentWorkspaceRole || EUserWorkspaceRoles.GUEST;
|
||||
|
||||
const handleLinkClick = (itemKey: string) => {
|
||||
if (window.innerWidth < 768) {
|
||||
|
|
@ -157,7 +153,7 @@ export const SidebarWorkspaceMenu = observer(() => {
|
|||
>
|
||||
{SIDEBAR_WORKSPACE_MENU_ITEMS.map(
|
||||
(link) =>
|
||||
workspaceMemberInfo >= link.access && (
|
||||
allowPermissions(link.access, EUserPermissionsLevel.WORKSPACE, workspaceSlug.toString()) && (
|
||||
<Tooltip
|
||||
key={link.key}
|
||||
tooltipContent={link.label}
|
||||
|
|
|
|||
|
|
@ -14,9 +14,10 @@ import {
|
|||
} from "@/components/workspace";
|
||||
// constants
|
||||
import { GLOBAL_VIEW_OPENED } from "@/constants/event-tracker";
|
||||
import { DEFAULT_GLOBAL_VIEWS_LIST, EUserWorkspaceRoles } from "@/constants/workspace";
|
||||
import { DEFAULT_GLOBAL_VIEWS_LIST } from "@/constants/workspace";
|
||||
// store hooks
|
||||
import { useEventTracker, useGlobalView, useUser } from "@/hooks/store";
|
||||
import { useEventTracker, useGlobalView, useUserPermissions } from "@/hooks/store";
|
||||
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||
|
||||
const ViewTab = observer((props: { viewId: string }) => {
|
||||
const { viewId } = props;
|
||||
|
|
@ -77,9 +78,8 @@ export const GlobalViewsHeader: React.FC = observer(() => {
|
|||
const { globalViewId } = useParams();
|
||||
// store hooks
|
||||
const { currentWorkspaceViews } = useGlobalView();
|
||||
const {
|
||||
membership: { currentWorkspaceRole },
|
||||
} = useUser();
|
||||
const { allowPermissions } = useUserPermissions();
|
||||
|
||||
const { captureEvent } = useEventTracker();
|
||||
|
||||
// bring the active view to the centre of the header
|
||||
|
|
@ -101,7 +101,10 @@ export const GlobalViewsHeader: React.FC = observer(() => {
|
|||
}
|
||||
}, [globalViewId, currentWorkspaceViews, containerRef, captureEvent]);
|
||||
|
||||
const isAuthorizedUser = !!currentWorkspaceRole && currentWorkspaceRole >= EUserWorkspaceRoles.MEMBER;
|
||||
const isAuthorizedUser = allowPermissions(
|
||||
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||
EUserPermissionsLevel.WORKSPACE
|
||||
);
|
||||
|
||||
return (
|
||||
<Header variant={EHeaderVariant.SECONDARY} className="min-h-[44px]">
|
||||
|
|
|
|||
|
|
@ -12,12 +12,12 @@ import { ContextMenu, CustomMenu, TContextMenuItem, TOAST_TYPE, setToast } from
|
|||
import { CreateUpdateWorkspaceViewModal, DeleteGlobalViewModal } from "@/components/workspace";
|
||||
// constants
|
||||
import { EViewAccess } from "@/constants/views";
|
||||
import { EUserWorkspaceRoles } from "@/constants/workspace";
|
||||
// helpers
|
||||
import { cn } from "@/helpers/common.helper";
|
||||
import { copyUrlToClipboard } from "@/helpers/string.helper";
|
||||
// hooks
|
||||
import { useUser } from "@/hooks/store";
|
||||
import { useUser, useUserPermissions } from "@/hooks/store";
|
||||
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||
|
||||
type Props = {
|
||||
parentRef: React.RefObject<HTMLElement>;
|
||||
|
|
@ -33,13 +33,11 @@ export const WorkspaceViewQuickActions: React.FC<Props> = observer((props) => {
|
|||
const [updateViewModal, setUpdateViewModal] = useState(false);
|
||||
const [deleteViewModal, setDeleteViewModal] = useState(false);
|
||||
// store hooks
|
||||
const {
|
||||
membership: { currentWorkspaceRole },
|
||||
data,
|
||||
} = useUser();
|
||||
const { data } = useUser();
|
||||
const { allowPermissions } = useUserPermissions();
|
||||
// auth
|
||||
const isOwner = view?.owned_by === data?.id;
|
||||
const isAdmin = !!currentWorkspaceRole && currentWorkspaceRole === EUserWorkspaceRoles.ADMIN;
|
||||
const isAdmin = allowPermissions([EUserPermissions.ADMIN], EUserPermissionsLevel.WORKSPACE);
|
||||
|
||||
const viewLink = `${workspaceSlug}/workspace-views/${view.id}`;
|
||||
const handleCopyText = () =>
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import { TIssuesListTypes, TStateGroups } from "@plane/types";
|
|||
// ui
|
||||
import { ContrastIcon, UserActivityIcon } from "@plane/ui";
|
||||
import { Props } from "@/components/icons/types";
|
||||
import { EUserPermissions } from "@/plane-web/constants/user-permissions";
|
||||
// assets
|
||||
import CompletedIssuesDark from "@/public/empty-state/dashboard/dark/completed-issues.svg";
|
||||
import OverdueIssuesDark from "@/public/empty-state/dashboard/dark/overdue-issues.svg";
|
||||
|
|
@ -15,8 +16,6 @@ import UpcomingIssuesDark from "@/public/empty-state/dashboard/dark/upcoming-iss
|
|||
import CompletedIssuesLight from "@/public/empty-state/dashboard/light/completed-issues.svg";
|
||||
import OverdueIssuesLight from "@/public/empty-state/dashboard/light/overdue-issues.svg";
|
||||
import UpcomingIssuesLight from "@/public/empty-state/dashboard/light/upcoming-issues.svg";
|
||||
// constants
|
||||
import { EUserWorkspaceRoles } from "./workspace";
|
||||
|
||||
// gradients for issues by priority widget graph bars
|
||||
export const PRIORITY_GRAPH_GRADIENTS = [
|
||||
|
|
@ -255,7 +254,7 @@ export const SIDEBAR_WORKSPACE_MENU_ITEMS: {
|
|||
key: string;
|
||||
label: string;
|
||||
href: string;
|
||||
access: EUserWorkspaceRoles;
|
||||
access: EUserPermissions[];
|
||||
highlight: (pathname: string, baseUrl: string) => boolean;
|
||||
Icon: React.FC<Props>;
|
||||
}[] = [
|
||||
|
|
@ -263,7 +262,7 @@ export const SIDEBAR_WORKSPACE_MENU_ITEMS: {
|
|||
key: "projects",
|
||||
label: "Projects",
|
||||
href: `/projects`,
|
||||
access: EUserWorkspaceRoles.GUEST,
|
||||
access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER, EUserPermissions.GUEST],
|
||||
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/projects/`,
|
||||
Icon: Briefcase,
|
||||
},
|
||||
|
|
@ -271,7 +270,7 @@ export const SIDEBAR_WORKSPACE_MENU_ITEMS: {
|
|||
key: "all-issues",
|
||||
label: "Views",
|
||||
href: `/workspace-views/all-issues`,
|
||||
access: EUserWorkspaceRoles.GUEST,
|
||||
access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER, EUserPermissions.GUEST],
|
||||
highlight: (pathname: string, baseUrl: string) => pathname.includes(`${baseUrl}/workspace-views/`),
|
||||
Icon: Layers,
|
||||
},
|
||||
|
|
@ -279,7 +278,7 @@ export const SIDEBAR_WORKSPACE_MENU_ITEMS: {
|
|||
key: "active-cycles",
|
||||
label: "Cycles",
|
||||
href: `/active-cycles`,
|
||||
access: EUserWorkspaceRoles.MEMBER,
|
||||
access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/active-cycles/`,
|
||||
Icon: ContrastIcon,
|
||||
},
|
||||
|
|
@ -287,7 +286,7 @@ export const SIDEBAR_WORKSPACE_MENU_ITEMS: {
|
|||
key: "analytics",
|
||||
label: "Analytics",
|
||||
href: `/analytics`,
|
||||
access: EUserWorkspaceRoles.MEMBER,
|
||||
access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||
highlight: (pathname: string, baseUrl: string) => pathname.includes(`${baseUrl}/analytics/`),
|
||||
Icon: BarChart2,
|
||||
},
|
||||
|
|
@ -301,7 +300,7 @@ export const SIDEBAR_USER_MENU_ITEMS: {
|
|||
key: string;
|
||||
label: string;
|
||||
href: string;
|
||||
access: EUserWorkspaceRoles;
|
||||
access: EUserPermissions[];
|
||||
highlight: (pathname: string, baseUrl: string, options?: TLinkOptions) => boolean;
|
||||
Icon: React.FC<Props>;
|
||||
}[] = [
|
||||
|
|
@ -309,7 +308,7 @@ export const SIDEBAR_USER_MENU_ITEMS: {
|
|||
key: "home",
|
||||
label: "Home",
|
||||
href: ``,
|
||||
access: EUserWorkspaceRoles.GUEST,
|
||||
access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER, EUserPermissions.GUEST],
|
||||
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/`,
|
||||
Icon: Home,
|
||||
},
|
||||
|
|
@ -317,7 +316,7 @@ export const SIDEBAR_USER_MENU_ITEMS: {
|
|||
key: "your-work",
|
||||
label: "Your work",
|
||||
href: "/profile",
|
||||
access: EUserWorkspaceRoles.MEMBER,
|
||||
access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||
highlight: (pathname: string, baseUrl: string, options?: TLinkOptions) =>
|
||||
options?.userId ? pathname.includes(`${baseUrl}/profile/${options?.userId}`) : false,
|
||||
Icon: UserActivityIcon,
|
||||
|
|
@ -326,7 +325,7 @@ export const SIDEBAR_USER_MENU_ITEMS: {
|
|||
key: "notifications",
|
||||
label: "Inbox",
|
||||
href: `/notifications`,
|
||||
access: EUserWorkspaceRoles.GUEST,
|
||||
access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||
highlight: (pathname: string, baseUrl: string) => pathname.includes(`${baseUrl}/notifications/`),
|
||||
Icon: Inbox,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import { EUserProjectRoles } from "./project";
|
||||
import { EUserWorkspaceRoles } from "./workspace";
|
||||
import { EUserPermissions } from "ee/constants/user-permissions";
|
||||
|
||||
export interface EmptyStateDetails {
|
||||
key: EmptyStateType;
|
||||
|
|
@ -23,7 +22,7 @@ export interface EmptyStateDetails {
|
|||
};
|
||||
};
|
||||
accessType?: "workspace" | "project";
|
||||
access?: EUserWorkspaceRoles | EUserProjectRoles;
|
||||
access?: any;
|
||||
}
|
||||
|
||||
export enum EmptyStateType {
|
||||
|
|
@ -126,7 +125,7 @@ const emptyStateDetails = {
|
|||
},
|
||||
|
||||
accessType: "workspace",
|
||||
access: EUserWorkspaceRoles.MEMBER,
|
||||
access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||
},
|
||||
[EmptyStateType.WORKSPACE_ANALYTICS]: {
|
||||
key: EmptyStateType.WORKSPACE_ANALYTICS,
|
||||
|
|
@ -143,7 +142,7 @@ const emptyStateDetails = {
|
|||
},
|
||||
},
|
||||
accessType: "workspace",
|
||||
access: EUserWorkspaceRoles.MEMBER,
|
||||
access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||
},
|
||||
[EmptyStateType.WORKSPACE_PROJECTS]: {
|
||||
key: EmptyStateType.WORKSPACE_PROJECTS,
|
||||
|
|
@ -159,7 +158,7 @@ const emptyStateDetails = {
|
|||
},
|
||||
},
|
||||
accessType: "workspace",
|
||||
access: EUserWorkspaceRoles.MEMBER,
|
||||
access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||
},
|
||||
// all-issues
|
||||
[EmptyStateType.WORKSPACE_ALL_ISSUES]: {
|
||||
|
|
@ -171,7 +170,7 @@ const emptyStateDetails = {
|
|||
text: "Create new issue",
|
||||
},
|
||||
accessType: "workspace",
|
||||
access: EUserWorkspaceRoles.MEMBER,
|
||||
access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||
},
|
||||
[EmptyStateType.WORKSPACE_ASSIGNED]: {
|
||||
key: EmptyStateType.WORKSPACE_ASSIGNED,
|
||||
|
|
@ -182,7 +181,7 @@ const emptyStateDetails = {
|
|||
text: "Create new issue",
|
||||
},
|
||||
accessType: "workspace",
|
||||
access: EUserWorkspaceRoles.MEMBER,
|
||||
access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||
},
|
||||
[EmptyStateType.WORKSPACE_CREATED]: {
|
||||
key: EmptyStateType.WORKSPACE_CREATED,
|
||||
|
|
@ -193,7 +192,7 @@ const emptyStateDetails = {
|
|||
text: "Create new issue",
|
||||
},
|
||||
accessType: "workspace",
|
||||
access: EUserWorkspaceRoles.MEMBER,
|
||||
access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||
},
|
||||
[EmptyStateType.WORKSPACE_SUBSCRIBED]: {
|
||||
key: EmptyStateType.WORKSPACE_SUBSCRIBED,
|
||||
|
|
@ -220,7 +219,7 @@ const emptyStateDetails = {
|
|||
},
|
||||
},
|
||||
accessType: "workspace",
|
||||
access: EUserWorkspaceRoles.MEMBER,
|
||||
access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||
},
|
||||
// workspace settings
|
||||
[EmptyStateType.WORKSPACE_SETTINGS_API_TOKENS]: {
|
||||
|
|
@ -309,7 +308,7 @@ const emptyStateDetails = {
|
|||
},
|
||||
},
|
||||
accessType: "project",
|
||||
access: EUserProjectRoles.MEMBER,
|
||||
access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||
},
|
||||
[EmptyStateType.PROJECT_CYCLE_NO_ISSUES]: {
|
||||
key: EmptyStateType.PROJECT_CYCLE_NO_ISSUES,
|
||||
|
|
@ -323,7 +322,7 @@ const emptyStateDetails = {
|
|||
text: "Add an existing issue",
|
||||
},
|
||||
accessType: "project",
|
||||
access: EUserProjectRoles.MEMBER,
|
||||
access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||
},
|
||||
[EmptyStateType.PROJECT_CYCLE_ACTIVE]: {
|
||||
key: EmptyStateType.PROJECT_CYCLE_ACTIVE,
|
||||
|
|
@ -361,7 +360,7 @@ const emptyStateDetails = {
|
|||
text: "Clear all filters",
|
||||
},
|
||||
accessType: "project",
|
||||
access: EUserProjectRoles.MEMBER,
|
||||
access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||
},
|
||||
[EmptyStateType.PROJECT_ARCHIVED_EMPTY_FILTER]: {
|
||||
key: EmptyStateType.PROJECT_ARCHIVED_EMPTY_FILTER,
|
||||
|
|
@ -371,7 +370,7 @@ const emptyStateDetails = {
|
|||
text: "Clear all filters",
|
||||
},
|
||||
accessType: "project",
|
||||
access: EUserProjectRoles.MEMBER,
|
||||
access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||
},
|
||||
[EmptyStateType.PROJECT_DRAFT_EMPTY_FILTER]: {
|
||||
key: EmptyStateType.PROJECT_DRAFT_EMPTY_FILTER,
|
||||
|
|
@ -381,7 +380,7 @@ const emptyStateDetails = {
|
|||
text: "Clear all filters",
|
||||
},
|
||||
accessType: "project",
|
||||
access: EUserProjectRoles.MEMBER,
|
||||
access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||
},
|
||||
// project issues
|
||||
[EmptyStateType.PROJECT_NO_ISSUES]: {
|
||||
|
|
@ -399,7 +398,7 @@ const emptyStateDetails = {
|
|||
},
|
||||
},
|
||||
accessType: "project",
|
||||
access: EUserProjectRoles.MEMBER,
|
||||
access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||
},
|
||||
[EmptyStateType.PROJECT_ARCHIVED_NO_ISSUES]: {
|
||||
key: EmptyStateType.PROJECT_ARCHIVED_NO_ISSUES,
|
||||
|
|
@ -411,7 +410,7 @@ const emptyStateDetails = {
|
|||
text: "Set automation",
|
||||
},
|
||||
accessType: "project",
|
||||
access: EUserProjectRoles.MEMBER,
|
||||
access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||
},
|
||||
[EmptyStateType.PROJECT_DRAFT_NO_ISSUES]: {
|
||||
key: EmptyStateType.PROJECT_DRAFT_NO_ISSUES,
|
||||
|
|
@ -451,7 +450,7 @@ const emptyStateDetails = {
|
|||
text: "Add an existing issue",
|
||||
},
|
||||
accessType: "project",
|
||||
access: EUserProjectRoles.MEMBER,
|
||||
access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||
},
|
||||
[EmptyStateType.PROJECT_MODULE]: {
|
||||
key: EmptyStateType.PROJECT_MODULE,
|
||||
|
|
@ -467,7 +466,7 @@ const emptyStateDetails = {
|
|||
},
|
||||
},
|
||||
accessType: "project",
|
||||
access: EUserProjectRoles.MEMBER,
|
||||
access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||
},
|
||||
[EmptyStateType.PROJECT_ARCHIVED_NO_MODULES]: {
|
||||
key: EmptyStateType.PROJECT_ARCHIVED_NO_MODULES,
|
||||
|
|
@ -490,7 +489,7 @@ const emptyStateDetails = {
|
|||
},
|
||||
},
|
||||
accessType: "project",
|
||||
access: EUserProjectRoles.GUEST,
|
||||
access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER, EUserPermissions.GUEST],
|
||||
},
|
||||
// project pages
|
||||
[EmptyStateType.PROJECT_PAGE]: {
|
||||
|
|
@ -503,7 +502,7 @@ const emptyStateDetails = {
|
|||
text: "Create your first page",
|
||||
},
|
||||
accessType: "project",
|
||||
access: EUserProjectRoles.MEMBER,
|
||||
access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER, EUserPermissions.GUEST],
|
||||
},
|
||||
[EmptyStateType.PROJECT_PAGE_PRIVATE]: {
|
||||
key: EmptyStateType.PROJECT_PAGE_PRIVATE,
|
||||
|
|
@ -514,7 +513,7 @@ const emptyStateDetails = {
|
|||
text: "Create your first page",
|
||||
},
|
||||
accessType: "project",
|
||||
access: EUserProjectRoles.MEMBER,
|
||||
access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER, EUserPermissions.GUEST],
|
||||
},
|
||||
[EmptyStateType.PROJECT_PAGE_PUBLIC]: {
|
||||
key: EmptyStateType.PROJECT_PAGE_PUBLIC,
|
||||
|
|
@ -525,7 +524,7 @@ const emptyStateDetails = {
|
|||
text: "Create your first page",
|
||||
},
|
||||
accessType: "project",
|
||||
access: EUserProjectRoles.MEMBER,
|
||||
access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER, EUserPermissions.GUEST],
|
||||
},
|
||||
[EmptyStateType.PROJECT_PAGE_ARCHIVED]: {
|
||||
key: EmptyStateType.PROJECT_PAGE_ARCHIVED,
|
||||
|
|
@ -543,7 +542,7 @@ const emptyStateDetails = {
|
|||
text: "Create your first page",
|
||||
},
|
||||
accessType: "workspace",
|
||||
access: EUserProjectRoles.GUEST,
|
||||
access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER, EUserPermissions.GUEST],
|
||||
},
|
||||
[EmptyStateType.WORKSPACE_PAGE_PRIVATE]: {
|
||||
key: EmptyStateType.WORKSPACE_PAGE_PRIVATE,
|
||||
|
|
@ -554,7 +553,7 @@ const emptyStateDetails = {
|
|||
text: "Create your first page",
|
||||
},
|
||||
accessType: "workspace",
|
||||
access: EUserProjectRoles.GUEST,
|
||||
access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER, EUserPermissions.GUEST],
|
||||
},
|
||||
[EmptyStateType.WORKSPACE_PAGE_PUBLIC]: {
|
||||
key: EmptyStateType.WORKSPACE_PAGE_PUBLIC,
|
||||
|
|
@ -565,7 +564,7 @@ const emptyStateDetails = {
|
|||
text: "Create your first page",
|
||||
},
|
||||
accessType: "workspace",
|
||||
access: EUserProjectRoles.GUEST,
|
||||
access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER, EUserPermissions.GUEST],
|
||||
},
|
||||
[EmptyStateType.WORKSPACE_PAGE_ARCHIVED]: {
|
||||
key: EmptyStateType.WORKSPACE_PAGE_ARCHIVED,
|
||||
|
|
@ -681,7 +680,7 @@ const emptyStateDetails = {
|
|||
description:
|
||||
"Intake helps you manage incoming requests to your project and add them as issues in your workflow. Enable intake \n from project settings to manage requests.",
|
||||
accessType: "project",
|
||||
access: EUserProjectRoles.ADMIN,
|
||||
access: [EUserPermissions.ADMIN],
|
||||
path: "/empty-state/disabled-feature/intake",
|
||||
primaryButton: {
|
||||
text: "Manage features",
|
||||
|
|
@ -693,7 +692,7 @@ const emptyStateDetails = {
|
|||
description:
|
||||
"Break work down by timeboxed chunks, work backwards from your project deadline to set dates, and make tangible progress as a team. Enable the cycles feature for your project to start using them.",
|
||||
accessType: "project",
|
||||
access: EUserProjectRoles.ADMIN,
|
||||
access: [EUserPermissions.ADMIN],
|
||||
path: "/empty-state/disabled-feature/cycles",
|
||||
primaryButton: {
|
||||
text: "Manage features",
|
||||
|
|
@ -705,7 +704,7 @@ const emptyStateDetails = {
|
|||
description:
|
||||
"A group of issues that belong to a logical, hierarchical parent form a module. Think of them as a way to track work by project milestones. Enable modules from project settings.",
|
||||
accessType: "project",
|
||||
access: EUserProjectRoles.ADMIN,
|
||||
access: [EUserPermissions.ADMIN],
|
||||
path: "/empty-state/disabled-feature/modules",
|
||||
primaryButton: {
|
||||
text: "Manage features",
|
||||
|
|
@ -717,7 +716,7 @@ const emptyStateDetails = {
|
|||
description:
|
||||
"Pages are thought spotting space in Plane. Take down meeting notes, format them easily, embed issues, lay them out using a library of components, and keep them all in your project’s context. Enable the pages feature to start creating them in your project.",
|
||||
accessType: "project",
|
||||
access: EUserProjectRoles.ADMIN,
|
||||
access: [EUserPermissions.ADMIN],
|
||||
path: "/empty-state/disabled-feature/pages",
|
||||
primaryButton: {
|
||||
text: "Manage features",
|
||||
|
|
@ -729,7 +728,7 @@ const emptyStateDetails = {
|
|||
description:
|
||||
"Views are a set of saved filters that you use frequently or want easy access to. All your colleagues in a project can see everyone’s views and choose whichever suits their needs best. Enable views in the project settings to start using them.",
|
||||
accessType: "project",
|
||||
access: EUserProjectRoles.ADMIN,
|
||||
access: [EUserPermissions.ADMIN],
|
||||
path: "/empty-state/disabled-feature/views",
|
||||
primaryButton: {
|
||||
text: "Manage features",
|
||||
|
|
|
|||
|
|
@ -2,13 +2,6 @@
|
|||
import { Globe2, Lock, LucideIcon } from "lucide-react";
|
||||
import { TProjectAppliedDisplayFilterKeys, TProjectOrderByOptions } from "@plane/types";
|
||||
|
||||
export enum EUserProjectRoles {
|
||||
GUEST = 5,
|
||||
VIEWER = 10,
|
||||
MEMBER = 15,
|
||||
ADMIN = 20,
|
||||
}
|
||||
|
||||
export const NETWORK_CHOICES: {
|
||||
key: 0 | 2;
|
||||
label: string;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
// types
|
||||
import { TStaticViewTypes } from "@plane/types";
|
||||
import { EUserPermissions } from "@/plane-web/constants/user-permissions";
|
||||
// services images
|
||||
import CSVLogo from "@/public/services/csv.svg";
|
||||
import ExcelLogo from "@/public/services/excel.svg";
|
||||
|
|
@ -7,34 +8,22 @@ import GithubLogo from "@/public/services/github.png";
|
|||
import JiraLogo from "@/public/services/jira.svg";
|
||||
import JSONLogo from "@/public/services/json.svg";
|
||||
|
||||
export enum EUserWorkspaceRoles {
|
||||
GUEST = 5,
|
||||
VIEWER = 10,
|
||||
MEMBER = 15,
|
||||
ADMIN = 20,
|
||||
}
|
||||
|
||||
export const ROLE = {
|
||||
5: "Guest",
|
||||
10: "Viewer",
|
||||
15: "Member",
|
||||
20: "Admin",
|
||||
[EUserPermissions.GUEST]: "Guest",
|
||||
[EUserPermissions.MEMBER]: "Member",
|
||||
[EUserPermissions.ADMIN]: "Admin",
|
||||
};
|
||||
|
||||
export const ROLE_DETAILS = {
|
||||
5: {
|
||||
[EUserPermissions.GUEST]: {
|
||||
title: "Guest",
|
||||
description: "External members of organizations can be invited as guests.",
|
||||
},
|
||||
10: {
|
||||
title: "Viewer",
|
||||
description: "External members of organizations can be invited as guests.",
|
||||
},
|
||||
15: {
|
||||
[EUserPermissions.MEMBER]: {
|
||||
title: "Member",
|
||||
description: "Ability to read, write, edit, and delete entities inside projects, cycles, and modules",
|
||||
},
|
||||
20: {
|
||||
[EUserPermissions.ADMIN]: {
|
||||
title: "Admin",
|
||||
description: "All permissions set to true within the workspace.",
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
export * from "./user-user";
|
||||
export * from "./user-user-profile";
|
||||
export * from "./user-user-settings";
|
||||
export * from "./user-permissions";
|
||||
|
|
|
|||
12
web/core/hooks/store/user/user-permissions.ts
Normal file
12
web/core/hooks/store/user/user-permissions.ts
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
import { useContext } from "react";
|
||||
// mobx store
|
||||
import { StoreContext } from "@/lib/store-context";
|
||||
// types
|
||||
import { IUserPermissionStore } from "@/store/user/permissions.store";
|
||||
|
||||
export const useUserPermissions = (): IUserPermissionStore => {
|
||||
const context = useContext(StoreContext);
|
||||
if (context === undefined) throw new Error("useUserPermissions must be used within StoreProvider");
|
||||
|
||||
return context.user.permission;
|
||||
};
|
||||
|
|
@ -16,9 +16,11 @@ import {
|
|||
useProject,
|
||||
useProjectState,
|
||||
useProjectView,
|
||||
useUser,
|
||||
useCommandPalette,
|
||||
useUserPermissions,
|
||||
} from "@/hooks/store";
|
||||
// plane web constants
|
||||
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||
// images
|
||||
import emptyProject from "@/public/empty-state/onboarding/dashboard-light.webp";
|
||||
|
||||
|
|
@ -32,9 +34,7 @@ export const ProjectAuthWrapper: FC<IProjectAuthWrapper> = observer((props) => {
|
|||
// const { fetchInboxes } = useInbox();
|
||||
const { toggleCreateProjectModal } = useCommandPalette();
|
||||
const { setTrackElement } = useEventTracker();
|
||||
const {
|
||||
membership: { fetchUserProjectInfo, projectMemberInfo, hasPermissionToProject },
|
||||
} = useUser();
|
||||
const { fetchUserProjectInfo, allowPermissions, projectUserInfo } = useUserPermissions();
|
||||
const { loader, getProjectById, fetchProjectDetails } = useProject();
|
||||
const { fetchAllCycles } = useCycle();
|
||||
const { fetchModules } = useModule();
|
||||
|
|
@ -48,6 +48,8 @@ export const ProjectAuthWrapper: FC<IProjectAuthWrapper> = observer((props) => {
|
|||
// router
|
||||
const { workspaceSlug, projectId } = useParams();
|
||||
|
||||
const projectMemberInfo = projectUserInfo?.[workspaceSlug.toString()]?.[projectId.toString()];
|
||||
|
||||
// fetching project details
|
||||
useSWR(
|
||||
workspaceSlug && projectId ? `PROJECT_DETAILS_${workspaceSlug.toString()}_${projectId.toString()}` : null,
|
||||
|
|
@ -55,7 +57,7 @@ export const ProjectAuthWrapper: FC<IProjectAuthWrapper> = observer((props) => {
|
|||
);
|
||||
// fetching user project member information
|
||||
useSWR(
|
||||
workspaceSlug && projectId ? `PROJECT_MEMBERS_ME_${workspaceSlug}_${projectId}` : null,
|
||||
workspaceSlug && projectId ? `PROJECT_ME_INFORMATION_${workspaceSlug}_${projectId}` : null,
|
||||
workspaceSlug && projectId ? () => fetchUserProjectInfo(workspaceSlug.toString(), projectId.toString()) : null
|
||||
);
|
||||
// fetching project labels
|
||||
|
|
@ -100,10 +102,18 @@ export const ProjectAuthWrapper: FC<IProjectAuthWrapper> = observer((props) => {
|
|||
workspaceSlug && projectId ? () => fetchViews(workspaceSlug.toString(), projectId.toString()) : null,
|
||||
{ revalidateIfStale: false, revalidateOnFocus: false }
|
||||
);
|
||||
|
||||
// derived values
|
||||
const projectExists = projectId ? getProjectById(projectId.toString()) : null;
|
||||
const hasPermissionToCurrentProject = projectId
|
||||
? allowPermissions(
|
||||
[EUserPermissions.ADMIN, EUserPermissions.MEMBER, EUserPermissions.GUEST],
|
||||
EUserPermissionsLevel.PROJECT
|
||||
)
|
||||
: undefined;
|
||||
|
||||
// check if the project member apis is loading
|
||||
if (!projectMemberInfo && projectId && hasPermissionToProject[projectId.toString()] === null)
|
||||
if (!projectMemberInfo && projectId && hasPermissionToCurrentProject === null)
|
||||
return (
|
||||
<div className="grid h-screen place-items-center bg-custom-background-100 p-4">
|
||||
<div className="flex flex-col items-center gap-3 text-center">
|
||||
|
|
@ -113,10 +123,10 @@ export const ProjectAuthWrapper: FC<IProjectAuthWrapper> = observer((props) => {
|
|||
);
|
||||
|
||||
// check if the user don't have permission to access the project
|
||||
if (projectExists && projectId && hasPermissionToProject[projectId.toString()] === false) return <JoinProject />;
|
||||
if (projectExists && projectId && hasPermissionToCurrentProject === false) return <JoinProject />;
|
||||
|
||||
// check if the project info is not found.
|
||||
if (!loader && !projectExists && projectId && !!hasPermissionToProject[projectId.toString()] === false)
|
||||
if (!loader && !projectExists && projectId && !!hasPermissionToCurrentProject === false)
|
||||
return (
|
||||
<div className="container grid h-screen place-items-center bg-custom-background-100">
|
||||
<EmptyState
|
||||
|
|
|
|||
|
|
@ -11,9 +11,11 @@ import { LogOut } from "lucide-react";
|
|||
// hooks
|
||||
import { Button, TOAST_TYPE, setToast, Tooltip } from "@plane/ui";
|
||||
import { LogoSpinner } from "@/components/common";
|
||||
import { useMember, useProject, useUser, useWorkspace } from "@/hooks/store";
|
||||
import { useMember, useProject, useUser, useUserPermissions, useWorkspace } from "@/hooks/store";
|
||||
import { useFavorite } from "@/hooks/store/use-favorite";
|
||||
import { usePlatformOS } from "@/hooks/use-platform-os";
|
||||
// constants
|
||||
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
|
||||
// images
|
||||
import PlaneBlackLogo from "@/public/plane-logos/black-horizontal-with-blue-logo.png";
|
||||
import PlaneWhiteLogo from "@/public/plane-logos/white-horizontal-with-blue-logo.png";
|
||||
|
|
@ -30,7 +32,7 @@ export const WorkspaceAuthWrapper: FC<IWorkspaceAuthWrapper> = observer((props)
|
|||
// next themes
|
||||
const { resolvedTheme } = useTheme();
|
||||
// store hooks
|
||||
const { membership, signOut, data: currentUser, canPerformWorkspaceMemberActions } = useUser();
|
||||
const { signOut, data: currentUser } = useUser();
|
||||
const { fetchProjects } = useProject();
|
||||
const { fetchFavorite } = useFavorite();
|
||||
const {
|
||||
|
|
@ -38,6 +40,13 @@ export const WorkspaceAuthWrapper: FC<IWorkspaceAuthWrapper> = observer((props)
|
|||
} = useMember();
|
||||
const { workspaces } = useWorkspace();
|
||||
const { isMobile } = usePlatformOS();
|
||||
const { workspaceInfoBySlug, fetchUserWorkspaceInfo, fetchUserProjectPermissions, allowPermissions } =
|
||||
useUserPermissions();
|
||||
// derived values
|
||||
const canPerformWorkspaceMemberActions = allowPermissions(
|
||||
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||
EUserPermissionsLevel.WORKSPACE
|
||||
);
|
||||
|
||||
const planeLogo = resolvedTheme === "dark" ? PlaneWhiteLogo : PlaneBlackLogo;
|
||||
const allWorkspaces = workspaces ? Object.values(workspaces) : undefined;
|
||||
|
|
@ -46,10 +55,16 @@ export const WorkspaceAuthWrapper: FC<IWorkspaceAuthWrapper> = observer((props)
|
|||
|
||||
// fetching user workspace information
|
||||
useSWR(
|
||||
workspaceSlug && currentWorkspace ? `WORKSPACE_MEMBERS_ME_${workspaceSlug}` : null,
|
||||
workspaceSlug && currentWorkspace ? () => membership.fetchUserWorkspaceInfo(workspaceSlug.toString()) : null,
|
||||
workspaceSlug && currentWorkspace ? `WORKSPACE_MEMBER_ME_INFORMATION_${workspaceSlug}` : null,
|
||||
workspaceSlug && currentWorkspace ? () => fetchUserWorkspaceInfo(workspaceSlug.toString()) : null,
|
||||
{ revalidateIfStale: false, revalidateOnFocus: false }
|
||||
);
|
||||
useSWR(
|
||||
workspaceSlug && currentWorkspace ? `WORKSPACE_PROJECTS_ROLES_INFORMATION_${workspaceSlug}` : null,
|
||||
workspaceSlug && currentWorkspace ? () => fetchUserProjectPermissions(workspaceSlug.toString()) : null,
|
||||
{ revalidateIfStale: false, revalidateOnFocus: false }
|
||||
);
|
||||
|
||||
// fetching workspace projects
|
||||
useSWR(
|
||||
workspaceSlug && currentWorkspace ? `WORKSPACE_PROJECTS_${workspaceSlug}` : null,
|
||||
|
|
@ -62,14 +77,6 @@ export const WorkspaceAuthWrapper: FC<IWorkspaceAuthWrapper> = observer((props)
|
|||
workspaceSlug && currentWorkspace ? () => fetchWorkspaceMembers(workspaceSlug.toString()) : null,
|
||||
{ revalidateIfStale: false, revalidateOnFocus: false }
|
||||
);
|
||||
// fetch workspace user projects role
|
||||
useSWR(
|
||||
workspaceSlug && currentWorkspace ? `WORKSPACE_PROJECTS_ROLE_${workspaceSlug}` : null,
|
||||
workspaceSlug && currentWorkspace
|
||||
? () => membership.fetchUserWorkspaceProjectsRole(workspaceSlug.toString())
|
||||
: null,
|
||||
{ revalidateIfStale: false, revalidateOnFocus: false }
|
||||
);
|
||||
// fetch workspace favorite
|
||||
useSWR(
|
||||
workspaceSlug && currentWorkspace && canPerformWorkspaceMemberActions
|
||||
|
|
@ -91,6 +98,9 @@ export const WorkspaceAuthWrapper: FC<IWorkspaceAuthWrapper> = observer((props)
|
|||
);
|
||||
};
|
||||
|
||||
// derived values
|
||||
const currentWorkspaceInfo = workspaceSlug && workspaceInfoBySlug(workspaceSlug.toString());
|
||||
|
||||
// if list of workspaces are not there then we have to render the spinner
|
||||
if (allWorkspaces === undefined) {
|
||||
return (
|
||||
|
|
@ -103,11 +113,7 @@ export const WorkspaceAuthWrapper: FC<IWorkspaceAuthWrapper> = observer((props)
|
|||
}
|
||||
|
||||
// if workspaces are there and we are trying to access the workspace that we are not part of then show the existing workspaces
|
||||
if (
|
||||
currentWorkspace === undefined &&
|
||||
!membership.currentWorkspaceMemberInfo &&
|
||||
membership.hasPermissionToCurrentWorkspace === undefined
|
||||
) {
|
||||
if (currentWorkspace === undefined && !currentWorkspaceInfo) {
|
||||
return (
|
||||
<div className="relative flex h-screen w-full flex-col items-center justify-center bg-custom-background-90 ">
|
||||
<div className="container relative mx-auto flex h-full w-full flex-col overflow-hidden overflow-y-auto px-5 py-14 md:px-0">
|
||||
|
|
@ -154,10 +160,7 @@ export const WorkspaceAuthWrapper: FC<IWorkspaceAuthWrapper> = observer((props)
|
|||
}
|
||||
|
||||
// while user does not have access to view that workspace
|
||||
if (
|
||||
membership.hasPermissionToCurrentWorkspace !== undefined &&
|
||||
membership.hasPermissionToCurrentWorkspace === false
|
||||
) {
|
||||
if (currentWorkspaceInfo === undefined) {
|
||||
return (
|
||||
<div className={`h-screen w-full overflow-hidden bg-custom-background-100`}>
|
||||
<div className="grid h-full place-items-center p-4">
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
import { FC, ReactNode, useEffect } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import dynamic from "next/dynamic";
|
||||
import { useParams } from "next/navigation";
|
||||
import posthog from "posthog-js";
|
||||
import { PostHogProvider as PHProvider } from "posthog-js/react";
|
||||
// constants
|
||||
|
|
@ -10,7 +11,7 @@ import { GROUP_WORKSPACE } from "@/constants/event-tracker";
|
|||
// helpers
|
||||
import { getUserRole } from "@/helpers/user.helper";
|
||||
// hooks
|
||||
import { useWorkspace, useUser, useInstance } from "@/hooks/store";
|
||||
import { useWorkspace, useUser, useInstance, useUserPermissions } from "@/hooks/store";
|
||||
// dynamic imports
|
||||
const PostHogPageView = dynamic(() => import("@/lib/posthog-view"), { ssr: false });
|
||||
|
||||
|
|
@ -20,13 +21,14 @@ export interface IPosthogWrapper {
|
|||
|
||||
const PostHogProvider: FC<IPosthogWrapper> = observer((props) => {
|
||||
const { children } = props;
|
||||
const {
|
||||
data: user,
|
||||
membership: { currentProjectRole, currentWorkspaceRole },
|
||||
} = useUser();
|
||||
const { data: user } = useUser();
|
||||
const { currentWorkspace } = useWorkspace();
|
||||
const { instance } = useInstance();
|
||||
const { workspaceSlug, projectId } = useParams();
|
||||
const { workspaceInfoBySlug, projectUserInfo } = useUserPermissions();
|
||||
|
||||
const currentProjectRole = projectUserInfo?.[workspaceSlug?.toString()]?.[projectId?.toString()]?.role;
|
||||
const currentWorkspaceRole = workspaceInfoBySlug(workspaceSlug?.toString())?.role;
|
||||
const is_telemetry_enabled = instance?.is_telemetry_enabled || false;
|
||||
|
||||
useEffect(() => {
|
||||
|
|
|
|||
|
|
@ -66,3 +66,7 @@ export class ProjectMemberService extends APIService {
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
const projectMemberService = new ProjectMemberService();
|
||||
|
||||
export default projectMemberService;
|
||||
|
|
|
|||
|
|
@ -258,3 +258,7 @@ export class UserService extends APIService {
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
const userService = new UserService();
|
||||
|
||||
export default userService;
|
||||
|
|
|
|||
|
|
@ -10,8 +10,8 @@ import {
|
|||
IProjectMembership,
|
||||
IUserLite,
|
||||
} from "@plane/types";
|
||||
// constants
|
||||
import { EUserProjectRoles } from "@/constants/project";
|
||||
// plane-web constants
|
||||
import { EUserPermissions } from "@/plane-web/constants/user-permissions";
|
||||
// services
|
||||
import { ProjectMemberService } from "@/services/project";
|
||||
// store
|
||||
|
|
@ -25,7 +25,7 @@ import { IMemberRootStore } from ".";
|
|||
export interface IProjectMemberDetails {
|
||||
id: string;
|
||||
member: IUserLite;
|
||||
role: EUserProjectRoles;
|
||||
role: EUserPermissions;
|
||||
}
|
||||
|
||||
export interface IProjectMemberStore {
|
||||
|
|
@ -51,7 +51,7 @@ export interface IProjectMemberStore {
|
|||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
userId: string,
|
||||
data: { role: EUserProjectRoles }
|
||||
data: { role: EUserPermissions }
|
||||
) => Promise<IProjectMember>;
|
||||
removeMemberFromProject: (workspaceSlug: string, projectId: string, userId: string) => Promise<void>;
|
||||
}
|
||||
|
|
@ -182,12 +182,7 @@ export class ProjectMemberStore implements IProjectMemberStore {
|
|||
* @param userId
|
||||
* @param data
|
||||
*/
|
||||
updateMember = async (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
userId: string,
|
||||
data: { role: EUserProjectRoles }
|
||||
) => {
|
||||
updateMember = async (workspaceSlug: string, projectId: string, userId: string, data: { role: EUserPermissions }) => {
|
||||
const memberDetails = this.getProjectMemberDetails(userId);
|
||||
if (!memberDetails) throw new Error("Member not found");
|
||||
// original data to revert back in case of error
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ import { action, computed, makeObservable, observable, runInAction } from "mobx"
|
|||
import { computedFn } from "mobx-utils";
|
||||
// types
|
||||
import { IWorkspaceBulkInviteFormData, IWorkspaceMember, IWorkspaceMemberInvitation } from "@plane/types";
|
||||
// constants
|
||||
import { EUserWorkspaceRoles } from "@/constants/workspace";
|
||||
// plane-web constants
|
||||
import { EUserPermissions } from "@/plane-web/constants/user-permissions";
|
||||
// services
|
||||
import { WorkspaceService } from "@/plane-web/services";
|
||||
// types
|
||||
|
|
@ -18,7 +18,7 @@ import { IMemberRootStore } from ".";
|
|||
export interface IWorkspaceMembership {
|
||||
id: string;
|
||||
member: string;
|
||||
role: EUserWorkspaceRoles;
|
||||
role: EUserPermissions;
|
||||
}
|
||||
|
||||
export interface IWorkspaceMemberStore {
|
||||
|
|
@ -38,7 +38,7 @@ export interface IWorkspaceMemberStore {
|
|||
fetchWorkspaceMembers: (workspaceSlug: string) => Promise<IWorkspaceMember[]>;
|
||||
fetchWorkspaceMemberInvitations: (workspaceSlug: string) => Promise<IWorkspaceMemberInvitation[]>;
|
||||
// crud actions
|
||||
updateMember: (workspaceSlug: string, userId: string, data: { role: EUserWorkspaceRoles }) => Promise<void>;
|
||||
updateMember: (workspaceSlug: string, userId: string, data: { role: EUserPermissions }) => Promise<void>;
|
||||
removeMemberFromWorkspace: (workspaceSlug: string, userId: string) => Promise<void>;
|
||||
// invite actions
|
||||
inviteMembersToWorkspace: (workspaceSlug: string, data: IWorkspaceBulkInviteFormData) => Promise<void>;
|
||||
|
|
@ -213,7 +213,7 @@ export class WorkspaceMemberStore implements IWorkspaceMemberStore {
|
|||
* @param userId
|
||||
* @param data
|
||||
*/
|
||||
updateMember = async (workspaceSlug: string, userId: string, data: { role: EUserWorkspaceRoles }) => {
|
||||
updateMember = async (workspaceSlug: string, userId: string, data: { role: EUserPermissions }) => {
|
||||
const memberDetails = this.getWorkspaceMemberDetails(userId);
|
||||
if (!memberDetails) throw new Error("Member not found");
|
||||
// original data to revert back in case of error
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { action, computed, makeObservable, observable, reaction, runInAction } f
|
|||
import { TLogoProps, TPage } from "@plane/types";
|
||||
// constants
|
||||
import { EPageAccess } from "@/constants/page";
|
||||
import { EUserProjectRoles } from "@/constants/project";
|
||||
import { EUserPermissions } from "@/plane-web/constants/user-permissions";
|
||||
// services
|
||||
import { ProjectPageService } from "@/services/page";
|
||||
// store
|
||||
|
|
@ -214,24 +214,39 @@ export class Page implements IPage {
|
|||
* @description returns true if the current logged in user can edit the page
|
||||
*/
|
||||
get canCurrentUserEditPage() {
|
||||
const currentUserProjectRole = this.store.user.membership.currentProjectRole;
|
||||
return this.isCurrentUserOwner || (!!currentUserProjectRole && currentUserProjectRole >= EUserProjectRoles.MEMBER);
|
||||
const { workspaceSlug, projectId } = this.store.router;
|
||||
|
||||
const currentUserProjectRole = this.store.user.permission.projectPermissionsByWorkspaceSlugAndProjectId(
|
||||
workspaceSlug?.toString() || "",
|
||||
projectId?.toString() || ""
|
||||
);
|
||||
return this.isCurrentUserOwner || (!!currentUserProjectRole && currentUserProjectRole >= EUserPermissions.MEMBER);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description returns true if the current logged in user can create a duplicate the page
|
||||
*/
|
||||
get canCurrentUserDuplicatePage() {
|
||||
const currentUserProjectRole = this.store.user.membership.currentProjectRole;
|
||||
return this.isCurrentUserOwner || (!!currentUserProjectRole && currentUserProjectRole >= EUserProjectRoles.MEMBER);
|
||||
const { workspaceSlug, projectId } = this.store.router;
|
||||
|
||||
const currentUserProjectRole = this.store.user.permission.projectPermissionsByWorkspaceSlugAndProjectId(
|
||||
workspaceSlug?.toString() || "",
|
||||
projectId?.toString() || ""
|
||||
);
|
||||
return this.isCurrentUserOwner || (!!currentUserProjectRole && currentUserProjectRole >= EUserPermissions.MEMBER);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description returns true if the current logged in user can lock the page
|
||||
*/
|
||||
get canCurrentUserLockPage() {
|
||||
const currentUserProjectRole = this.store.user.membership.currentProjectRole;
|
||||
return this.isCurrentUserOwner || (!!currentUserProjectRole && currentUserProjectRole >= EUserProjectRoles.MEMBER);
|
||||
const { workspaceSlug, projectId } = this.store.router;
|
||||
|
||||
const currentUserProjectRole = this.store.user.permission.projectPermissionsByWorkspaceSlugAndProjectId(
|
||||
workspaceSlug?.toString() || "",
|
||||
projectId?.toString() || ""
|
||||
);
|
||||
return this.isCurrentUserOwner || (!!currentUserProjectRole && currentUserProjectRole >= EUserPermissions.MEMBER);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -245,32 +260,52 @@ export class Page implements IPage {
|
|||
* @description returns true if the current logged in user can archive the page
|
||||
*/
|
||||
get canCurrentUserArchivePage() {
|
||||
const currentUserProjectRole = this.store.user.membership.currentProjectRole;
|
||||
return this.isCurrentUserOwner || currentUserProjectRole === EUserProjectRoles.ADMIN;
|
||||
const { workspaceSlug, projectId } = this.store.router;
|
||||
|
||||
const currentUserProjectRole = this.store.user.permission.projectPermissionsByWorkspaceSlugAndProjectId(
|
||||
workspaceSlug?.toString() || "",
|
||||
projectId?.toString() || ""
|
||||
);
|
||||
return this.isCurrentUserOwner || currentUserProjectRole === EUserPermissions.ADMIN;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description returns true if the current logged in user can delete the page
|
||||
*/
|
||||
get canCurrentUserDeletePage() {
|
||||
const currentUserProjectRole = this.store.user.membership.currentProjectRole;
|
||||
return this.isCurrentUserOwner || currentUserProjectRole === EUserProjectRoles.ADMIN;
|
||||
const { workspaceSlug, projectId } = this.store.router;
|
||||
|
||||
const currentUserProjectRole = this.store.user.permission.projectPermissionsByWorkspaceSlugAndProjectId(
|
||||
workspaceSlug?.toString() || "",
|
||||
projectId?.toString() || ""
|
||||
);
|
||||
return this.isCurrentUserOwner || currentUserProjectRole === EUserPermissions.ADMIN;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description returns true if the current logged in user can favorite the page
|
||||
*/
|
||||
get canCurrentUserFavoritePage() {
|
||||
const currentUserProjectRole = this.store.user.membership.currentProjectRole;
|
||||
return !!currentUserProjectRole && currentUserProjectRole >= EUserProjectRoles.MEMBER;
|
||||
const { workspaceSlug, projectId } = this.store.router;
|
||||
|
||||
const currentUserProjectRole = this.store.user.permission.projectPermissionsByWorkspaceSlugAndProjectId(
|
||||
workspaceSlug?.toString() || "",
|
||||
projectId?.toString() || ""
|
||||
);
|
||||
return !!currentUserProjectRole && currentUserProjectRole >= EUserPermissions.MEMBER;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description returns true if the page can be edited
|
||||
*/
|
||||
get isContentEditable() {
|
||||
const { workspaceSlug, projectId } = this.store.router;
|
||||
|
||||
const isOwner = this.isCurrentUserOwner;
|
||||
const currentUserRole = this.store.user.membership.currentProjectRole;
|
||||
const currentUserRole = this.store.user.permission.projectPermissionsByWorkspaceSlugAndProjectId(
|
||||
workspaceSlug?.toString() || "",
|
||||
projectId?.toString() || ""
|
||||
);
|
||||
const isPublic = this.access === EPageAccess.PUBLIC;
|
||||
const isArchived = this.archived_at;
|
||||
const isLocked = this.is_locked;
|
||||
|
|
@ -278,7 +313,7 @@ export class Page implements IPage {
|
|||
return (
|
||||
!isArchived &&
|
||||
!isLocked &&
|
||||
(isOwner || (isPublic && !!currentUserRole && currentUserRole >= EUserProjectRoles.MEMBER))
|
||||
(isOwner || (isPublic && !!currentUserRole && currentUserRole >= EUserPermissions.MEMBER))
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -354,7 +354,12 @@ export class ProjectStore implements IProjectStore {
|
|||
const response = await this.projectService.createProject(workspaceSlug, data);
|
||||
runInAction(() => {
|
||||
set(this.projectMap, [response.id], response);
|
||||
set(this.rootStore.user.membership.workspaceProjectsRole, [workspaceSlug, response.id], response.member_role);
|
||||
// updating the user project role in workspaceProjectsPermissions
|
||||
set(
|
||||
this.rootStore.user.permission.workspaceProjectsPermissions,
|
||||
[workspaceSlug, response.id],
|
||||
response.member_role
|
||||
);
|
||||
});
|
||||
return response;
|
||||
} catch (error) {
|
||||
|
|
|
|||
|
|
@ -4,10 +4,9 @@ import { action, makeObservable, observable, runInAction, computed } from "mobx"
|
|||
// types
|
||||
import { IUser } from "@plane/types";
|
||||
// constants
|
||||
import { EUserProjectRoles } from "@/constants/project";
|
||||
import { EUserWorkspaceRoles } from "@/constants/workspace";
|
||||
// helpers
|
||||
import { API_BASE_URL } from "@/helpers/common.helper";
|
||||
import { EUserPermissions } from "@/plane-web/constants/user-permissions";
|
||||
// services
|
||||
import { AuthService } from "@/services/auth.service";
|
||||
import { UserService } from "@/services/user.service";
|
||||
|
|
@ -15,8 +14,9 @@ import { UserService } from "@/services/user.service";
|
|||
import { CoreRootStore } from "@/store/root.store";
|
||||
import { IAccountStore } from "@/store/user/account.store";
|
||||
import { ProfileStore, IUserProfileStore } from "@/store/user/profile.store";
|
||||
import { IUserMembershipStore, UserMembershipStore } from "@/store/user/user-membership.store";
|
||||
import { IUserPermissionStore, UserPermissionStore } from "./permissions.store";
|
||||
import { IUserSettingsStore, UserSettingsStore } from "./settings.store";
|
||||
import { TUserPermissions } from "@plane/types/src/enums";
|
||||
|
||||
type TUserErrorStatus = {
|
||||
status: string;
|
||||
|
|
@ -33,7 +33,7 @@ export interface IUserStore {
|
|||
userProfile: IUserProfileStore;
|
||||
userSettings: IUserSettingsStore;
|
||||
accounts: Record<string, IAccountStore>;
|
||||
membership: IUserMembershipStore;
|
||||
permission: IUserPermissionStore;
|
||||
// actions
|
||||
fetchCurrentUser: () => Promise<IUser | undefined>;
|
||||
updateCurrentUser: (data: Partial<IUser>) => Promise<IUser | undefined>;
|
||||
|
|
@ -42,18 +42,6 @@ export interface IUserStore {
|
|||
reset: () => void;
|
||||
signOut: () => Promise<void>;
|
||||
// computed
|
||||
|
||||
// workspace level
|
||||
canPerformWorkspaceAdminActions: boolean;
|
||||
canPerformWorkspaceMemberActions: boolean;
|
||||
canPerformWorkspaceViewerActions: boolean;
|
||||
canPerformWorkspaceGuestActions: boolean;
|
||||
|
||||
// project level
|
||||
canPerformProjectAdminActions: boolean;
|
||||
canPerformProjectMemberActions: boolean;
|
||||
canPerformProjectViewerActions: boolean;
|
||||
canPerformProjectGuestActions: boolean;
|
||||
canPerformAnyCreateAction: boolean;
|
||||
projectsWithCreatePermissions: { [projectId: string]: number } | null;
|
||||
}
|
||||
|
|
@ -68,7 +56,7 @@ export class UserStore implements IUserStore {
|
|||
userProfile: IUserProfileStore;
|
||||
userSettings: IUserSettingsStore;
|
||||
accounts: Record<string, IAccountStore> = {};
|
||||
membership: IUserMembershipStore;
|
||||
permission: IUserPermissionStore;
|
||||
// service
|
||||
userService: UserService;
|
||||
authService: AuthService;
|
||||
|
|
@ -77,7 +65,7 @@ export class UserStore implements IUserStore {
|
|||
// stores
|
||||
this.userProfile = new ProfileStore(store);
|
||||
this.userSettings = new UserSettingsStore();
|
||||
this.membership = new UserMembershipStore(store);
|
||||
this.permission = new UserPermissionStore(store);
|
||||
// service
|
||||
this.userService = new UserService();
|
||||
this.authService = new AuthService();
|
||||
|
|
@ -92,7 +80,7 @@ export class UserStore implements IUserStore {
|
|||
userProfile: observable,
|
||||
userSettings: observable,
|
||||
accounts: observable,
|
||||
membership: observable,
|
||||
permission: observable,
|
||||
// actions
|
||||
fetchCurrentUser: action,
|
||||
updateCurrentUser: action,
|
||||
|
|
@ -101,16 +89,6 @@ export class UserStore implements IUserStore {
|
|||
reset: action,
|
||||
signOut: action,
|
||||
// computed
|
||||
canPerformWorkspaceAdminActions: computed,
|
||||
canPerformWorkspaceMemberActions: computed,
|
||||
canPerformWorkspaceViewerActions: computed,
|
||||
canPerformWorkspaceGuestActions: computed,
|
||||
|
||||
canPerformProjectAdminActions: computed,
|
||||
canPerformProjectMemberActions: computed,
|
||||
canPerformProjectViewerActions: computed,
|
||||
canPerformProjectGuestActions: computed,
|
||||
|
||||
canPerformAnyCreateAction: computed,
|
||||
projectsWithCreatePermissions: computed,
|
||||
});
|
||||
|
|
@ -158,26 +136,6 @@ export class UserStore implements IUserStore {
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @description fetches the prjects with write permissions
|
||||
* @returns {{[projectId: string]: number} || null}
|
||||
*/
|
||||
fetchProjectsWithCreatePermissions() {
|
||||
const allWorkspaceRoles =
|
||||
this.membership.workspaceProjectsRole &&
|
||||
this.membership.workspaceProjectsRole[this.membership.router.workspaceSlug || ""];
|
||||
return (
|
||||
(allWorkspaceRoles &&
|
||||
Object.keys(allWorkspaceRoles)
|
||||
.filter((key) => allWorkspaceRoles[key] >= EUserProjectRoles.MEMBER)
|
||||
.reduce(
|
||||
(res: { [projectId: string]: number }, key: string) => ((res[key] = allWorkspaceRoles[key]), res),
|
||||
{}
|
||||
)) ||
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description updates the current user
|
||||
* @param data
|
||||
|
|
@ -258,7 +216,7 @@ export class UserStore implements IUserStore {
|
|||
this.data = undefined;
|
||||
this.userProfile = new ProfileStore(this.store);
|
||||
this.userSettings = new UserSettingsStore();
|
||||
this.membership = new UserMembershipStore(this.store);
|
||||
this.permission = new UserPermissionStore(this.store);
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -271,6 +229,30 @@ export class UserStore implements IUserStore {
|
|||
this.store.resetOnSignOut();
|
||||
};
|
||||
|
||||
// helper actions
|
||||
/**
|
||||
* @description fetches the prjects with write permissions
|
||||
* @returns {{[projectId: string]: number} || null}
|
||||
*/
|
||||
fetchProjectsWithCreatePermissions = (): { [key: string]: TUserPermissions } => {
|
||||
const { workspaceSlug } = this.store.router;
|
||||
|
||||
const allWorkspaceProjectRoles =
|
||||
this.permission.workspaceProjectsPermissions && this.permission.workspaceProjectsPermissions[workspaceSlug || ""];
|
||||
|
||||
const userPermissions =
|
||||
(allWorkspaceProjectRoles &&
|
||||
Object.keys(allWorkspaceProjectRoles)
|
||||
.filter((key) => allWorkspaceProjectRoles[key] >= EUserPermissions.MEMBER)
|
||||
.reduce(
|
||||
(res: { [projectId: string]: number }, key: string) => ((res[key] = allWorkspaceProjectRoles[key]), res),
|
||||
{}
|
||||
)) ||
|
||||
null;
|
||||
|
||||
return userPermissions;
|
||||
};
|
||||
|
||||
/**
|
||||
* @description returns projects where user has permissions
|
||||
* @returns {{[projectId: string]: number} || null}
|
||||
|
|
@ -287,70 +269,4 @@ export class UserStore implements IUserStore {
|
|||
const filteredProjects = this.fetchProjectsWithCreatePermissions();
|
||||
return filteredProjects ? Object.keys(filteredProjects).length > 0 : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description returns true if user has workspace admin actions permissions
|
||||
* @returns {boolean}
|
||||
*/
|
||||
get canPerformWorkspaceAdminActions() {
|
||||
return !!this.membership.currentWorkspaceRole && this.membership.currentWorkspaceRole === EUserWorkspaceRoles.ADMIN;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description returns true if user has workspace member actions permissions
|
||||
* @returns {boolean}
|
||||
*/
|
||||
get canPerformWorkspaceMemberActions() {
|
||||
return !!this.membership.currentWorkspaceRole && this.membership.currentWorkspaceRole >= EUserWorkspaceRoles.MEMBER;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description returns true if user has workspace viewer actions permissions
|
||||
* @returns {boolean}
|
||||
*/
|
||||
|
||||
get canPerformWorkspaceViewerActions() {
|
||||
return !!this.membership.currentWorkspaceRole && this.membership.currentWorkspaceRole >= EUserWorkspaceRoles.VIEWER;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description returns true if user has workspace guest actions permissions
|
||||
* @returns {boolean}
|
||||
*/
|
||||
get canPerformWorkspaceGuestActions() {
|
||||
return !!this.membership.currentWorkspaceRole && this.membership.currentWorkspaceRole >= EUserWorkspaceRoles.GUEST;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description returns true if user has project admin actions permissions
|
||||
* @returns {boolean}
|
||||
*/
|
||||
get canPerformProjectAdminActions() {
|
||||
return !!this.membership.currentProjectRole && this.membership.currentProjectRole === EUserProjectRoles.ADMIN;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description returns true if user has project member actions permissions
|
||||
* @returns {boolean}
|
||||
*/
|
||||
get canPerformProjectMemberActions() {
|
||||
return !!this.membership.currentProjectRole && this.membership.currentProjectRole >= EUserProjectRoles.MEMBER;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description returns true if user has project viewer actions permissions
|
||||
* @returns {boolean}
|
||||
*/
|
||||
|
||||
get canPerformProjectViewerActions() {
|
||||
return !!this.membership.currentProjectRole && this.membership.currentProjectRole >= EUserProjectRoles.VIEWER;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description returns true if user has project guest actions permissions
|
||||
* @returns {boolean}
|
||||
*/
|
||||
get canPerformProjectGuestActions() {
|
||||
return !!this.membership.currentProjectRole && this.membership.currentProjectRole >= EUserProjectRoles.GUEST;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
270
web/core/store/user/permissions.store.ts
Normal file
270
web/core/store/user/permissions.store.ts
Normal file
|
|
@ -0,0 +1,270 @@
|
|||
import set from "lodash/set";
|
||||
import unset from "lodash/unset";
|
||||
import { action, makeObservable, observable, runInAction } from "mobx";
|
||||
import { computedFn } from "mobx-utils";
|
||||
// types
|
||||
import { IProjectMember, IUserProjectsRole, IWorkspaceMemberMe } from "@plane/types";
|
||||
// plane web types
|
||||
import {
|
||||
EUserPermissions,
|
||||
EUserPermissionsLevel,
|
||||
TUserPermissions,
|
||||
TUserPermissionsLevel,
|
||||
} from "@/plane-web/constants/user-permissions";
|
||||
// plane web services
|
||||
import { WorkspaceService } from "@/plane-web/services/workspace.service";
|
||||
// services
|
||||
import projectMemberService from "@/services/project/project-member.service";
|
||||
import userService from "@/services/user.service";
|
||||
// store
|
||||
import { CoreRootStore } from "@/store/root.store";
|
||||
|
||||
// derived services
|
||||
const workspaceService = new WorkspaceService();
|
||||
|
||||
export interface IUserPermissionStore {
|
||||
// observables
|
||||
workspaceUserInfo: Record<string, IWorkspaceMemberMe>; // workspaceSlug -> IWorkspaceMemberMe
|
||||
projectUserInfo: Record<string, Record<string, IProjectMember>>; // workspaceSlug -> projectId -> IProjectMember
|
||||
workspaceProjectsPermissions: Record<string, IUserProjectsRole>; // workspaceSlug -> IUserProjectsRole
|
||||
// computed
|
||||
// computed helpers
|
||||
workspaceInfoBySlug: (workspaceSlug: string) => IWorkspaceMemberMe | undefined;
|
||||
projectPermissionsByWorkspaceSlugAndProjectId: (
|
||||
workspaceSlug: string,
|
||||
projectId: string
|
||||
) => TUserPermissions | undefined;
|
||||
allowPermissions: (
|
||||
allowPermissions: TUserPermissions[],
|
||||
level: TUserPermissionsLevel,
|
||||
workspaceSlug?: string,
|
||||
projectId?: string,
|
||||
onPermissionAllowed?: () => boolean
|
||||
) => boolean;
|
||||
// action helpers
|
||||
// actions
|
||||
fetchUserWorkspaceInfo: (workspaceSlug: string) => Promise<IWorkspaceMemberMe | undefined>;
|
||||
leaveWorkspace: (workspaceSlug: string) => Promise<void>;
|
||||
fetchUserProjectInfo: (workspaceSlug: string, projectId: string) => Promise<IProjectMember | undefined>;
|
||||
fetchUserProjectPermissions: (workspaceSlug: string) => Promise<IUserProjectsRole | undefined>;
|
||||
joinProject: (workspaceSlug: string, projectId: string) => Promise<void | undefined>;
|
||||
leaveProject: (workspaceSlug: string, projectId: string) => Promise<void>;
|
||||
}
|
||||
|
||||
export class UserPermissionStore implements IUserPermissionStore {
|
||||
// constants
|
||||
workspaceUserInfo: Record<string, IWorkspaceMemberMe> = {};
|
||||
projectUserInfo: Record<string, Record<string, IProjectMember>> = {};
|
||||
workspaceProjectsPermissions: Record<string, IUserProjectsRole> = {};
|
||||
// observables
|
||||
|
||||
constructor(private store: CoreRootStore) {
|
||||
makeObservable(this, {
|
||||
// observables
|
||||
workspaceUserInfo: observable,
|
||||
projectUserInfo: observable,
|
||||
workspaceProjectsPermissions: observable,
|
||||
// computed
|
||||
// actions
|
||||
fetchUserWorkspaceInfo: action,
|
||||
leaveWorkspace: action,
|
||||
fetchUserProjectInfo: action,
|
||||
fetchUserProjectPermissions: action,
|
||||
joinProject: action,
|
||||
leaveProject: action,
|
||||
});
|
||||
}
|
||||
|
||||
// computed
|
||||
|
||||
// computed helpers
|
||||
/**
|
||||
* @description Returns the current workspace information
|
||||
* @param { string } workspaceSlug
|
||||
* @returns { IWorkspaceMemberMe | undefined }
|
||||
*/
|
||||
workspaceInfoBySlug = computedFn((workspaceSlug: string): IWorkspaceMemberMe | undefined => {
|
||||
if (!workspaceSlug) return undefined;
|
||||
return this.workspaceUserInfo[workspaceSlug] || undefined;
|
||||
});
|
||||
|
||||
/**
|
||||
* @description Returns the current project permissions
|
||||
* @param { string } workspaceSlug
|
||||
* @param { string } projectId
|
||||
* @returns { IUserProjectsRole | undefined }
|
||||
*/
|
||||
projectPermissionsByWorkspaceSlugAndProjectId = computedFn(
|
||||
(workspaceSlug: string, projectId: string): TUserPermissions | undefined => {
|
||||
if (!workspaceSlug || !projectId) return undefined;
|
||||
return this.workspaceProjectsPermissions?.[workspaceSlug]?.[projectId] || undefined;
|
||||
}
|
||||
);
|
||||
|
||||
// action helpers
|
||||
/**
|
||||
* @description Returns whether the user has the permission to perform an action
|
||||
* @param { TUserPermissions[] } allowPermissions
|
||||
* @param { TUserPermissionsLevel } level
|
||||
* @param { string } workspaceSlug
|
||||
* @param { string } projectId
|
||||
* @param { () => boolean } onPermissionAllowed
|
||||
* @returns { boolean }
|
||||
*/
|
||||
allowPermissions = (
|
||||
allowPermissions: TUserPermissions[],
|
||||
level: TUserPermissionsLevel,
|
||||
workspaceSlug?: string,
|
||||
projectId?: string,
|
||||
onPermissionAllowed?: () => boolean
|
||||
): boolean => {
|
||||
const { workspaceSlug: currentWorkspaceSlug, projectId: currentProjectId } = this.store.router;
|
||||
if (!workspaceSlug) workspaceSlug = currentWorkspaceSlug;
|
||||
if (!projectId) projectId = currentProjectId;
|
||||
|
||||
let currentUserRole: TUserPermissions | undefined = undefined;
|
||||
|
||||
if (level === EUserPermissionsLevel.WORKSPACE) {
|
||||
const workspaceInfoBySlug = workspaceSlug && this.workspaceInfoBySlug(workspaceSlug);
|
||||
if (workspaceInfoBySlug) {
|
||||
currentUserRole = workspaceInfoBySlug?.role as unknown as EUserPermissions;
|
||||
}
|
||||
}
|
||||
|
||||
if (level === EUserPermissionsLevel.PROJECT) {
|
||||
currentUserRole = (workspaceSlug &&
|
||||
projectId &&
|
||||
this.projectPermissionsByWorkspaceSlugAndProjectId(workspaceSlug, projectId)) as EUserPermissions | undefined;
|
||||
}
|
||||
|
||||
if (currentUserRole && allowPermissions.includes(currentUserRole)) {
|
||||
if (onPermissionAllowed) {
|
||||
return onPermissionAllowed();
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
// actions
|
||||
/**
|
||||
* @description Fetches the user's workspace information
|
||||
* @param { string } workspaceSlug
|
||||
* @returns { Promise<void | undefined> }
|
||||
*/
|
||||
fetchUserWorkspaceInfo = async (workspaceSlug: string): Promise<IWorkspaceMemberMe | undefined> => {
|
||||
try {
|
||||
const response = await workspaceService.workspaceMemberMe(workspaceSlug);
|
||||
if (response) {
|
||||
runInAction(() => {
|
||||
set(this.workspaceUserInfo, [workspaceSlug], response);
|
||||
});
|
||||
}
|
||||
return response;
|
||||
} catch (error) {
|
||||
console.error("Error fetching user workspace information", error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @description Leaves a workspace
|
||||
* @param { string } workspaceSlug
|
||||
* @returns { Promise<void | undefined> }
|
||||
*/
|
||||
leaveWorkspace = async (workspaceSlug: string): Promise<void> => {
|
||||
try {
|
||||
await userService.leaveWorkspace(workspaceSlug);
|
||||
runInAction(() => {
|
||||
unset(this.workspaceUserInfo, workspaceSlug);
|
||||
unset(this.projectUserInfo, workspaceSlug);
|
||||
unset(this.workspaceProjectsPermissions, workspaceSlug);
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error user leaving the workspace", error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @description Fetches the user's project information
|
||||
* @param { string } workspaceSlug
|
||||
* @param { string } projectId
|
||||
* @returns { Promise<void | undefined> }
|
||||
*/
|
||||
fetchUserProjectInfo = async (workspaceSlug: string, projectId: string): Promise<IProjectMember | undefined> => {
|
||||
try {
|
||||
const response = await projectMemberService.projectMemberMe(workspaceSlug, projectId);
|
||||
if (response) {
|
||||
runInAction(() => {
|
||||
set(this.projectUserInfo, [workspaceSlug, projectId], response);
|
||||
set(this.workspaceProjectsPermissions, [workspaceSlug, projectId], response.role);
|
||||
});
|
||||
}
|
||||
return response;
|
||||
} catch (error) {
|
||||
console.error("Error fetching user project information", error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @description Fetches the user's project permissions
|
||||
* @param { string } workspaceSlug
|
||||
* @returns { Promise<void | undefined> }
|
||||
*/
|
||||
fetchUserProjectPermissions = async (workspaceSlug: string): Promise<IUserProjectsRole | undefined> => {
|
||||
try {
|
||||
const response = await workspaceService.getWorkspaceUserProjectsRole(workspaceSlug);
|
||||
runInAction(() => {
|
||||
set(this.workspaceProjectsPermissions, [workspaceSlug], response);
|
||||
});
|
||||
return response;
|
||||
} catch (error) {
|
||||
console.error("Error fetching user project permissions", error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @description Joins a project
|
||||
* @param { string } workspaceSlug
|
||||
* @param { string } projectId
|
||||
* @returns { Promise<void | undefined> }
|
||||
*/
|
||||
joinProject = async (workspaceSlug: string, projectId: string): Promise<void | undefined> => {
|
||||
try {
|
||||
const response = await userService.joinProject(workspaceSlug, [projectId]);
|
||||
if (response) {
|
||||
runInAction(() => {
|
||||
set(this.workspaceProjectsPermissions, [workspaceSlug, projectId], response);
|
||||
});
|
||||
}
|
||||
return response;
|
||||
} catch (error) {
|
||||
console.error("Error user joining the project", error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @description Leaves a project
|
||||
* @param { string } workspaceSlug
|
||||
* @param { string } projectId
|
||||
* @returns { Promise<void | undefined> }
|
||||
*/
|
||||
leaveProject = async (workspaceSlug: string, projectId: string): Promise<void> => {
|
||||
try {
|
||||
await userService.leaveProject(workspaceSlug, projectId);
|
||||
runInAction(() => {
|
||||
unset(this.workspaceProjectsPermissions, [workspaceSlug, projectId]);
|
||||
unset(this.projectUserInfo, [workspaceSlug, projectId]);
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error user leaving the project", error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
@ -1,283 +0,0 @@
|
|||
import set from "lodash/set";
|
||||
import update from "lodash/update";
|
||||
import { action, observable, runInAction, makeObservable, computed } from "mobx";
|
||||
// types
|
||||
import { IWorkspaceMemberMe, IProjectMember, IUserProjectsRole, IProjectMemberLite } from "@plane/types";
|
||||
// constants
|
||||
import { EUserProjectRoles } from "@/constants/project";
|
||||
import { EUserWorkspaceRoles } from "@/constants/workspace";
|
||||
// services
|
||||
import { WorkspaceService } from "@/plane-web/services";
|
||||
import { ProjectMemberService } from "@/services/project";
|
||||
import { UserService } from "@/services/user.service";
|
||||
// plane web store
|
||||
import { CoreRootStore } from "../root.store";
|
||||
import { IRouterStore } from "../router.store";
|
||||
|
||||
export interface IUserMembershipStore {
|
||||
// observables
|
||||
workspaceMemberInfo: {
|
||||
[workspaceSlug: string]: IWorkspaceMemberMe;
|
||||
};
|
||||
hasPermissionToWorkspace: {
|
||||
[workspaceSlug: string]: boolean | null;
|
||||
};
|
||||
projectMemberInfo: {
|
||||
[projectId: string]: IProjectMember;
|
||||
};
|
||||
hasPermissionToProject: {
|
||||
[projectId: string]: boolean | null;
|
||||
};
|
||||
workspaceProjectsRole: { [workspaceSlug: string]: IUserProjectsRole };
|
||||
// computed
|
||||
currentProjectMemberInfo: IProjectMember | undefined;
|
||||
currentWorkspaceMemberInfo: IWorkspaceMemberMe | undefined;
|
||||
currentProjectRole: EUserProjectRoles | undefined;
|
||||
currentWorkspaceRole: EUserWorkspaceRoles | undefined;
|
||||
currentWorkspaceAllProjectsRole: IUserProjectsRole | undefined;
|
||||
|
||||
// computed functions
|
||||
currentProjectRoleByProjectId: (projectId: string) => EUserProjectRoles | undefined;
|
||||
|
||||
hasPermissionToCurrentWorkspace: boolean | undefined;
|
||||
hasPermissionToCurrentProject: boolean | undefined;
|
||||
// fetch actions
|
||||
fetchUserWorkspaceInfo: (workspaceSlug: string) => Promise<IWorkspaceMemberMe>;
|
||||
fetchUserProjectInfo: (workspaceSlug: string, projectId: string) => Promise<IProjectMember>;
|
||||
fetchUserWorkspaceProjectsRole: (workspaceSlug: string) => Promise<IUserProjectsRole>;
|
||||
// crud actions
|
||||
leaveWorkspace: (workspaceSlug: string) => Promise<void>;
|
||||
joinProject: (workspaceSlug: string, projectIds: string[]) => Promise<any>;
|
||||
leaveProject: (workspaceSlug: string, projectId: string) => Promise<void>;
|
||||
|
||||
router: IRouterStore;
|
||||
}
|
||||
|
||||
export class UserMembershipStore implements IUserMembershipStore {
|
||||
workspaceMemberInfo: {
|
||||
[workspaceSlug: string]: IWorkspaceMemberMe;
|
||||
} = {};
|
||||
hasPermissionToWorkspace: {
|
||||
[workspaceSlug: string]: boolean;
|
||||
} = {};
|
||||
projectMemberInfo: {
|
||||
[projectId: string]: IProjectMember;
|
||||
} = {};
|
||||
hasPermissionToProject: {
|
||||
[projectId: string]: boolean;
|
||||
} = {};
|
||||
workspaceProjectsRole: { [workspaceSlug: string]: IUserProjectsRole } = {};
|
||||
// stores
|
||||
router;
|
||||
store;
|
||||
// services
|
||||
userService;
|
||||
workspaceService;
|
||||
projectMemberService;
|
||||
|
||||
constructor(_rootStore: CoreRootStore) {
|
||||
makeObservable(this, {
|
||||
// observables
|
||||
workspaceMemberInfo: observable,
|
||||
hasPermissionToWorkspace: observable,
|
||||
projectMemberInfo: observable,
|
||||
hasPermissionToProject: observable,
|
||||
workspaceProjectsRole: observable,
|
||||
// computed
|
||||
currentWorkspaceMemberInfo: computed,
|
||||
currentWorkspaceRole: computed,
|
||||
currentProjectMemberInfo: computed,
|
||||
currentProjectRole: computed,
|
||||
currentWorkspaceAllProjectsRole: computed,
|
||||
hasPermissionToCurrentWorkspace: computed,
|
||||
hasPermissionToCurrentProject: computed,
|
||||
// actions
|
||||
fetchUserWorkspaceInfo: action,
|
||||
fetchUserProjectInfo: action,
|
||||
leaveWorkspace: action,
|
||||
joinProject: action,
|
||||
leaveProject: action,
|
||||
fetchUserWorkspaceProjectsRole: action,
|
||||
});
|
||||
this.router = _rootStore.router;
|
||||
this.store = _rootStore;
|
||||
// services
|
||||
this.userService = new UserService();
|
||||
this.workspaceService = new WorkspaceService();
|
||||
this.projectMemberService = new ProjectMemberService();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current workspace member info
|
||||
*/
|
||||
get currentWorkspaceMemberInfo() {
|
||||
if (!this.router.workspaceSlug) return;
|
||||
return this.workspaceMemberInfo[this.router.workspaceSlug];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current workspace role
|
||||
*/
|
||||
get currentWorkspaceRole() {
|
||||
if (!this.router.workspaceSlug) return;
|
||||
return this.workspaceMemberInfo[this.router.workspaceSlug]?.role;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current project member info
|
||||
*/
|
||||
get currentProjectMemberInfo() {
|
||||
if (!this.router.projectId) return;
|
||||
return this.projectMemberInfo[this.router.projectId];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current project role
|
||||
*/
|
||||
get currentProjectRole() {
|
||||
if (!this.router.projectId) return;
|
||||
return this.projectMemberInfo[this.router.projectId]?.role;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all projects role for the current workspace
|
||||
*/
|
||||
get currentWorkspaceAllProjectsRole() {
|
||||
if (!this.router.workspaceSlug) return;
|
||||
return this.workspaceProjectsRole?.[this.router.workspaceSlug];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if the user has permission to the current workspace
|
||||
*/
|
||||
get hasPermissionToCurrentWorkspace() {
|
||||
if (!this.router.workspaceSlug) return;
|
||||
return this.hasPermissionToWorkspace[this.router.workspaceSlug];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if the user has permission to the current project
|
||||
*/
|
||||
get hasPermissionToCurrentProject() {
|
||||
if (!this.router.projectId) return;
|
||||
return this.hasPermissionToProject[this.router.projectId];
|
||||
}
|
||||
|
||||
// computed functions
|
||||
/**
|
||||
* Returns the current project role by project id
|
||||
* @param projectId
|
||||
* @returns EUserProjectRoles
|
||||
*/
|
||||
currentProjectRoleByProjectId = (projectId: string) => this.projectMemberInfo[projectId]?.role || undefined;
|
||||
|
||||
/**
|
||||
* Fetches the current user workspace info
|
||||
* @param workspaceSlug
|
||||
* @returns Promise<IWorkspaceMemberMe>
|
||||
*/
|
||||
fetchUserWorkspaceInfo = async (workspaceSlug: string) =>
|
||||
await this.workspaceService.workspaceMemberMe(workspaceSlug).then((response) => {
|
||||
runInAction(() => {
|
||||
set(this.workspaceMemberInfo, [workspaceSlug], response);
|
||||
set(this.hasPermissionToWorkspace, [workspaceSlug], true);
|
||||
});
|
||||
return response;
|
||||
});
|
||||
|
||||
/**
|
||||
* Fetches the current user project info
|
||||
* @param workspaceSlug
|
||||
* @param projectId
|
||||
* @returns Promise<IProjectMember>
|
||||
*/
|
||||
fetchUserProjectInfo = async (workspaceSlug: string, projectId: string) =>
|
||||
await this.projectMemberService.projectMemberMe(workspaceSlug, projectId).then((response) => {
|
||||
runInAction(() => {
|
||||
this.projectMemberInfo = {
|
||||
...this.projectMemberInfo,
|
||||
[projectId]: response,
|
||||
};
|
||||
this.hasPermissionToProject = {
|
||||
...this.hasPermissionToProject,
|
||||
[projectId]: true,
|
||||
};
|
||||
});
|
||||
return response;
|
||||
});
|
||||
|
||||
/**
|
||||
* Leaves a workspace
|
||||
* @param workspaceSlug
|
||||
* @returns Promise<void>
|
||||
*/
|
||||
leaveWorkspace = async (workspaceSlug: string) => {
|
||||
const currentWorksSpace = this.store.workspaceRoot?.currentWorkspace;
|
||||
await this.userService.leaveWorkspace(workspaceSlug).then(() => {
|
||||
runInAction(() => {
|
||||
if (currentWorksSpace) delete this.store.workspaceRoot?.workspaces?.[currentWorksSpace?.id];
|
||||
delete this.workspaceMemberInfo[workspaceSlug];
|
||||
delete this.hasPermissionToWorkspace[workspaceSlug];
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Joins a project
|
||||
* @param workspaceSlug
|
||||
* @param projectIds
|
||||
* @returns Promise<void>
|
||||
*/
|
||||
joinProject = async (workspaceSlug: string, projectIds: string[]) =>
|
||||
await this.userService.joinProject(workspaceSlug, projectIds).then(() => {
|
||||
const newPermissions: { [projectId: string]: boolean } = {};
|
||||
projectIds.forEach((projectId) => {
|
||||
newPermissions[projectId] = true;
|
||||
});
|
||||
runInAction(() => {
|
||||
this.hasPermissionToProject = {
|
||||
...this.hasPermissionToProject,
|
||||
...newPermissions,
|
||||
};
|
||||
projectIds.forEach((projectId) => {
|
||||
set(this.workspaceProjectsRole, [workspaceSlug, projectId], EUserProjectRoles.MEMBER);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Leaves a project
|
||||
* @param workspaceSlug
|
||||
* @param projectId
|
||||
* @returns Promise<void>
|
||||
*/
|
||||
leaveProject = async (workspaceSlug: string, projectId: string) =>
|
||||
await this.userService.leaveProject(workspaceSlug, projectId).then(() => {
|
||||
// remove the user membership for a project
|
||||
set(this.hasPermissionToProject, [projectId], false);
|
||||
// update the project member list with the new permissions
|
||||
set(this.store.projectRoot.project.projectMap, [projectId, "is_member"], false);
|
||||
// remove user from project members list
|
||||
update(this.store.projectRoot.project.projectMap, projectId, (project) => {
|
||||
if (project) {
|
||||
project.members = project.members.filter(
|
||||
(member: IProjectMemberLite) => member.member_id !== this.store.user?.data?.id
|
||||
);
|
||||
}
|
||||
return project;
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Fetches the current user workspace projects role
|
||||
* @param workspaceSlug
|
||||
* @returns Promise<IUserProjectsRole>
|
||||
*/
|
||||
fetchUserWorkspaceProjectsRole = async (workspaceSlug: string) =>
|
||||
await this.workspaceService.getWorkspaceUserProjectsRole(workspaceSlug).then((response) => {
|
||||
runInAction(() => {
|
||||
set(this.workspaceProjectsRole, [workspaceSlug], response);
|
||||
});
|
||||
return response;
|
||||
});
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue