User role validation across workspace and projects. (#3167)

* chore: remove `add link` button for guests & viewer in modules sidebar.

* chore: remove `+` (add view) icon for guests & viewer in `All Issues`.

* chore: remove `Start Project` button from Dashboard & Projects empty state for guests & viewers.

* chore: project level user role validation for empty states.
This commit is contained in:
Prateek Shourya 2023-12-18 13:25:03 +05:30 committed by GitHub
parent b7a0f3c693
commit 184db0156c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 108 additions and 67 deletions

View File

@ -19,7 +19,7 @@ type Props = {
icon?: any; icon?: any;
text: string; text: string;
onClick: () => void; onClick: () => void;
}; } | null;
disabled?: boolean; disabled?: boolean;
}; };

View File

@ -31,14 +31,18 @@ export const ProjectEmptyState: React.FC = observer(() => {
description: description:
"Redesign the Plane UI, Rebrand the company, or Launch the new fuel injection system are examples of issues that likely have sub-issues.", "Redesign the Plane UI, Rebrand the company, or Launch the new fuel injection system are examples of issues that likely have sub-issues.",
}} }}
primaryButton={{ primaryButton={
text: "Create your first issue", isEditingAllowed
icon: <PlusIcon className="h-3 w-3" strokeWidth={2} />, ? {
onClick: () => { text: "Create your first issue",
setTrackElement("PROJECT_EMPTY_STATE"); icon: <PlusIcon className="h-3 w-3" strokeWidth={2} />,
commandPaletteStore.toggleCreateIssueModal(true, EProjectStore.PROJECT); onClick: () => {
}, setTrackElement("PROJECT_EMPTY_STATE");
}} commandPaletteStore.toggleCreateIssueModal(true, EProjectStore.PROJECT);
},
}
: null
}
disabled={!isEditingAllowed} disabled={!isEditingAllowed}
/> />
</div> </div>

View File

@ -96,11 +96,15 @@ export const ModulesListView: React.FC = observer(() => {
description: description:
"A cart module, a chassis module, and a warehouse module are all good example of this grouping.", "A cart module, a chassis module, and a warehouse module are all good example of this grouping.",
}} }}
primaryButton={{ primaryButton={
icon: <Plus className="h-4 w-4" />, isEditingAllowed
text: "Build your first module", ? {
onClick: () => commandPaletteStore.toggleCreateModuleModal(true), icon: <Plus className="h-4 w-4" />,
}} text: "Build your first module",
onClick: () => commandPaletteStore.toggleCreateModuleModal(true),
}
: null
}
disabled={!isEditingAllowed} disabled={!isEditingAllowed}
/> />
)} )}

View File

@ -626,13 +626,15 @@ export const ModuleDetailsSidebar: React.FC<Props> = observer((props) => {
<Info className="h-3.5 w-3.5 stroke-[1.5] text-custom-text-300" /> <Info className="h-3.5 w-3.5 stroke-[1.5] text-custom-text-300" />
<span className="p-0.5 text-xs text-custom-text-300">No links added yet</span> <span className="p-0.5 text-xs text-custom-text-300">No links added yet</span>
</div> </div>
<button {isEditingAllowed && (
className="flex items-center gap-1.5 text-sm font-medium text-custom-primary-100" <button
onClick={() => setModuleLinkModal(true)} className="flex items-center gap-1.5 text-sm font-medium text-custom-primary-100"
> onClick={() => setModuleLinkModal(true)}
<Plus className="h-3 w-3" /> >
Add link <Plus className="h-3 w-3" />
</button> Add link
</button>
)}
</div> </div>
)} )}
</div> </div>

View File

