fix: store level fixes (#2597)

This commit is contained in:
sriram veeraghanta 2023-11-01 19:22:10 +05:30 committed by GitHub
parent d46eb9c59a
commit 8c620c4f96
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 233 additions and 185 deletions

View File

@ -24,19 +24,15 @@ type Props = {
export const EstimateListItem: React.FC<Props> = observer((props) => { export const EstimateListItem: React.FC<Props> = observer((props) => {
const { estimate, editEstimate, deleteEstimate } = props; const { estimate, editEstimate, deleteEstimate } = props;
// router // router
const router = useRouter(); const router = useRouter();
const { workspaceSlug, projectId } = router.query; const { workspaceSlug, projectId } = router.query;
// store // store
const { project: projectStore } = useMobxStore(); const { project: projectStore } = useMobxStore();
const { currentProjectDetails } = projectStore;
// hooks
const { setToastAlert } = useToast(); const { setToastAlert } = useToast();
// derived values
const projectDetails = projectStore.project_details?.[projectId?.toString()!];
const handleUseEstimate = async () => { const handleUseEstimate = async () => {
if (!workspaceSlug || !projectId) return; if (!workspaceSlug || !projectId) return;
@ -63,7 +59,7 @@ export const EstimateListItem: React.FC<Props> = observer((props) => {
<div> <div>
<h6 className="flex w-[40vw] items-center gap-2 truncate text-sm font-medium"> <h6 className="flex w-[40vw] items-center gap-2 truncate text-sm font-medium">
{estimate.name} {estimate.name}
{projectDetails?.estimate && projectDetails?.estimate === estimate.id && ( {currentProjectDetails?.estimate && currentProjectDetails?.estimate === estimate.id && (
<span className="rounded bg-green-500/20 px-2 py-0.5 text-xs text-green-500">In use</span> <span className="rounded bg-green-500/20 px-2 py-0.5 text-xs text-green-500">In use</span>
)} )}
</h6> </h6>
@ -72,7 +68,7 @@ export const EstimateListItem: React.FC<Props> = observer((props) => {
</p> </p>
</div> </div>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
{projectDetails?.estimate !== estimate?.id && estimate?.points?.length > 0 && ( {currentProjectDetails?.estimate !== estimate?.id && estimate?.points?.length > 0 && (
<Button variant="neutral-primary" onClick={handleUseEstimate}> <Button variant="neutral-primary" onClick={handleUseEstimate}>
Use Use
</Button> </Button>
@ -88,7 +84,7 @@ export const EstimateListItem: React.FC<Props> = observer((props) => {
<span>Edit estimate</span> <span>Edit estimate</span>
</div> </div>
</CustomMenu.MenuItem> </CustomMenu.MenuItem>
{projectDetails?.estimate !== estimate.id && ( {currentProjectDetails?.estimate !== estimate.id && (
<CustomMenu.MenuItem <CustomMenu.MenuItem
onClick={() => { onClick={() => {
deleteEstimate(estimate.id); deleteEstimate(estimate.id);

View File

@ -1,6 +1,5 @@
import React, { useState } from "react"; import React, { useState } from "react";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
// store // store
import { observer } from "mobx-react-lite"; import { observer } from "mobx-react-lite";
import { useMobxStore } from "lib/mobx/store-provider"; import { useMobxStore } from "lib/mobx/store-provider";
@ -25,18 +24,15 @@ export const EstimatesList: React.FC = observer(() => {
// store // store
const { project: projectStore } = useMobxStore(); const { project: projectStore } = useMobxStore();
const { currentProjectDetails } = projectStore;
// states // states
const [estimateFormOpen, setEstimateFormOpen] = useState(false); const [estimateFormOpen, setEstimateFormOpen] = useState(false);
const [estimateToDelete, setEstimateToDelete] = useState<string | null>(null); const [estimateToDelete, setEstimateToDelete] = useState<string | null>(null);
const [estimateToUpdate, setEstimateToUpdate] = useState<IEstimate | undefined>(); const [estimateToUpdate, setEstimateToUpdate] = useState<IEstimate | undefined>();
// hooks // hooks
const { setToastAlert } = useToast(); const { setToastAlert } = useToast();
// derived values // derived values
const estimatesList = projectStore.projectEstimates; const estimatesList = projectStore.projectEstimates;
const projectDetails = projectStore.project_details?.[projectId?.toString()!];
const editEstimate = (estimate: IEstimate) => { const editEstimate = (estimate: IEstimate) => {
setEstimateFormOpen(true); setEstimateFormOpen(true);
@ -88,7 +84,7 @@ export const EstimatesList: React.FC = observer(() => {
> >
Add Estimate Add Estimate
</Button> </Button>
{projectDetails?.estimate && ( {currentProjectDetails?.estimate && (
<Button variant="neutral-primary" onClick={disableEstimates}> <Button variant="neutral-primary" onClick={disableEstimates}>
Disable Estimates Disable Estimates
</Button> </Button>

View File

@ -29,10 +29,10 @@ const moduleViewOptions: { type: "list" | "grid" | "gantt_chart"; icon: any }[]
export const ModulesListHeader: React.FC = observer(() => { export const ModulesListHeader: React.FC = observer(() => {
// router // router
const router = useRouter(); const router = useRouter();
const { workspaceSlug, projectId } = router.query; const { workspaceSlug } = router.query;
// store
const { project: projectStore } = useMobxStore(); const { project: projectStore } = useMobxStore();
const projectDetails = projectId ? projectStore.project_details[projectId.toString()] : undefined; const { currentProjectDetails } = projectStore;
const { storedValue: modulesView, setValue: setModulesView } = useLocalStorage("modules_view", "grid"); const { storedValue: modulesView, setValue: setModulesView } = useLocalStorage("modules_view", "grid");
@ -52,7 +52,7 @@ export const ModulesListHeader: React.FC = observer(() => {
</Link> </Link>
} }
/> />
<BreadcrumbItem title={`${truncateText(projectDetails?.name ?? "Project", 32)} Modules`} /> <BreadcrumbItem title={`${truncateText(currentProjectDetails?.name ?? "Project", 32)} Modules`} />
</Breadcrumbs> </Breadcrumbs>
</div> </div>
</div> </div>

View File

@ -17,10 +17,10 @@ export interface IProjectSettingHeader {
export const ProjectSettingHeader: FC<IProjectSettingHeader> = observer((props) => { export const ProjectSettingHeader: FC<IProjectSettingHeader> = observer((props) => {
const { title } = props; const { title } = props;
const router = useRouter(); const router = useRouter();
const { workspaceSlug, projectId } = router.query; const { workspaceSlug } = router.query;
// store // store
const { project: projectStore } = useMobxStore(); const { project: projectStore } = useMobxStore();
const projectDetails = projectId ? projectStore.project_details[projectId.toString()] : null; const { currentProjectDetails } = projectStore;
return ( return (
<div <div
@ -31,9 +31,9 @@ export const ProjectSettingHeader: FC<IProjectSettingHeader> = observer((props)
<Breadcrumbs onBack={() => router.back()}> <Breadcrumbs onBack={() => router.back()}>
<BreadcrumbItem <BreadcrumbItem
link={ link={
<Link href={`/${workspaceSlug}/projects/${projectDetails?.id}/issues`}> <Link href={`/${workspaceSlug}/projects/${currentProjectDetails?.id}/issues`}>
<a className={`border-r-2 border-custom-sidebar-border-200 px-3 text-sm `}> <a className={`border-r-2 border-custom-sidebar-border-200 px-3 text-sm `}>
<p className="truncate">{`${truncateText(projectDetails?.name ?? "Project", 32)}`}</p> <p className="truncate">{`${truncateText(currentProjectDetails?.name ?? "Project", 32)}`}</p>
</a> </a>
</Link> </Link>
} }

View File

@ -18,6 +18,10 @@ import { ISSUE_STATE_GROUPS, ISSUE_PRIORITIES } from "constants/issue";
export interface ICycleKanBanLayout {} export interface ICycleKanBanLayout {}
export const CycleKanBanLayout: React.FC = observer(() => { export const CycleKanBanLayout: React.FC = observer(() => {
// router
const router = useRouter();
const { workspaceSlug, cycleId } = router.query;
// store
const { const {
project: projectStore, project: projectStore,
cycleIssue: cycleIssueStore, cycleIssue: cycleIssueStore,
@ -25,9 +29,7 @@ export const CycleKanBanLayout: React.FC = observer(() => {
cycleIssueKanBanView: cycleIssueKanBanViewStore, cycleIssueKanBanView: cycleIssueKanBanViewStore,
issueDetail: issueDetailStore, issueDetail: issueDetailStore,
} = useMobxStore(); } = useMobxStore();
const { currentProjectDetails } = projectStore;
const router = useRouter();
const { workspaceSlug, projectId, cycleId } = router.query;
const issues = cycleIssueStore?.getIssues; const issues = cycleIssueStore?.getIssues;
@ -83,8 +85,6 @@ export const CycleKanBanLayout: React.FC = observer(() => {
cycleIssueKanBanViewStore.handleKanBanToggle(toggle, value); cycleIssueKanBanViewStore.handleKanBanToggle(toggle, value);
}; };
const projectDetails = projectId ? projectStore.project_details[projectId.toString()] : null;
const states = projectStore?.projectStates || null; const states = projectStore?.projectStates || null;
const priorities = ISSUE_PRIORITIES || null; const priorities = ISSUE_PRIORITIES || null;
const labels = projectStore?.projectLabels || null; const labels = projectStore?.projectLabels || null;
@ -92,8 +92,8 @@ export const CycleKanBanLayout: React.FC = observer(() => {
const stateGroups = ISSUE_STATE_GROUPS || null; const stateGroups = ISSUE_STATE_GROUPS || null;
const projects = workspaceSlug ? projectStore?.projects[workspaceSlug.toString()] || null : null; const projects = workspaceSlug ? projectStore?.projects[workspaceSlug.toString()] || null : null;
const estimates = const estimates =
projectDetails?.estimate !== null currentProjectDetails?.estimate !== null
? projectStore.projectEstimates?.find((e) => e.id === projectDetails?.estimate) || null ? projectStore.projectEstimates?.find((e) => e.id === currentProjectDetails?.estimate) || null
: null; : null;
return ( return (

View File

@ -18,6 +18,9 @@ import { ISSUE_STATE_GROUPS, ISSUE_PRIORITIES } from "constants/issue";
export interface IModuleKanBanLayout {} export interface IModuleKanBanLayout {}
export const ModuleKanBanLayout: React.FC = observer(() => { export const ModuleKanBanLayout: React.FC = observer(() => {
const router = useRouter();
const { workspaceSlug, moduleId } = router.query;
// store
const { const {
project: projectStore, project: projectStore,
moduleIssue: moduleIssueStore, moduleIssue: moduleIssueStore,
@ -25,9 +28,7 @@ export const ModuleKanBanLayout: React.FC = observer(() => {
moduleIssueKanBanView: moduleIssueKanBanViewStore, moduleIssueKanBanView: moduleIssueKanBanViewStore,
issueDetail: issueDetailStore, issueDetail: issueDetailStore,
} = useMobxStore(); } = useMobxStore();
const { currentProjectDetails } = projectStore;
const router = useRouter();
const { workspaceSlug, projectId, moduleId } = router.query;
const issues = moduleIssueStore?.getIssues; const issues = moduleIssueStore?.getIssues;
@ -83,8 +84,6 @@ export const ModuleKanBanLayout: React.FC = observer(() => {
moduleIssueKanBanViewStore.handleKanBanToggle(toggle, value); moduleIssueKanBanViewStore.handleKanBanToggle(toggle, value);
}; };
const projectDetails = projectId ? projectStore.project_details[projectId.toString()] : null;
const states = projectStore?.projectStates || null; const states = projectStore?.projectStates || null;
const priorities = ISSUE_PRIORITIES || null; const priorities = ISSUE_PRIORITIES || null;
const labels = projectStore?.projectLabels || null; const labels = projectStore?.projectLabels || null;
@ -92,8 +91,8 @@ export const ModuleKanBanLayout: React.FC = observer(() => {
const stateGroups = ISSUE_STATE_GROUPS || null; const stateGroups = ISSUE_STATE_GROUPS || null;
const projects = workspaceSlug ? projectStore?.projects[workspaceSlug.toString()] || null : null; const projects = workspaceSlug ? projectStore?.projects[workspaceSlug.toString()] || null : null;
const estimates = const estimates =
projectDetails?.estimate !== null currentProjectDetails?.estimate !== null
? projectStore.projectEstimates?.find((e) => e.id === projectDetails?.estimate) || null ? projectStore.projectEstimates?.find((e) => e.id === currentProjectDetails?.estimate) || null
: null; : null;
return ( return (

View File

@ -19,7 +19,7 @@ export interface IKanBanLayout {}
export const KanBanLayout: React.FC = observer(() => { export const KanBanLayout: React.FC = observer(() => {
const router = useRouter(); const router = useRouter();
const { workspaceSlug, projectId } = router.query; const { workspaceSlug } = router.query;
const { const {
project: projectStore, project: projectStore,
@ -28,6 +28,7 @@ export const KanBanLayout: React.FC = observer(() => {
issueKanBanView: issueKanBanViewStore, issueKanBanView: issueKanBanViewStore,
issueDetail: issueDetailStore, issueDetail: issueDetailStore,
} = useMobxStore(); } = useMobxStore();
const { currentProjectDetails } = projectStore;
const issues = issueStore?.getIssues; const issues = issueStore?.getIssues;
@ -74,8 +75,6 @@ export const KanBanLayout: React.FC = observer(() => {
issueKanBanViewStore.handleKanBanToggle(toggle, value); issueKanBanViewStore.handleKanBanToggle(toggle, value);
}; };
const projectDetails = projectId ? projectStore.project_details[projectId.toString()] : null;
const states = projectStore?.projectStates || null; const states = projectStore?.projectStates || null;
const priorities = ISSUE_PRIORITIES || null; const priorities = ISSUE_PRIORITIES || null;
const labels = projectStore?.projectLabels || null; const labels = projectStore?.projectLabels || null;
@ -83,8 +82,8 @@ export const KanBanLayout: React.FC = observer(() => {
const stateGroups = ISSUE_STATE_GROUPS || null; const stateGroups = ISSUE_STATE_GROUPS || null;
const projects = workspaceSlug ? projectStore?.projects[workspaceSlug.toString()] || null : null; const projects = workspaceSlug ? projectStore?.projects[workspaceSlug.toString()] || null : null;
const estimates = const estimates =
projectDetails?.estimate !== null currentProjectDetails?.estimate !== null
? projectStore.projectEstimates?.find((e) => e.id === projectDetails?.estimate) || null ? projectStore.projectEstimates?.find((e) => e.id === currentProjectDetails?.estimate) || null
: null; : null;
return ( return (

View File

@ -17,14 +17,15 @@ export interface ICycleListLayout {}
export const CycleListLayout: React.FC = observer(() => { export const CycleListLayout: React.FC = observer(() => {
const router = useRouter(); const router = useRouter();
const { workspaceSlug, projectId, cycleId } = router.query; const { workspaceSlug, cycleId } = router.query;
// store
const { const {
project: projectStore, project: projectStore,
issueFilter: issueFilterStore, issueFilter: issueFilterStore,
cycleIssue: cycleIssueStore, cycleIssue: cycleIssueStore,
issueDetail: issueDetailStore, issueDetail: issueDetailStore,
} = useMobxStore(); } = useMobxStore();
const { currentProjectDetails } = projectStore;
const issues = cycleIssueStore?.getIssues; const issues = cycleIssueStore?.getIssues;
@ -54,8 +55,6 @@ export const CycleListLayout: React.FC = observer(() => {
[cycleIssueStore, issueDetailStore, cycleId, workspaceSlug] [cycleIssueStore, issueDetailStore, cycleId, workspaceSlug]
); );
const projectDetails = projectId ? projectStore.project_details[projectId.toString()] : null;
const states = projectStore?.projectStates || null; const states = projectStore?.projectStates || null;
const priorities = ISSUE_PRIORITIES || null; const priorities = ISSUE_PRIORITIES || null;
const labels = projectStore?.projectLabels || null; const labels = projectStore?.projectLabels || null;
@ -63,8 +62,8 @@ export const CycleListLayout: React.FC = observer(() => {
const stateGroups = ISSUE_STATE_GROUPS || null; const stateGroups = ISSUE_STATE_GROUPS || null;
const projects = workspaceSlug ? projectStore?.projects[workspaceSlug.toString()] || null : null; const projects = workspaceSlug ? projectStore?.projects[workspaceSlug.toString()] || null : null;
const estimates = const estimates =
projectDetails?.estimate !== null currentProjectDetails?.estimate !== null
? projectStore.projectEstimates?.find((e) => e.id === projectDetails?.estimate) || null ? projectStore.projectEstimates?.find((e) => e.id === currentProjectDetails?.estimate) || null
: null; : null;
return ( return (

View File

@ -17,7 +17,7 @@ export interface IModuleListLayout {}
export const ModuleListLayout: React.FC = observer(() => { export const ModuleListLayout: React.FC = observer(() => {
const router = useRouter(); const router = useRouter();
const { workspaceSlug, projectId, moduleId } = router.query; const { workspaceSlug, moduleId } = router.query;
const { const {
project: projectStore, project: projectStore,
@ -25,6 +25,7 @@ export const ModuleListLayout: React.FC = observer(() => {
moduleIssue: moduleIssueStore, moduleIssue: moduleIssueStore,
issueDetail: issueDetailStore, issueDetail: issueDetailStore,
} = useMobxStore(); } = useMobxStore();
const { currentProjectDetails } = projectStore;
const issues = moduleIssueStore?.getIssues; const issues = moduleIssueStore?.getIssues;
@ -54,8 +55,6 @@ export const ModuleListLayout: React.FC = observer(() => {
[moduleIssueStore, issueDetailStore, moduleId, workspaceSlug] [moduleIssueStore, issueDetailStore, moduleId, workspaceSlug]
); );
const projectDetails = projectId ? projectStore.project_details[projectId.toString()] : null;
const states = projectStore?.projectStates || null; const states = projectStore?.projectStates || null;
const priorities = ISSUE_PRIORITIES || null; const priorities = ISSUE_PRIORITIES || null;
const labels = projectStore?.projectLabels || null; const labels = projectStore?.projectLabels || null;
@ -63,8 +62,8 @@ export const ModuleListLayout: React.FC = observer(() => {
const stateGroups = ISSUE_STATE_GROUPS || null; const stateGroups = ISSUE_STATE_GROUPS || null;
const projects = workspaceSlug ? projectStore?.projects[workspaceSlug.toString()] || null : null; const projects = workspaceSlug ? projectStore?.projects[workspaceSlug.toString()] || null : null;
const estimates = const estimates =
projectDetails?.estimate !== null currentProjectDetails?.estimate !== null
? projectStore.projectEstimates?.find((e) => e.id === projectDetails?.estimate) || null ? projectStore.projectEstimates?.find((e) => e.id === currentProjectDetails?.estimate) || null
: null; : null;
return ( return (

View File

@ -15,14 +15,15 @@ import { ISSUE_STATE_GROUPS, ISSUE_PRIORITIES } from "constants/issue";
export const ListLayout: FC = observer(() => { export const ListLayout: FC = observer(() => {
const router = useRouter(); const router = useRouter();
const { workspaceSlug, projectId } = router.query; const { workspaceSlug } = router.query;
// store
const { const {
project: projectStore, project: projectStore,
issue: issueStore, issue: issueStore,
issueDetail: issueDetailStore, issueDetail: issueDetailStore,
issueFilter: issueFilterStore, issueFilter: issueFilterStore,
} = useMobxStore(); } = useMobxStore();
const { currentProjectDetails } = projectStore;
const issues = issueStore?.getIssues; const issues = issueStore?.getIssues;
@ -43,8 +44,6 @@ export const ListLayout: FC = observer(() => {
[issueStore, issueDetailStore, workspaceSlug] [issueStore, issueDetailStore, workspaceSlug]
); );
const projectDetails = projectId ? projectStore.project_details[projectId.toString()] : null;
const states = projectStore?.projectStates || null; const states = projectStore?.projectStates || null;
const priorities = ISSUE_PRIORITIES || null; const priorities = ISSUE_PRIORITIES || null;
const labels = projectStore?.projectLabels || null; const labels = projectStore?.projectLabels || null;
@ -52,8 +51,8 @@ export const ListLayout: FC = observer(() => {
const stateGroups = ISSUE_STATE_GROUPS || null; const stateGroups = ISSUE_STATE_GROUPS || null;
const projects = workspaceSlug ? projectStore?.projects[workspaceSlug.toString()] || null : null; const projects = workspaceSlug ? projectStore?.projects[workspaceSlug.toString()] || null : null;
const estimates = const estimates =
projectDetails?.estimate !== null currentProjectDetails?.estimate !== null
? projectStore.projectEstimates?.find((e) => e.id === projectDetails?.estimate) || null ? projectStore.projectEstimates?.find((e) => e.id === currentProjectDetails?.estimate) || null
: null; : null;
return ( return (

View File

@ -31,7 +31,8 @@ export const PeekOverviewIssueDetails: FC<IPeekOverviewIssueDetails> = (props) =
const { workspaceSlug, issue, issueReactions, user, issueUpdate, issueReactionCreate, issueReactionRemove } = props; const { workspaceSlug, issue, issueReactions, user, issueUpdate, issueReactionCreate, issueReactionRemove } = props;
// store // store
const { user: userStore } = useMobxStore(); const { user: userStore } = useMobxStore();
const isAllowed = [5, 10].includes(userStore.projectMemberInfo?.role || 0); const { currentProjectRole } = userStore;
const isAllowed = [5, 10].includes(currentProjectRole || 0);
// states // states
const [isSubmitting, setIsSubmitting] = useState<"submitting" | "submitted" | "saved">("saved"); const [isSubmitting, setIsSubmitting] = useState<"submitting" | "submitted" | "saved">("saved");
const [characterLimit, setCharacterLimit] = useState(false); const [characterLimit, setCharacterLimit] = useState(false);

View File

@ -9,12 +9,12 @@ import { ModuleGanttBlock, ModuleGanttSidebarBlock } from "components/modules";
import { IModule } from "types"; import { IModule } from "types";
export const ModulesListGanttChartView: React.FC = observer(() => { export const ModulesListGanttChartView: React.FC = observer(() => {
// router
const router = useRouter(); const router = useRouter();
const { workspaceSlug, projectId } = router.query; const { workspaceSlug } = router.query;
// store
const { project: projectStore, module: moduleStore } = useMobxStore(); const { project: projectStore, module: moduleStore } = useMobxStore();
const { currentProjectDetails } = projectStore;
const projectDetails = projectId ? projectStore.project_details[projectId.toString()] : undefined;
const modules = moduleStore.projectModules; const modules = moduleStore.projectModules;
const handleModuleUpdate = (module: IModule, payload: IBlockUpdateData) => { const handleModuleUpdate = (module: IModule, payload: IBlockUpdateData) => {
@ -36,7 +36,7 @@ export const ModulesListGanttChartView: React.FC = observer(() => {
})) }))
: []; : [];
const isAllowed = projectDetails?.member_role === 20 || projectDetails?.member_role === 15; const isAllowed = currentProjectDetails?.member_role === 20 || currentProjectDetails?.member_role === 15;
return ( return (
<div className="w-full h-full overflow-y-auto"> <div className="w-full h-full overflow-y-auto">

View File

@ -26,32 +26,28 @@ type Props = {
export const ProjectMemberListItem: React.FC<Props> = observer((props) => { export const ProjectMemberListItem: React.FC<Props> = observer((props) => {
const { member } = props; const { member } = props;
// router // router
const router = useRouter(); const router = useRouter();
const { workspaceSlug, projectId } = router.query; const { workspaceSlug, projectId } = router.query;
// states // states
const [selectedRemoveMember, setSelectedRemoveMember] = useState<any | null>(null); const [selectedRemoveMember, setSelectedRemoveMember] = useState<any | null>(null);
const [selectedInviteRemoveMember, setSelectedInviteRemoveMember] = useState<any | null>(null); const [selectedInviteRemoveMember, setSelectedInviteRemoveMember] = useState<any | null>(null);
// store // store
const { user: userStore, project: projectStore } = useMobxStore(); const { user: userStore, project: projectStore } = useMobxStore();
// hooks
const { setToastAlert } = useToast(); const { setToastAlert } = useToast();
// fetching project members
useSWR( useSWR(
workspaceSlug && projectId ? `PROJECT_MEMBERS_${projectId.toString().toUpperCase()}` : null, workspaceSlug && projectId ? `PROJECT_MEMBERS_${projectId.toString().toUpperCase()}` : null,
workspaceSlug && projectId workspaceSlug && projectId
? () => projectStore.fetchProjectMembers(workspaceSlug.toString(), projectId.toString()) ? () => projectStore.fetchProjectMembers(workspaceSlug.toString(), projectId.toString())
: null : null
); );
// derived values // derived values
const user = userStore.currentUser; const user = userStore.currentUser;
const memberDetails = userStore.projectMemberInfo; const { currentProjectRole } = userStore;
const isAdmin = memberDetails?.role === 20; const isAdmin = currentProjectRole === 20;
const isOwner = memberDetails?.role === 20; const isOwner = currentProjectRole === 20;
const projectMembers = projectStore.members?.[projectId?.toString()!]; const projectMembers = projectStore.members?.[projectId?.toString()!];
const currentUser = projectMembers?.find((item) => item.member.id === user?.id); const currentUser = projectMembers?.find((item) => item.member.id === user?.id);

View File

@ -25,20 +25,16 @@ export const ProjectSettingsMemberDefaults: React.FC = observer(() => {
// router // router
const router = useRouter(); const router = useRouter();
const { workspaceSlug, projectId } = router.query; const { workspaceSlug, projectId } = router.query;
// store // store
const { user: userStore, project: projectStore } = useMobxStore(); const { user: userStore, project: projectStore } = useMobxStore();
const { currentProjectDetails } = projectStore;
const { currentProjectRole } = userStore;
const isAdmin = currentProjectRole === 20;
// hooks // hooks
const { setToastAlert } = useToast(); const { setToastAlert } = useToast();
// form info
// derived values
const memberDetails = userStore.projectMemberInfo;
const isAdmin = memberDetails?.role === 20;
const projectDetails = projectStore.project_details[projectId?.toString()!];
const { reset, control } = useForm<IProject>({ defaultValues }); const { reset, control } = useForm<IProject>({ defaultValues });
// fetching user members
useSWR( useSWR(
workspaceSlug && projectId ? PROJECT_MEMBERS(projectId.toString()) : null, workspaceSlug && projectId ? PROJECT_MEMBERS(projectId.toString()) : null,
workspaceSlug && projectId workspaceSlug && projectId
@ -47,23 +43,23 @@ export const ProjectSettingsMemberDefaults: React.FC = observer(() => {
); );
useEffect(() => { useEffect(() => {
if (!projectDetails) return; if (!currentProjectDetails) return;
reset({ reset({
...projectDetails, ...currentProjectDetails,
default_assignee: projectDetails.default_assignee?.id ?? projectDetails.default_assignee, default_assignee: currentProjectDetails.default_assignee?.id ?? currentProjectDetails.default_assignee,
project_lead: (projectDetails.project_lead as IUserLite)?.id ?? projectDetails.project_lead, project_lead: (currentProjectDetails.project_lead as IUserLite)?.id ?? currentProjectDetails.project_lead,
workspace: (projectDetails.workspace as IWorkspace).id, workspace: (currentProjectDetails.workspace as IWorkspace).id,
}); });
}, [projectDetails, reset]); }, [currentProjectDetails, reset]);
const submitChanges = async (formData: Partial<IProject>) => { const submitChanges = async (formData: Partial<IProject>) => {
if (!workspaceSlug || !projectId) return; if (!workspaceSlug || !projectId) return;
reset({ reset({
...projectDetails, ...currentProjectDetails,
default_assignee: projectDetails.default_assignee?.id ?? projectDetails.default_assignee, default_assignee: currentProjectDetails?.default_assignee?.id ?? currentProjectDetails?.default_assignee,
project_lead: (projectDetails.project_lead as IUserLite)?.id ?? projectDetails.project_lead, project_lead: (currentProjectDetails?.project_lead as IUserLite)?.id ?? currentProjectDetails?.project_lead,
...formData, ...formData,
}); });
@ -96,7 +92,7 @@ export const ProjectSettingsMemberDefaults: React.FC = observer(() => {
<div className="flex flex-col gap-2 w-1/2"> <div className="flex flex-col gap-2 w-1/2">
<h4 className="text-sm">Project Lead</h4> <h4 className="text-sm">Project Lead</h4>
<div className=""> <div className="">
{projectDetails ? ( {currentProjectDetails ? (
<Controller <Controller
control={control} control={control}
name="project_lead" name="project_lead"
@ -121,7 +117,7 @@ export const ProjectSettingsMemberDefaults: React.FC = observer(() => {
<div className="flex flex-col gap-2 w-1/2"> <div className="flex flex-col gap-2 w-1/2">
<h4 className="text-sm">Default Assignee</h4> <h4 className="text-sm">Default Assignee</h4>
<div className=""> <div className="">
{projectDetails ? ( {currentProjectDetails ? (
<Controller <Controller
control={control} control={control}
name="default_assignee" name="default_assignee"

View File

@ -1,3 +1,4 @@
import { FC } from "react";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { observer } from "mobx-react-lite"; import { observer } from "mobx-react-lite";
import { ContrastIcon, FileText, Inbox, Layers } from "lucide-react"; import { ContrastIcon, FileText, Inbox, Layers } from "lucide-react";
@ -18,7 +19,6 @@ const PROJECT_FEATURES_LIST = [
title: "Cycles", title: "Cycles",
description: "Cycles are enabled for all the projects in this workspace. Access them from the sidebar.", description: "Cycles are enabled for all the projects in this workspace. Access them from the sidebar.",
icon: <ContrastIcon className="h-4 w-4 text-purple-500 flex-shrink-0 rotate-180" />, icon: <ContrastIcon className="h-4 w-4 text-purple-500 flex-shrink-0 rotate-180" />,
property: "cycle_view", property: "cycle_view",
}, },
{ {
@ -67,32 +67,30 @@ const getEventType = (feature: string, toggle: boolean): MiscellaneousEventType
// services // services
const trackEventService = new TrackEventService(); const trackEventService = new TrackEventService();
export const ProjectFeaturesList: React.FC<Props> = observer((props) => { export const ProjectFeaturesList: FC<Props> = observer(() => {
const {} = props; // router
const router = useRouter(); const router = useRouter();
const { workspaceSlug, projectId } = router.query; const { workspaceSlug, projectId } = router.query;
// store
const { project: projectStore, user: userStore } = useMobxStore(); const { project: projectStore, user: userStore } = useMobxStore();
const { currentUser, currentProjectRole } = userStore;
const projectDetails = projectId ? projectStore.project_details[projectId.toString()] : undefined; const { currentProjectDetails } = projectStore;
const user = userStore.currentUser ?? undefined; const isAdmin = currentProjectRole === 20;
const isAdmin = userStore.projectMemberInfo?.role === 20; // hooks
const { setToastAlert } = useToast(); const { setToastAlert } = useToast();
const handleSubmit = async (formData: Partial<IProject>) => { const handleSubmit = async (formData: Partial<IProject>) => {
if (!workspaceSlug || !projectId || !projectDetails) return; if (!workspaceSlug || !projectId || !currentProjectDetails) return;
setToastAlert({ setToastAlert({
type: "success", type: "success",
title: "Success!", title: "Success!",
message: "Project feature updated successfully.", message: "Project feature updated successfully.",
}); });
projectStore.updateProject(workspaceSlug.toString(), projectId.toString(), formData); projectStore.updateProject(workspaceSlug.toString(), projectId.toString(), formData);
}; };
if (!currentUser) return <></>;
return ( return (
<div> <div>
{PROJECT_FEATURES_LIST.map((feature) => ( {PROJECT_FEATURES_LIST.map((feature) => (
@ -108,21 +106,21 @@ export const ProjectFeaturesList: React.FC<Props> = observer((props) => {
</div> </div>
</div> </div>
<ToggleSwitch <ToggleSwitch
value={projectDetails?.[feature.property as keyof IProject]} value={currentProjectDetails?.[feature.property as keyof IProject]}
onChange={() => { onChange={() => {
trackEventService.trackMiscellaneousEvent( trackEventService.trackMiscellaneousEvent(
{ {
workspaceId: (projectDetails?.workspace as any)?.id, workspaceId: (currentProjectDetails?.workspace as any)?.id,
workspaceSlug, workspaceSlug,
projectId, projectId,
projectIdentifier: projectDetails?.identifier, projectIdentifier: currentProjectDetails?.identifier,
projectName: projectDetails?.name, projectName: currentProjectDetails?.name,
}, },
getEventType(feature.title, !projectDetails?.[feature.property as keyof IProject]), getEventType(feature.title, !currentProjectDetails?.[feature.property as keyof IProject]),
user currentUser
); );
handleSubmit({ handleSubmit({
[feature.property]: !projectDetails?.[feature.property as keyof IProject], [feature.property]: !currentProjectDetails?.[feature.property as keyof IProject],
}); });
}} }}
disabled={!isAdmin} disabled={!isAdmin}

View File

@ -1,9 +1,6 @@
import React, { useState } from "react"; import React, { useState } from "react";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import useSWR from "swr"; import useSWR from "swr";
// store // store
import { observer } from "mobx-react-lite"; import { observer } from "mobx-react-lite";
import { useMobxStore } from "lib/mobx/store-provider"; import { useMobxStore } from "lib/mobx/store-provider";
@ -22,10 +19,9 @@ export const ProjectSettingStateList: React.FC = observer(() => {
// router // router
const router = useRouter(); const router = useRouter();
const { workspaceSlug, projectId } = router.query; const { workspaceSlug, projectId } = router.query;
// store // store
const { project: projectStore } = useMobxStore(); const { project: projectStore } = useMobxStore();
const { currentProjectDetails } = projectStore;
// state // state
const [activeGroup, setActiveGroup] = useState<StateGroup>(null); const [activeGroup, setActiveGroup] = useState<StateGroup>(null);
const [selectedState, setSelectedState] = useState<string | null>(null); const [selectedState, setSelectedState] = useState<string | null>(null);
@ -47,7 +43,6 @@ export const ProjectSettingStateList: React.FC = observer(() => {
// derived values // derived values
const states = projectStore.projectStatesByGroups; const states = projectStore.projectStatesByGroups;
const projectDetails = projectStore.project_details[projectId?.toString()!] ?? null;
const orderedStateGroups = orderStateGroups(states!); const orderedStateGroups = orderStateGroups(states!);
const statesList = getStatesList(orderedStateGroups); const statesList = getStatesList(orderedStateGroups);
@ -60,7 +55,7 @@ export const ProjectSettingStateList: React.FC = observer(() => {
/> />
<div className="space-y-8 py-6"> <div className="space-y-8 py-6">
{states && projectDetails && orderedStateGroups ? ( {states && currentProjectDetails && orderedStateGroups ? (
Object.keys(orderedStateGroups || {}).map((key) => { Object.keys(orderedStateGroups || {}).map((key) => {
if (orderedStateGroups[key].length !== 0) if (orderedStateGroups[key].length !== 0)
return ( return (

View File

@ -1,4 +1,4 @@
import { useState } from "react"; import { useState, FC } from "react";
import Link from "next/link"; import Link from "next/link";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
// mobx store // mobx store
@ -35,20 +35,19 @@ type Props = {
// services // services
const workspaceService = new WorkspaceService(); const workspaceService = new WorkspaceService();
export const WorkspaceMembersListItem: React.FC<Props> = (props) => { export const WorkspaceMembersListItem: FC<Props> = (props) => {
const { member } = props; const { member } = props;
// router
const [removeMemberModal, setRemoveMemberModal] = useState(false);
const router = useRouter(); const router = useRouter();
const { workspaceSlug } = router.query; const { workspaceSlug } = router.query;
// store
const { setToastAlert } = useToast();
const { workspace: workspaceStore, user: userStore } = useMobxStore(); const { workspace: workspaceStore, user: userStore } = useMobxStore();
const { currentWorkspaceMemberInfo, currentWorkspaceRole } = userStore;
const user = userStore.workspaceMemberInfo; const isAdmin = currentWorkspaceRole === 20;
const isAdmin = userStore.workspaceMemberInfo?.role === 20; // states
const [removeMemberModal, setRemoveMemberModal] = useState(false);
// hooks
const { setToastAlert } = useToast();
const handleRemoveMember = async () => { const handleRemoveMember = async () => {
if (!workspaceSlug) return; if (!workspaceSlug) return;
@ -83,7 +82,7 @@ export const WorkspaceMembersListItem: React.FC<Props> = (props) => {
}); });
}; };
if (!user) return null; if (!currentWorkspaceMemberInfo) return null;
return ( return (
<> <>
@ -141,12 +140,12 @@ export const WorkspaceMembersListItem: React.FC<Props> = (props) => {
<div className="flex item-center gap-1 px-2 py-0.5 rounded"> <div className="flex item-center gap-1 px-2 py-0.5 rounded">
<span <span
className={`flex items-center text-xs font-medium rounded ${ className={`flex items-center text-xs font-medium rounded ${
member.memberId !== user.member ? "" : "text-custom-sidebar-text-400" member.memberId !== currentWorkspaceMemberInfo.member ? "" : "text-custom-sidebar-text-400"
}`} }`}
> >
{ROLE[member.role as keyof typeof ROLE]} {ROLE[member.role as keyof typeof ROLE]}
</span> </span>
{member.memberId !== user.member && ( {member.memberId !== currentWorkspaceMemberInfo.member && (
<span className="grid place-items-center"> <span className="grid place-items-center">
<ChevronDown className="h-3 w-3" /> <ChevronDown className="h-3 w-3" />
</span> </span>
@ -155,7 +154,7 @@ export const WorkspaceMembersListItem: React.FC<Props> = (props) => {
} }
value={member.role} value={member.role}
onChange={(value: 5 | 10 | 15 | 20 | undefined) => { onChange={(value: 5 | 10 | 15 | 20 | undefined) => {
if (!workspaceSlug) return; if (!workspaceSlug || !value) return;
workspaceStore workspaceStore
.updateMember(workspaceSlug.toString(), member.id, { .updateMember(workspaceSlug.toString(), member.id, {
@ -170,12 +169,15 @@ export const WorkspaceMembersListItem: React.FC<Props> = (props) => {
}); });
}} }}
disabled={ disabled={
member.memberId === user.member || !member.status || (user.role !== 20 && user.role < member.role) member.memberId === currentWorkspaceMemberInfo.member ||
!member.status ||
Boolean(currentWorkspaceRole && currentWorkspaceRole !== 20 && currentWorkspaceRole < member.role)
} }
placement="bottom-end" placement="bottom-end"
> >
{Object.keys(ROLE).map((key) => { {Object.keys(ROLE).map((key) => {
if (user.role !== 20 && user.role < parseInt(key)) return null; if (currentWorkspaceRole && currentWorkspaceRole !== 20 && currentWorkspaceRole < parseInt(key))
return null;
return ( return (
<CustomSelect.Option key={key} value={parseInt(key, 10)}> <CustomSelect.Option key={key} value={parseInt(key, 10)}>
@ -185,7 +187,11 @@ export const WorkspaceMembersListItem: React.FC<Props> = (props) => {
})} })}
</CustomSelect> </CustomSelect>
{isAdmin && ( {isAdmin && (
<Tooltip tooltipContent={member.memberId === user.member ? "Leave workspace" : "Remove member"}> <Tooltip
tooltipContent={
member.memberId === currentWorkspaceMemberInfo.member ? "Leave workspace" : "Remove member"
}
>
<button <button
type="button" type="button"
onClick={() => setRemoveMemberModal(true)} onClick={() => setRemoveMemberModal(true)}

View File

@ -15,12 +15,11 @@ const workspaceService = new WorkspaceService();
export const WorkspaceMembersList: React.FC = observer(() => { export const WorkspaceMembersList: React.FC = observer(() => {
const router = useRouter(); const router = useRouter();
const { workspaceSlug } = router.query; const { workspaceSlug } = router.query;
// store
const { workspace: workspaceStore, user: userStore } = useMobxStore(); const { workspace: workspaceStore, user: userStore } = useMobxStore();
const workspaceMembers = workspaceStore.workspaceMembers; const workspaceMembers = workspaceStore.workspaceMembers;
const user = userStore.workspaceMemberInfo; const user = userStore.currentWorkspaceMemberInfo;
// fetching workspace invitations
const { data: workspaceInvitations } = useSWR( const { data: workspaceInvitations } = useSWR(
workspaceSlug ? `WORKSPACE_INVITATIONS_${workspaceSlug.toString()}` : null, workspaceSlug ? `WORKSPACE_INVITATIONS_${workspaceSlug.toString()}` : null,
workspaceSlug ? () => workspaceService.workspaceInvitations(workspaceSlug.toString()) : null workspaceSlug ? () => workspaceService.workspaceInvitations(workspaceSlug.toString()) : null

View File

@ -30,16 +30,20 @@ const defaultValues: Partial<IWorkspace> = {
const fileService = new FileService(); const fileService = new FileService();
export const WorkspaceDetails: React.FC = observer(() => { export const WorkspaceDetails: React.FC = observer(() => {
// states
const [deleteWorkspaceModal, setDeleteWorkspaceModal] = useState(false); const [deleteWorkspaceModal, setDeleteWorkspaceModal] = useState(false);
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [isImageUploading, setIsImageUploading] = useState(false); const [isImageUploading, setIsImageUploading] = useState(false);
const [isImageRemoving, setIsImageRemoving] = useState(false); const [isImageRemoving, setIsImageRemoving] = useState(false);
const [isImageUploadModalOpen, setIsImageUploadModalOpen] = useState(false); const [isImageUploadModalOpen, setIsImageUploadModalOpen] = useState(false);
// store
const { workspace: workspaceStore, user: userStore } = useMobxStore(); const { workspace: workspaceStore, user: userStore } = useMobxStore();
const activeWorkspace = workspaceStore.currentWorkspace; const activeWorkspace = workspaceStore.currentWorkspace;
const { currentWorkspaceRole } = userStore;
const isAdmin = currentWorkspaceRole === 20;
// hooks
const { setToastAlert } = useToast(); const { setToastAlert } = useToast();
// form info
const { const {
handleSubmit, handleSubmit,
control, control,
@ -103,8 +107,6 @@ export const WorkspaceDetails: React.FC = observer(() => {
if (activeWorkspace) reset({ ...activeWorkspace }); if (activeWorkspace) reset({ ...activeWorkspace });
}, [activeWorkspace, reset]); }, [activeWorkspace, reset]);
const isAdmin = userStore.workspaceMemberInfo?.role === 20;
if (!activeWorkspace) if (!activeWorkspace)
return ( return (
<div className="grid place-items-center h-full w-full px-4 sm:px-0"> <div className="grid place-items-center h-full w-full px-4 sm:px-0">

View File

@ -16,6 +16,7 @@ export const WorkspaceAuthWrapper: FC<IWorkspaceAuthWrapper> = observer((props)
const { children } = props; const { children } = props;
// store // store
const { user: userStore, project: projectStore, workspace: workspaceStore } = useMobxStore(); const { user: userStore, project: projectStore, workspace: workspaceStore } = useMobxStore();
const { currentWorkspaceMemberInfo, hasPermissionToCurrentWorkspace } = userStore;
// router // router
const router = useRouter(); const router = useRouter();
const { workspaceSlug } = router.query; const { workspaceSlug } = router.query;
@ -43,11 +44,7 @@ export const WorkspaceAuthWrapper: FC<IWorkspaceAuthWrapper> = observer((props)
); );
// while data is being loaded // while data is being loaded
if ( if (!currentWorkspaceMemberInfo && hasPermissionToCurrentWorkspace === undefined) {
!userStore.workspaceMemberInfo &&
workspaceSlug &&
userStore.hasPermissionToWorkspace[workspaceSlug.toString()] === null
) {
return ( return (
<div className="grid h-screen place-items-center p-4 bg-custom-background-100"> <div className="grid h-screen place-items-center p-4 bg-custom-background-100">
<div className="flex flex-col items-center gap-3 text-center"> <div className="flex flex-col items-center gap-3 text-center">
@ -57,11 +54,7 @@ export const WorkspaceAuthWrapper: FC<IWorkspaceAuthWrapper> = observer((props)
); );
} }
// while user does not have access to view that workspace // while user does not have access to view that workspace
if ( if (hasPermissionToCurrentWorkspace !== undefined && hasPermissionToCurrentWorkspace === false) {
userStore.hasPermissionToWorkspace !== null &&
workspaceSlug &&
userStore.hasPermissionToWorkspace[workspaceSlug.toString()] === false
) {
return ( return (
<div className={`h-screen w-full overflow-hidden bg-custom-background-100`}> <div className={`h-screen w-full overflow-hidden bg-custom-background-100`}>
<div className="grid h-full place-items-center p-4"> <div className="grid h-full place-items-center p-4">

View File

@ -27,6 +27,7 @@ const ProjectCyclesPage: NextPage = observer(() => {
const [createModal, setCreateModal] = useState(false); const [createModal, setCreateModal] = useState(false);
// store // store
const { project: projectStore, cycle: cycleStore } = useMobxStore(); const { project: projectStore, cycle: cycleStore } = useMobxStore();
const { currentProjectDetails } = projectStore;
// router // router
const router = useRouter(); const router = useRouter();
const { workspaceSlug, projectId, peekCycle } = router.query as { const { workspaceSlug, projectId, peekCycle } = router.query as {
@ -78,19 +79,18 @@ const ProjectCyclesPage: NextPage = observer(() => {
} }
}, [projectId, cycleStore, handleCurrentView, handleCurrentLayout]); }, [projectId, cycleStore, handleCurrentView, handleCurrentLayout]);
const projectDetails = projectId ? projectStore.project_details[projectId] : null;
const cycleView = cycleStore?.cycleView; const cycleView = cycleStore?.cycleView;
const cycleLayout = cycleStore?.cycleLayout; const cycleLayout = cycleStore?.cycleLayout;
return ( return (
<AppLayout header={<CyclesHeader name={projectDetails?.name} />} withProjectWrapper> <AppLayout header={<CyclesHeader name={currentProjectDetails?.name} />} withProjectWrapper>
<CycleCreateUpdateModal <CycleCreateUpdateModal
workspaceSlug={workspaceSlug} workspaceSlug={workspaceSlug}
projectId={projectId} projectId={projectId}
isOpen={createModal} isOpen={createModal}
handleClose={() => setCreateModal(false)} handleClose={() => setCreateModal(false)}
/> />
{projectDetails?.total_cycles === 0 ? ( {currentProjectDetails?.total_cycles === 0 ? (
<div className="h-full grid place-items-center"> <div className="h-full grid place-items-center">
<EmptyState <EmptyState
title="Plan your project with cycles" title="Plan your project with cycles"

View File

@ -20,14 +20,14 @@ import { useMobxStore } from "lib/mobx/store-provider";
import { observer } from "mobx-react-lite"; import { observer } from "mobx-react-lite";
const GeneralSettings: NextPage = observer(() => { const GeneralSettings: NextPage = observer(() => {
// store
const { project: projectStore } = useMobxStore(); const { project: projectStore } = useMobxStore();
const { currentProjectDetails } = projectStore;
// states // states
const [selectProject, setSelectedProject] = useState<string | null>(null); const [selectProject, setSelectedProject] = useState<string | null>(null);
// router // router
const router = useRouter(); const router = useRouter();
const { workspaceSlug, projectId } = router.query; const { workspaceSlug, projectId } = router.query;
// derived values
const projectDetails = projectId ? projectStore.project_details[projectId.toString()] : null;
// api call to fetch project details // api call to fetch project details
useSWR( useSWR(
workspaceSlug && projectId ? "PROJECT_DETAILS" : null, workspaceSlug && projectId ? "PROJECT_DETAILS" : null,
@ -39,30 +39,34 @@ const GeneralSettings: NextPage = observer(() => {
// const currentNetwork = NETWORK_CHOICES.find((n) => n.key === projectDetails?.network); // const currentNetwork = NETWORK_CHOICES.find((n) => n.key === projectDetails?.network);
// const selectedNetwork = NETWORK_CHOICES.find((n) => n.key === watch("network")); // const selectedNetwork = NETWORK_CHOICES.find((n) => n.key === watch("network"));
const isAdmin = projectDetails?.member_role === 20; const isAdmin = currentProjectDetails?.member_role === 20;
return ( return (
<AppLayout header={<ProjectSettingHeader title="General Settings" />} withProjectWrapper> <AppLayout header={<ProjectSettingHeader title="General Settings" />} withProjectWrapper>
<ProjectSettingLayout> <ProjectSettingLayout>
{projectDetails && ( {currentProjectDetails && (
<DeleteProjectModal <DeleteProjectModal
project={projectDetails} project={currentProjectDetails}
isOpen={Boolean(selectProject)} isOpen={Boolean(selectProject)}
onClose={() => setSelectedProject(null)} onClose={() => setSelectedProject(null)}
/> />
)} )}
<div className={`pr-9 py-8 w-full overflow-y-auto ${isAdmin ? "" : "opacity-60"}`}> <div className={`pr-9 py-8 w-full overflow-y-auto ${isAdmin ? "" : "opacity-60"}`}>
{projectDetails && workspaceSlug ? ( {currentProjectDetails && workspaceSlug ? (
<ProjectDetailsForm project={projectDetails} workspaceSlug={workspaceSlug.toString()} isAdmin={isAdmin} /> <ProjectDetailsForm
project={currentProjectDetails}
workspaceSlug={workspaceSlug.toString()}
isAdmin={isAdmin}
/>
) : ( ) : (
<ProjectDetailsFormLoader /> <ProjectDetailsFormLoader />
)} )}
{isAdmin && ( {isAdmin && (
<DeleteProjectSection <DeleteProjectSection
projectDetails={projectDetails} projectDetails={currentProjectDetails}
handleDelete={() => setSelectedProject(projectDetails.id ?? null)} handleDelete={() => setSelectedProject(currentProjectDetails.id ?? null)}
/> />
)} )}
</div> </div>

View File

@ -41,6 +41,8 @@ export interface IProjectStore {
joinedProjects: IProject[]; joinedProjects: IProject[];
favoriteProjects: IProject[]; favoriteProjects: IProject[];
currentProjectDetails: IProject | undefined;
// actions // actions
setProjectId: (projectId: string) => void; setProjectId: (projectId: string) => void;
setSearchQuery: (query: string) => void; setSearchQuery: (query: string) => void;
@ -137,6 +139,8 @@ export class ProjectStore implements IProjectStore {
projectMembers: computed, projectMembers: computed,
projectEstimates: computed, projectEstimates: computed,
currentProjectDetails: computed,
joinedProjects: computed, joinedProjects: computed,
favoriteProjects: computed, favoriteProjects: computed,
@ -198,6 +202,11 @@ export class ProjectStore implements IProjectStore {
return this.projects?.[this.rootStore.workspace.workspaceSlug]; return this.projects?.[this.rootStore.workspace.workspaceSlug];
} }
get currentProjectDetails() {
if (!this.projectId) return;
return this.project_details[this.projectId];
}
get joinedProjects() { get joinedProjects() {
if (!this.rootStore.workspace.workspaceSlug) return []; if (!this.rootStore.workspace.workspaceSlug) return [];
return this.projects?.[this.rootStore.workspace.workspaceSlug]?.filter((p) => p.is_member); return this.projects?.[this.rootStore.workspace.workspaceSlug]?.filter((p) => p.is_member);

View File

@ -98,10 +98,7 @@ import {
InboxStore, InboxStore,
} from "store/inbox"; } from "store/inbox";
import { import { IMentionsStore, MentionsStore } from "store/editor";
IMentionsStore,
MentionsStore
} from "store/editor"
enableStaticRendering(typeof window === "undefined"); enableStaticRendering(typeof window === "undefined");

View File

@ -1,5 +1,5 @@
// mobx // mobx
import { action, observable, runInAction, makeObservable } from "mobx"; import { action, observable, runInAction, makeObservable, computed } from "mobx";
// services // services
import { ProjectService } from "services/project"; import { ProjectService } from "services/project";
import { UserService } from "services/user.service"; import { UserService } from "services/user.service";
@ -7,6 +7,7 @@ import { WorkspaceService } from "services/workspace.service";
// interfaces // interfaces
import { IUser, IUserSettings } from "types/users"; import { IUser, IUserSettings } from "types/users";
import { IWorkspaceMemberMe, IProjectMember } from "types"; import { IWorkspaceMemberMe, IProjectMember } from "types";
import { RootStore } from "./root";
export interface IUserStore { export interface IUserStore {
loader: boolean; loader: boolean;
@ -17,16 +18,28 @@ export interface IUserStore {
dashboardInfo: any; dashboardInfo: any;
workspaceMemberInfo: IWorkspaceMemberMe | null; workspaceMemberInfo: {
[workspaceSlug: string]: IWorkspaceMemberMe;
};
hasPermissionToWorkspace: { hasPermissionToWorkspace: {
[workspaceSlug: string]: boolean | null; [workspaceSlug: string]: boolean | null;
}; };
projectMemberInfo: IProjectMember | null; projectMemberInfo: {
[projectId: string]: IProjectMember;
};
hasPermissionToProject: { hasPermissionToProject: {
[projectId: string]: boolean | null; [projectId: string]: boolean | null;
}; };
currentProjectMemberInfo: IProjectMember | undefined;
currentWorkspaceMemberInfo: IWorkspaceMemberMe | undefined;
currentProjectRole: number | undefined;
currentWorkspaceRole: number | undefined;
hasPermissionToCurrentWorkspace: boolean | undefined;
hasPermissionToCurrentProject: boolean | undefined;
fetchCurrentUser: () => Promise<IUser>; fetchCurrentUser: () => Promise<IUser>;
fetchCurrentUserSettings: () => Promise<IUserSettings>; fetchCurrentUserSettings: () => Promise<IUserSettings>;
@ -48,14 +61,18 @@ class UserStore implements IUserStore {
dashboardInfo: any = null; dashboardInfo: any = null;
workspaceMemberInfo: IWorkspaceMemberMe | null = null; workspaceMemberInfo: {
[workspaceSlug: string]: IWorkspaceMemberMe;
} = {};
hasPermissionToWorkspace: { hasPermissionToWorkspace: {
[workspaceSlug: string]: boolean | null; [workspaceSlug: string]: boolean;
} = {}; } = {};
projectMemberInfo: IProjectMember | null = null; projectMemberInfo: {
[projectId: string]: IProjectMember;
} = {};
hasPermissionToProject: { hasPermissionToProject: {
[projectId: string]: boolean | null; [projectId: string]: boolean;
} = {}; } = {};
// root store // root store
rootStore; rootStore;
@ -64,7 +81,7 @@ class UserStore implements IUserStore {
workspaceService; workspaceService;
projectService; projectService;
constructor(_rootStore: any) { constructor(_rootStore: RootStore) {
makeObservable(this, { makeObservable(this, {
// observable // observable
loader: observable.ref, loader: observable.ref,
@ -78,7 +95,19 @@ class UserStore implements IUserStore {
// action // action
fetchCurrentUser: action, fetchCurrentUser: action,
fetchCurrentUserSettings: action, fetchCurrentUserSettings: action,
fetchUserDashboardInfo: action,
fetchUserWorkspaceInfo: action,
fetchUserProjectInfo: action,
updateTourCompleted: action,
updateCurrentUser: action,
updateCurrentUserTheme: action,
// computed // computed
currentProjectMemberInfo: computed,
currentWorkspaceMemberInfo: computed,
currentProjectRole: computed,
currentWorkspaceRole: computed,
hasPermissionToCurrentWorkspace: computed,
hasPermissionToCurrentProject: computed,
}); });
this.rootStore = _rootStore; this.rootStore = _rootStore;
this.userService = new UserService(); this.userService = new UserService();
@ -86,6 +115,36 @@ class UserStore implements IUserStore {
this.projectService = new ProjectService(); this.projectService = new ProjectService();
} }
get currentWorkspaceMemberInfo() {
if (!this.rootStore.workspace.workspaceSlug) return;
return this.workspaceMemberInfo[this.rootStore.workspace.workspaceSlug];
}
get currentWorkspaceRole() {
if (!this.rootStore.workspace.workspaceSlug) return;
return this.workspaceMemberInfo[this.rootStore.workspace.workspaceSlug].role;
}
get currentProjectMemberInfo() {
if (!this.rootStore.project.projectId) return;
return this.projectMemberInfo[this.rootStore.project.projectId];
}
get currentProjectRole() {
if (!this.rootStore.project.projectId) return;
return this.projectMemberInfo[this.rootStore.project.projectId].role;
}
get hasPermissionToCurrentWorkspace() {
if (!this.rootStore.workspace.workspaceSlug) return;
return this.hasPermissionToWorkspace[this.rootStore.workspace.workspaceSlug];
}
get hasPermissionToCurrentProject() {
if (!this.rootStore.project.projectId) return;
return this.hasPermissionToProject[this.rootStore.project.projectId];
}
fetchCurrentUser = async () => { fetchCurrentUser = async () => {
try { try {
const response = await this.userService.currentUser(); const response = await this.userService.currentUser();
@ -132,10 +191,13 @@ class UserStore implements IUserStore {
fetchUserWorkspaceInfo = async (workspaceSlug: string) => { fetchUserWorkspaceInfo = async (workspaceSlug: string) => {
try { try {
const response = await this.workspaceService.workspaceMemberMe(workspaceSlug.toString()); const response = await this.workspaceService.workspaceMemberMe(workspaceSlug);
runInAction(() => { runInAction(() => {
this.workspaceMemberInfo = response; this.workspaceMemberInfo = {
...this.workspaceMemberInfo,
[workspaceSlug]: response,
};
this.hasPermissionToWorkspace = { this.hasPermissionToWorkspace = {
...this.hasPermissionToWorkspace, ...this.hasPermissionToWorkspace,
[workspaceSlug]: true, [workspaceSlug]: true,
@ -158,7 +220,10 @@ class UserStore implements IUserStore {
const response = await this.projectService.projectMemberMe(workspaceSlug, projectId); const response = await this.projectService.projectMemberMe(workspaceSlug, projectId);
runInAction(() => { runInAction(() => {
this.projectMemberInfo = response; this.projectMemberInfo = {
...this.projectMemberInfo,
[projectId]: response,
};
this.hasPermissionToProject = { this.hasPermissionToProject = {
...this.hasPermissionToProject, ...this.hasPermissionToProject,
[projectId]: true, [projectId]: true,