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={
isEditingAllowed
? {
text: "Create your first issue", text: "Create your first issue",
icon: <PlusIcon className="h-3 w-3" strokeWidth={2} />, icon: <PlusIcon className="h-3 w-3" strokeWidth={2} />,
onClick: () => { onClick: () => {
setTrackElement("PROJECT_EMPTY_STATE"); setTrackElement("PROJECT_EMPTY_STATE");
commandPaletteStore.toggleCreateIssueModal(true, EProjectStore.PROJECT); 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={
isEditingAllowed
? {
icon: <Plus className="h-4 w-4" />, icon: <Plus className="h-4 w-4" />,
text: "Build your first module", text: "Build your first module",
onClick: () => commandPaletteStore.toggleCreateModuleModal(true), onClick: () => commandPaletteStore.toggleCreateModuleModal(true),
}} }
: null
}
disabled={!isEditingAllowed} disabled={!isEditingAllowed}
/> />
)} )}

View File

@ -626,6 +626,7 @@ 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>
{isEditingAllowed && (
<button <button
className="flex items-center gap-1.5 text-sm font-medium text-custom-primary-100" className="flex items-center gap-1.5 text-sm font-medium text-custom-primary-100"
onClick={() => setModuleLinkModal(true)} onClick={() => setModuleLinkModal(true)}
@ -633,6 +634,7 @@ export const ModuleDetailsSidebar: React.FC<Props> = observer((props) => {
<Plus className="h-3 w-3" /> <Plus className="h-3 w-3" />
Add link Add link
</button> </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={
isEditingAllowed
? {
text: "Build your first project", text: "Build your first project",
onClick: () => { onClick: () => {
setTrackElement("DASHBOARD_PAGE"); setTrackElement("DASHBOARD_PAGE");
commandPaletteStore.toggleCreateProjectModal(true); 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={
isEditingAllowed
? {
icon: <Plus className="h-4 w-4" />, icon: <Plus className="h-4 w-4" />,
text: "Create your first page", text: "Create your first page",
onClick: () => toggleCreatePageModal(true), 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={
isEditingAllowed
? {
icon: <Plus className="h-4 w-4" />, icon: <Plus className="h-4 w-4" />,
text: "Create your first page", text: "Create your first page",
onClick: () => commandPaletteStore.toggleCreatePageModal(true), 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={
isEditingAllowed
? {
text: "Start your first project", text: "Start your first project",
onClick: () => { onClick: () => {
setTrackElement("PROJECTS_EMPTY_STATE"); setTrackElement("PROJECTS_EMPTY_STATE");
commandPaletteStore.toggleCreateProjectModal(true); 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={
isEditingAllowed
? {
icon: <Plus size={14} strokeWidth={2} />, icon: <Plus size={14} strokeWidth={2} />,
text: "Build your first view", text: "Build your first view",
onClick: () => commandPaletteStore.toggleCreateViewModal(true), 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>
))} ))}
{isAuthorizedUser && (
<button <button
type="button" type="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" 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)} onClick={() => setCreateViewModal(true)}
> >
<Plus className="h-4 w-4 text-custom-primary-200" /> <Plus className="h-4 w-4 text-custom-primary-200" />
</button> </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={
isEditingAllowed
? {
icon: <Plus className="h-4 w-4" />, icon: <Plus className="h-4 w-4" />,
text: "Set your first cycle", text: "Set your first cycle",
onClick: () => { onClick: () => {
setCreateModal(true); setCreateModal(true);
}, },
}} }
: null
}
disabled={!isEditingAllowed} disabled={!isEditingAllowed}
/> />
</div> </div>