@ -93,13 +93,17 @@ export const WorkspaceDashboardView = observer(() => {
direction: "right", direction: "right",
description: "A project could be a products roadmap, a marketing campaign, or launching a new car.", description: "A project could be a products roadmap, a marketing campaign, or launching a new car.",
}} }}
primaryButton={{ primaryButton={
text: "Build your first project", isEditingAllowed
onClick: () => { ? {
setTrackElement("DASHBOARD_PAGE"); text: "Build your first project",
commandPaletteStore.toggleCreateProjectModal(true); onClick: () => {
}, setTrackElement("DASHBOARD_PAGE");
}} commandPaletteStore.toggleCreateProjectModal(true);
},
}
: null
}
disabled={!isEditingAllowed} disabled={!isEditingAllowed}
/> />
) )

View File

@ -58,11 +58,15 @@ export const PagesListView: FC<IPagesListView> = observer(({ pages }) => {
"We wrote Parth and Meeras love story. You could write your projects mission, goals, and eventual vision.", "We wrote Parth and Meeras love story. You could write your projects mission, goals, and eventual vision.",
direction: "right", direction: "right",
}} }}
primaryButton={{ primaryButton={
icon: <Plus className="h-4 w-4" />, isEditingAllowed
text: "Create your first page", ? {
onClick: () => toggleCreatePageModal(true), icon: <Plus className="h-4 w-4" />,
}} text: "Create your first page",
onClick: () => toggleCreatePageModal(true),
}
: null
}
disabled={!isEditingAllowed} disabled={!isEditingAllowed}
/> />
)} )}

View File

@ -66,11 +66,15 @@ export const RecentPagesList: FC = observer(() => {
"We wrote Parth and Meeras love story. You could write your projects mission, goals, and eventual vision.", "We wrote Parth and Meeras love story. You could write your projects mission, goals, and eventual vision.",
direction: "right", direction: "right",
}} }}
primaryButton={{ primaryButton={
icon: <Plus className="h-4 w-4" />, isEditingAllowed
text: "Create your first page", ? {
onClick: () => commandPaletteStore.toggleCreatePageModal(true), icon: <Plus className="h-4 w-4" />,
}} text: "Create your first page",
onClick: () => commandPaletteStore.toggleCreatePageModal(true),
}
: null
}
disabled={!isEditingAllowed} disabled={!isEditingAllowed}
/> />
</> </>

View File

@ -67,13 +67,17 @@ export const ProjectCardList: FC<IProjectCardList> = observer((props) => {
direction: "right", direction: "right",
description: "A project could be a products roadmap, a marketing campaign, or launching a new car.", description: "A project could be a products roadmap, a marketing campaign, or launching a new car.",
}} }}
primaryButton={{ primaryButton={
text: "Start your first project", isEditingAllowed
onClick: () => { ? {
setTrackElement("PROJECTS_EMPTY_STATE"); text: "Start your first project",
commandPaletteStore.toggleCreateProjectModal(true); onClick: () => {
}, setTrackElement("PROJECTS_EMPTY_STATE");
}} commandPaletteStore.toggleCreateProjectModal(true);
},
}
: null
}
disabled={!isEditingAllowed} disabled={!isEditingAllowed}
/> />
)} )}

View File

@ -76,11 +76,15 @@ export const ProjectViewsList = observer(() => {
description: "You can create a view from here with as many properties as filters as you see fit.", description: "You can create a view from here with as many properties as filters as you see fit.",
direction: "right", direction: "right",
}} }}
primaryButton={{ primaryButton={
icon: <Plus size={14} strokeWidth={2} />, isEditingAllowed
text: "Build your first view", ? {
onClick: () => commandPaletteStore.toggleCreateViewModal(true), icon: <Plus size={14} strokeWidth={2} />,
}} text: "Build your first view",
onClick: () => commandPaletteStore.toggleCreateViewModal(true),
}
: null
}
disabled={!isEditingAllowed} disabled={!isEditingAllowed}
/> />
)} )}

View File

