Impersonate UI (#4243)

* Impersonate UI

* Depracates Old admin required

* Move permission container to features

* Remove logic from dumb components

* Fix export from v2

* Fix type cast - add tab filter

* Redirect on authed pages

* Add key back

Co-authored-by: Joe Au-Yeung <65426560+joeauyeung@users.noreply.github.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Omar López <zomars@me.com>
This commit is contained in:
sean-brydon 2022-09-08 16:35:31 +01:00 committed by GitHub
parent 306a1212cf
commit 1bb987556d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 69 additions and 21 deletions

View File

@ -6,6 +6,7 @@ type AdminRequiredProps = {
children?: React.ReactNode;
};
/** @deprecated use PermssionContainer instead. Will delete once V2 goes live */
export const AdminRequired: FC<AdminRequiredProps> = ({ children, as, ...rest }) => {
const session = useSession();

View File

@ -2,8 +2,7 @@ import { signIn } from "next-auth/react";
import { useRef } from "react";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import Button from "@calcom/ui/Button";
import { TextField } from "@calcom/ui/form/fields";
import { TextField, Button } from "@calcom/ui/v2";
import Meta from "@calcom/ui/v2/core/Meta";
import { getLayout } from "@calcom/ui/v2/core/layouts/AdminLayout";
@ -13,7 +12,7 @@ function AdminView() {
return (
<>
<Meta title="impersonation" description="impersonation_description" />
<Meta title="admin" description="impersonation" />
<form
className="mb-6 w-full sm:w-1/2"
onSubmit={(e) => {
@ -23,20 +22,15 @@ function AdminView() {
console.log(res);
});
}}>
<TextField
name="Impersonate User"
addOnLeading={
<span className="inline-flex items-center rounded-l-sm border border-r-0 border-gray-300 bg-gray-50 px-3 text-sm text-gray-500">
{process.env.NEXT_PUBLIC_WEBSITE_URL}/
</span>
}
ref={usernameRef}
defaultValue={undefined}
/>
<p className="mt-2 text-sm text-gray-500" id="email-description">
{t("impersonate_user_tip")}
</p>
<div className="flex justify-end py-4">
<div className="flex items-center space-x-2">
<TextField
containerClassName="w-full"
name="Impersonate User"
addOnLeading={<>{process.env.NEXT_PUBLIC_WEBSITE_URL}/</>}
ref={usernameRef}
hint={t("impersonate_user_tip")}
defaultValue={undefined}
/>
<Button type="submit">{t("impersonate")}</Button>
</div>
</form>

View File

@ -0,0 +1,25 @@
import { useSession } from "next-auth/react";
import { FC, Fragment } from "react";
import { UserPermissionRole } from ".prisma/client";
type AdminRequiredProps = {
as?: keyof JSX.IntrinsicElements;
children?: React.ReactNode;
/**Not needed right now but will be useful if we ever expand our permission roles */
roleRequired?: UserPermissionRole;
};
export const PermissionContainer: FC<AdminRequiredProps> = ({
children,
as,
roleRequired = "ADMIN",
...rest
}) => {
const session = useSession();
// Admin can do everything
if (session.data?.user.role !== roleRequired || !UserPermissionRole.ADMIN) return null;
const Component = as ?? Fragment;
return <Component {...rest}>{children}</Component>;
};

View File

@ -1,12 +1,25 @@
import React, { ComponentProps } from "react";
import { useSession } from "next-auth/react";
import { useRouter } from "next/router";
import React, { ComponentProps, useEffect } from "react";
import Shell from "../Shell";
import SettingsLayout from "./SettingsLayout";
import { UserPermissionRole } from ".prisma/client";
export default function AdminLayout({
children,
...rest
}: { children: React.ReactNode } & ComponentProps<typeof Shell>) {
const session = useSession();
const router = useRouter();
// Force redirect on component level
useEffect(() => {
if (session.data?.user.role !== UserPermissionRole.ADMIN) {
router.replace("/settings");
}
}, [session, router]);
return (
<SettingsLayout {...rest}>
<div className="mx-auto flex max-w-4xl flex-row divide-y divide-gray-200 md:px-12">

View File

@ -1,3 +1,4 @@
import { useSession } from "next-auth/react";
import React, { ComponentProps, useState } from "react";
import { classNames } from "@calcom/lib";
@ -7,9 +8,10 @@ import Button from "@calcom/ui/v2/core/Button";
import { Icon } from "../../../Icon";
import { useMeta } from "../Meta";
import Shell from "../Shell";
import { VerticalTabItemProps } from "../navigation/tabs/VerticalTabItem";
import VerticalTabs, { VerticalTabItem } from "../navigation/tabs/VerticalTabs";
const tabs = [
const tabs: VerticalTabItemProps[] = [
{
name: "my_account",
href: "/settings/my-account",
@ -64,7 +66,6 @@ const tabs = [
name: "admin",
href: "/settings/admin",
icon: Icon.FiLock,
adminRequired: true,
children: [
//
{ name: "impersonation", href: "/settings/admin/impersonation" },
@ -74,9 +75,23 @@ const tabs = [
},
];
// The following keys are assigned to admin only
const adminRequiredKeys = ["admin"];
const useTabs = () => {
const session = useSession();
const isAdmin = session.data?.user.role === "ADMIN";
// check if name is in adminRequiredKeys
return tabs.filter((tab) => {
if (isAdmin) return true;
return !adminRequiredKeys.includes(tab.name);
});
};
const SettingsSidebarContainer = ({ className = "" }) => {
const tabsWithPermissions = useTabs();
return (
<VerticalTabs tabs={tabs} className={`py-3 pl-3 ${className}`}>
<VerticalTabs tabs={tabsWithPermissions} className={`py-3 pl-3 ${className}`}>
<VerticalTabItem
name="Settings"
href="/"