@ -9,7 +9,7 @@ import { CreateUpdateWorkspaceViewModal } from "components/workspace";
// icon // icon
import { Plus } from "lucide-react"; import { Plus } from "lucide-react";
// constants // constants
import { DEFAULT_GLOBAL_VIEWS_LIST } from "constants/workspace"; import { DEFAULT_GLOBAL_VIEWS_LIST, EUserWorkspaceRoles } from "constants/workspace";
export const GlobalViewsHeader: React.FC = observer(() => { export const GlobalViewsHeader: React.FC = observer(() => {
const [createViewModal, setCreateViewModal] = useState(false); const [createViewModal, setCreateViewModal] = useState(false);
@ -17,7 +17,10 @@ export const GlobalViewsHeader: React.FC = observer(() => {
const router = useRouter(); const router = useRouter();
const { workspaceSlug, globalViewId } = router.query; const { workspaceSlug, globalViewId } = router.query;
const { globalViews: globalViewsStore } = useMobxStore(); const {
globalViews: globalViewsStore,
user: { currentWorkspaceRole },
} = useMobxStore();
// bring the active view to the centre of the header // bring the active view to the centre of the header
useEffect(() => { useEffect(() => {
@ -28,11 +31,13 @@ export const GlobalViewsHeader: React.FC = observer(() => {
if (activeTabElement) activeTabElement.scrollIntoView({ behavior: "smooth", inline: "center" }); if (activeTabElement) activeTabElement.scrollIntoView({ behavior: "smooth", inline: "center" });
}, [globalViewId]); }, [globalViewId]);
const isAuthorizedUser = !!currentWorkspaceRole && currentWorkspaceRole >= EUserWorkspaceRoles.MEMBER;
const isTabSelected = (tabKey: string) => router.pathname.includes(tabKey); const isTabSelected = (tabKey: string) => router.pathname.includes(tabKey);
return ( return (
<> <>
<CreateUpdateWorkspaceViewModal isOpen={createViewModal} onClose={() => setCreateViewModal(false)} /> <CreateUpdateWorkspaceViewModal isOpen={createViewModal} onClose={() => setCreateViewModal(false)} />
<div className="group relative flex w-full items-center overflow-x-scroll border-b border-custom-border-200 px-4"> <div className="group relative flex w-full items-center overflow-x-scroll border-b border-custom-border-200 px-4 py-2">
{DEFAULT_GLOBAL_VIEWS_LIST.map((tab) => ( {DEFAULT_GLOBAL_VIEWS_LIST.map((tab) => (
<Link key={tab.key} href={`/${workspaceSlug}/workspace-views/${tab.key}`}> <Link key={tab.key} href={`/${workspaceSlug}/workspace-views/${tab.key}`}>
<span <span
@ -62,13 +67,15 @@ export const GlobalViewsHeader: React.FC = observer(() => {
</Link> </Link>
))} ))}
<button {isAuthorizedUser && (
type="button" <button
className="sticky -right-4 flex w-12 flex-shrink-0 items-center justify-center border-transparent bg-custom-background-100 py-3 hover:border-custom-border-200 hover:text-custom-text-400" type="button"
onClick={() => setCreateViewModal(true)} className="sticky -right-4 flex w-12 flex-shrink-0 items-center justify-center border-transparent bg-custom-background-100 hover:border-custom-border-200 hover:text-custom-text-400"
> onClick={() => setCreateViewModal(true)}
<Plus className="h-4 w-4 text-custom-primary-200" /> >
</button> <Plus className="h-4 w-4 text-custom-primary-200" />
</button>
)}
</div> </div>
</> </>
); );

View File

@ -103,13 +103,17 @@ const ProjectCyclesPage: NextPageWithLayout = observer(() => {
description: description:
"A sprint, an iteration, and or any other term you use for weekly or fortnightly tracking of work is a cycle.", "A sprint, an iteration, and or any other term you use for weekly or fortnightly tracking of work is a cycle.",
}} }}
primaryButton={{ primaryButton={
icon: <Plus className="h-4 w-4" />, isEditingAllowed
text: "Set your first cycle", ? {
onClick: () => { icon: <Plus className="h-4 w-4" />,
setCreateModal(true); text: "Set your first cycle",
}, onClick: () => {
}} setCreateModal(true);
},
}
: null
}
disabled={!isEditingAllowed} disabled={!isEditingAllowed}
/> />
</div> </div>