forked from github/plane
refactor: Instance admin setting and UI updates. (#2889)
* refactor: shift instance admin restriction content to seperate component. fix: instance components export logic. * style: fix sidebar dropdown `God Mode` icon padding. * style: update profile settings user dropdown menu width. * fix: update input type to `password` for Client Secret and API/ Access Key fields. * style: update loader design for all forms. * fix: typo * style: ui updates. * chore: add show/ hide button for all password fields.
This commit is contained in:
parent
3a1b722d31
commit
041c3af35a
@ -1,4 +1,4 @@
|
||||
import { FC } from "react";
|
||||
import { FC, useState } from "react";
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
// ui
|
||||
import { Button, Input } from "@plane/ui";
|
||||
@ -8,6 +8,8 @@ import { IFormattedInstanceConfiguration } from "types/instance";
|
||||
import useToast from "hooks/use-toast";
|
||||
// mobx store
|
||||
import { useMobxStore } from "lib/mobx/store-provider";
|
||||
// icons
|
||||
import { Eye, EyeOff } from "lucide-react";
|
||||
|
||||
export interface IInstanceAIForm {
|
||||
config: IFormattedInstanceConfiguration;
|
||||
@ -20,6 +22,8 @@ export interface AIFormValues {
|
||||
|
||||
export const InstanceAIForm: FC<IInstanceAIForm> = (props) => {
|
||||
const { config } = props;
|
||||
// states
|
||||
const [showPassword, setShowPassword] = useState(false);
|
||||
// store
|
||||
const { instance: instanceStore } = useMobxStore();
|
||||
// toast
|
||||
@ -55,7 +59,7 @@ export const InstanceAIForm: FC<IInstanceAIForm> = (props) => {
|
||||
<>
|
||||
<div className="grid grid-col grid-cols-1 lg:grid-cols-3 items-center justify-between gap-x-16 gap-y-8 w-full">
|
||||
<div className="flex flex-col gap-1">
|
||||
<h4 className="text-sm">GPT Engine</h4>
|
||||
<h4 className="text-sm">GPT_ENGINE</h4>
|
||||
<Controller
|
||||
control={control}
|
||||
name="GPT_ENGINE"
|
||||
@ -87,24 +91,41 @@ export const InstanceAIForm: FC<IInstanceAIForm> = (props) => {
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-1">
|
||||
<h4 className="text-sm">API Key</h4>
|
||||
<Controller
|
||||
control={control}
|
||||
name="OPENAI_API_KEY"
|
||||
render={({ field: { value, onChange, ref } }) => (
|
||||
<Input
|
||||
id="OPENAI_API_KEY"
|
||||
name="OPENAI_API_KEY"
|
||||
type="text"
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
ref={ref}
|
||||
hasError={Boolean(errors.OPENAI_API_KEY)}
|
||||
placeholder="sk-asddassdfasdefqsdfasd23das3dasdcasd"
|
||||
className="rounded-md font-medium w-full"
|
||||
/>
|
||||
<h4 className="text-sm">API key</h4>
|
||||
<div className="relative">
|
||||
<Controller
|
||||
control={control}
|
||||
name="OPENAI_API_KEY"
|
||||
render={({ field: { value, onChange, ref } }) => (
|
||||
<Input
|
||||
id="OPENAI_API_KEY"
|
||||
name="OPENAI_API_KEY"
|
||||
type={showPassword ? "text" : "password"}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
ref={ref}
|
||||
hasError={Boolean(errors.OPENAI_API_KEY)}
|
||||
placeholder="sk-asddassdfasdefqsdfasd23das3dasdcasd"
|
||||
className="rounded-md font-medium w-full !pr-10"
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
{showPassword ? (
|
||||
<button
|
||||
className="absolute right-3 top-2.5 flex items-center justify-center text-custom-text-400"
|
||||
onClick={() => setShowPassword(false)}
|
||||
>
|
||||
<EyeOff className="h-4 w-4" />
|
||||
</button>
|
||||
) : (
|
||||
<button
|
||||
className="absolute right-3 top-2.5 flex items-center justify-center text-custom-text-400"
|
||||
onClick={() => setShowPassword(true)}
|
||||
>
|
||||
<Eye className="h-4 w-4" />
|
||||
</button>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<p className="text-xs text-custom-text-400">
|
||||
You will find your API key{" "}
|
||||
<a
|
||||
@ -121,7 +142,7 @@ export const InstanceAIForm: FC<IInstanceAIForm> = (props) => {
|
||||
|
||||
<div className="flex items-center py-1">
|
||||
<Button variant="primary" onClick={handleSubmit(onSubmit)} loading={isSubmitting}>
|
||||
{isSubmitting ? "Saving..." : "Save Changes"}
|
||||
{isSubmitting ? "Saving..." : "Save changes"}
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
|
@ -25,6 +25,7 @@ export interface EmailFormValues {
|
||||
|
||||
export const InstanceEmailForm: FC<IInstanceEmailForm> = (props) => {
|
||||
const { config } = props;
|
||||
// states
|
||||
const [showPassword, setShowPassword] = useState(false);
|
||||
// store
|
||||
const { instance: instanceStore } = useMobxStore();
|
||||
@ -145,7 +146,7 @@ export const InstanceEmailForm: FC<IInstanceEmailForm> = (props) => {
|
||||
ref={ref}
|
||||
hasError={Boolean(errors.EMAIL_HOST_PASSWORD)}
|
||||
placeholder="Password"
|
||||
className="rounded-md font-medium w-full"
|
||||
className="rounded-md font-medium w-full !pr-10"
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
@ -222,7 +223,7 @@ export const InstanceEmailForm: FC<IInstanceEmailForm> = (props) => {
|
||||
|
||||
<div className="flex items-center py-1 max-w-4xl">
|
||||
<Button variant="primary" onClick={handleSubmit(onSubmit)} loading={isSubmitting}>
|
||||
{isSubmitting ? "Saving..." : "Save Changes"}
|
||||
{isSubmitting ? "Saving..." : "Save changes"}
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
|
@ -15,7 +15,7 @@ export interface IInstanceGeneralForm {
|
||||
|
||||
export interface GeneralFormValues {
|
||||
instance_name: string;
|
||||
is_telemetry_enabled: boolean;
|
||||
// is_telemetry_enabled: boolean;
|
||||
}
|
||||
|
||||
export const InstanceGeneralForm: FC<IInstanceGeneralForm> = (props) => {
|
||||
@ -32,7 +32,7 @@ export const InstanceGeneralForm: FC<IInstanceGeneralForm> = (props) => {
|
||||
} = useForm<GeneralFormValues>({
|
||||
defaultValues: {
|
||||
instance_name: instance.instance_name,
|
||||
is_telemetry_enabled: instance.is_telemetry_enabled,
|
||||
// is_telemetry_enabled: instance.is_telemetry_enabled,
|
||||
},
|
||||
});
|
||||
|
||||
@ -101,7 +101,7 @@ export const InstanceGeneralForm: FC<IInstanceGeneralForm> = (props) => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-12 pt-4">
|
||||
{/* <div className="flex items-center gap-12 pt-4">
|
||||
<div>
|
||||
<div className="text-custom-text-100 font-medium text-sm">Share anonymous usage instance</div>
|
||||
<div className="text-custom-text-300 font-normal text-xs">
|
||||
@ -115,11 +115,11 @@ export const InstanceGeneralForm: FC<IInstanceGeneralForm> = (props) => {
|
||||
render={({ field: { value, onChange } }) => <ToggleSwitch value={value} onChange={onChange} size="sm" />}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div> */}
|
||||
|
||||
<div className="flex items-center py-1">
|
||||
<Button variant="primary" size="md" onClick={handleSubmit(onSubmit)} loading={isSubmitting}>
|
||||
{isSubmitting ? "Saving..." : "Save Changes"}
|
||||
<Button variant="primary" onClick={handleSubmit(onSubmit)} loading={isSubmitting}>
|
||||
{isSubmitting ? "Saving..." : "Save changes"}
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { FC } from "react";
|
||||
import { FC, useState } from "react";
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
// ui
|
||||
import { Button, Input } from "@plane/ui";
|
||||
@ -9,7 +9,7 @@ import useToast from "hooks/use-toast";
|
||||
// mobx store
|
||||
import { useMobxStore } from "lib/mobx/store-provider";
|
||||
// icons
|
||||
import { Copy } from "lucide-react";
|
||||
import { Copy, Eye, EyeOff } from "lucide-react";
|
||||
|
||||
export interface IInstanceGithubConfigForm {
|
||||
config: IFormattedInstanceConfiguration;
|
||||
@ -22,6 +22,8 @@ export interface GithubConfigFormValues {
|
||||
|
||||
export const InstanceGithubConfigForm: FC<IInstanceGithubConfigForm> = (props) => {
|
||||
const { config } = props;
|
||||
// states
|
||||
const [showPassword, setShowPassword] = useState(false);
|
||||
// store
|
||||
const { instance: instanceStore } = useMobxStore();
|
||||
// toast
|
||||
@ -90,24 +92,42 @@ export const InstanceGithubConfigForm: FC<IInstanceGithubConfigForm> = (props) =
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex flex-col gap-1">
|
||||
<h4 className="text-sm">Client Secret</h4>
|
||||
<Controller
|
||||
control={control}
|
||||
name="GITHUB_CLIENT_SECRET"
|
||||
render={({ field: { value, onChange, ref } }) => (
|
||||
<Input
|
||||
id="GITHUB_CLIENT_SECRET"
|
||||
name="GITHUB_CLIENT_SECRET"
|
||||
type="text"
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
ref={ref}
|
||||
hasError={Boolean(errors.GITHUB_CLIENT_SECRET)}
|
||||
placeholder="9b0050f94ec1b744e32ce79ea4ffacd40d4119cb"
|
||||
className="rounded-md font-medium w-full"
|
||||
/>
|
||||
<h4 className="text-sm">Client secret</h4>
|
||||
<div className="relative">
|
||||
<Controller
|
||||
control={control}
|
||||
name="GITHUB_CLIENT_SECRET"
|
||||
render={({ field: { value, onChange, ref } }) => (
|
||||
<Input
|
||||
id="GITHUB_CLIENT_SECRET"
|
||||
name="GITHUB_CLIENT_SECRET"
|
||||
type={showPassword ? "text" : "password"}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
ref={ref}
|
||||
hasError={Boolean(errors.GITHUB_CLIENT_SECRET)}
|
||||
placeholder="9b0050f94ec1b744e32ce79ea4ffacd40d4119cb"
|
||||
className="rounded-md font-medium w-full !pr-10"
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
{showPassword ? (
|
||||
<button
|
||||
className="absolute right-3 top-2.5 flex items-center justify-center text-custom-text-400"
|
||||
onClick={() => setShowPassword(false)}
|
||||
>
|
||||
<EyeOff className="h-4 w-4" />
|
||||
</button>
|
||||
) : (
|
||||
<button
|
||||
className="absolute right-3 top-2.5 flex items-center justify-center text-custom-text-400"
|
||||
onClick={() => setShowPassword(true)}
|
||||
>
|
||||
<Eye className="h-4 w-4" />
|
||||
</button>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<p className="text-xs text-custom-text-400">
|
||||
Your client secret is also found in your{" "}
|
||||
<a
|
||||
@ -153,7 +173,7 @@ export const InstanceGithubConfigForm: FC<IInstanceGithubConfigForm> = (props) =
|
||||
<div className="flex flex-col gap-1">
|
||||
<div className="flex items-center">
|
||||
<Button variant="primary" onClick={handleSubmit(onSubmit)} loading={isSubmitting}>
|
||||
{isSubmitting ? "Saving..." : "Save Changes"}
|
||||
{isSubmitting ? "Saving..." : "Save changes"}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -90,7 +90,7 @@ export const InstanceGoogleConfigForm: FC<IInstanceGoogleConfigForm> = (props) =
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex flex-col gap-1">
|
||||
<h4 className="text-sm">Origin URL</h4>
|
||||
<h4 className="text-sm">JavaScript origin URL</h4>
|
||||
<Button
|
||||
variant="neutral-primary"
|
||||
className="py-2 flex justify-between items-center"
|
||||
@ -122,7 +122,7 @@ export const InstanceGoogleConfigForm: FC<IInstanceGoogleConfigForm> = (props) =
|
||||
<div className="flex flex-col gap-1">
|
||||
<div className="flex items-center">
|
||||
<Button variant="primary" onClick={handleSubmit(onSubmit)} loading={isSubmitting}>
|
||||
{isSubmitting ? "Saving..." : "Save Changes"}
|
||||
{isSubmitting ? "Saving..." : "Save changes"}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { FC } from "react";
|
||||
import { FC, useState } from "react";
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
// ui
|
||||
import { Button, Input } from "@plane/ui";
|
||||
@ -8,6 +8,8 @@ import { IFormattedInstanceConfiguration } from "types/instance";
|
||||
import useToast from "hooks/use-toast";
|
||||
// mobx store
|
||||
import { useMobxStore } from "lib/mobx/store-provider";
|
||||
// icons
|
||||
import { Eye, EyeOff } from "lucide-react";
|
||||
|
||||
export interface IInstanceImageConfigForm {
|
||||
config: IFormattedInstanceConfiguration;
|
||||
@ -19,6 +21,8 @@ export interface ImageConfigFormValues {
|
||||
|
||||
export const InstanceImageConfigForm: FC<IInstanceImageConfigForm> = (props) => {
|
||||
const { config } = props;
|
||||
// states
|
||||
const [showPassword, setShowPassword] = useState(false);
|
||||
// store
|
||||
const { instance: instanceStore } = useMobxStore();
|
||||
// toast
|
||||
@ -54,23 +58,40 @@ export const InstanceImageConfigForm: FC<IInstanceImageConfigForm> = (props) =>
|
||||
<div className="grid grid-col grid-cols-1 lg:grid-cols-2 items-center justify-between gap-x-16 gap-y-8 w-full">
|
||||
<div className="flex flex-col gap-1 max-w-md">
|
||||
<h4 className="text-sm">Access key from your Unsplash account</h4>
|
||||
<Controller
|
||||
control={control}
|
||||
name="UNSPLASH_ACCESS_KEY"
|
||||
render={({ field: { value, onChange, ref } }) => (
|
||||
<Input
|
||||
id="UNSPLASH_ACCESS_KEY"
|
||||
name="UNSPLASH_ACCESS_KEY"
|
||||
type="text"
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
ref={ref}
|
||||
hasError={Boolean(errors.UNSPLASH_ACCESS_KEY)}
|
||||
placeholder="oXgq-sdfadsaeweqasdfasdf3234234rassd"
|
||||
className="rounded-md font-medium w-full"
|
||||
/>
|
||||
<div className="relative">
|
||||
<Controller
|
||||
control={control}
|
||||
name="UNSPLASH_ACCESS_KEY"
|
||||
render={({ field: { value, onChange, ref } }) => (
|
||||
<Input
|
||||
id="UNSPLASH_ACCESS_KEY"
|
||||
name="UNSPLASH_ACCESS_KEY"
|
||||
type={showPassword ? "text" : "password"}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
ref={ref}
|
||||
hasError={Boolean(errors.UNSPLASH_ACCESS_KEY)}
|
||||
placeholder="oXgq-sdfadsaeweqasdfasdf3234234rassd"
|
||||
className="rounded-md font-medium w-full !pr-10"
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
{showPassword ? (
|
||||
<button
|
||||
className="absolute right-3 top-2.5 flex items-center justify-center text-custom-text-400"
|
||||
onClick={() => setShowPassword(false)}
|
||||
>
|
||||
<EyeOff className="h-4 w-4" />
|
||||
</button>
|
||||
) : (
|
||||
<button
|
||||
className="absolute right-3 top-2.5 flex items-center justify-center text-custom-text-400"
|
||||
onClick={() => setShowPassword(true)}
|
||||
>
|
||||
<Eye className="h-4 w-4" />
|
||||
</button>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<p className="text-xs text-custom-text-400">
|
||||
You will find your access key in your Unsplash developer console.{" "}
|
||||
<a
|
||||
@ -87,7 +108,7 @@ export const InstanceImageConfigForm: FC<IInstanceImageConfigForm> = (props) =>
|
||||
|
||||
<div className="flex items-center py-1">
|
||||
<Button variant="primary" onClick={handleSubmit(onSubmit)} loading={isSubmitting}>
|
||||
{isSubmitting ? "Saving..." : "Save Changes"}
|
||||
{isSubmitting ? "Saving..." : "Save changes"}
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
|
@ -2,3 +2,9 @@ export * from "./help-section";
|
||||
export * from "./sidebar-menu";
|
||||
export * from "./sidebar-dropdown";
|
||||
export * from "./general-form";
|
||||
export * from "./ai-form";
|
||||
export * from "./email-form";
|
||||
export * from "./github-config-form";
|
||||
export * from "./google-config-form";
|
||||
export * from "./image-config-form";
|
||||
export * from "./instance-admin-restriction";
|
||||
|
79
web/components/instance/instance-admin-restriction.tsx
Normal file
79
web/components/instance/instance-admin-restriction.tsx
Normal file
@ -0,0 +1,79 @@
|
||||
import { FC } from "react";
|
||||
import Link from "next/link";
|
||||
import Image from "next/image";
|
||||
// images
|
||||
import AccessDeniedImg from "public/auth/access-denied.svg";
|
||||
// ui
|
||||
import { Button } from "@plane/ui";
|
||||
// icons
|
||||
import { LayoutGrid } from "lucide-react";
|
||||
|
||||
interface InstanceAdminRestrictionProps {
|
||||
redirectWorkspaceSlug: string;
|
||||
}
|
||||
|
||||
export const InstanceAdminRestriction: FC<InstanceAdminRestrictionProps> = ({ redirectWorkspaceSlug }) => (
|
||||
<div className={`my-8 w-full flex flex-col gap-4 items-center justify-center overflow-hidden`}>
|
||||
<div className="w-3/5 bg-custom-background-90">
|
||||
<div className="grid h-full place-items-center p-2 pb-0">
|
||||
<div className="text-center">
|
||||
<Image src={AccessDeniedImg} height="250" width="550" alt="AccessDeniedImg" />
|
||||
<h3 className="text-3xl font-semibold">God mode needs a god role</h3>
|
||||
<p className="text-base text-custom-text-300">Doesn’t look like you have that role.</p>
|
||||
</div>
|
||||
<div className="flex flex-col gap-2 my-8 text-center">
|
||||
<div>
|
||||
<p className="font-medium text-xs text-custom-text-400 tracking-tight">Do we have a god role?</p>
|
||||
<p className="text-custom-text-300 text-sm">Yes.</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-medium text-xs text-custom-text-400 tracking-tight">Do we call it god role?</p>
|
||||
<p className="text-custom-text-300 text-sm">No. Obviously not.</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-medium text-xs text-custom-text-400 tracking-tight">Can you get it?</p>
|
||||
<p className="text-custom-text-300 text-sm">Maybe. Ask your god.</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-medium text-xs text-custom-text-400 tracking-tight">
|
||||
Are we being intentionally cryptic?
|
||||
</p>
|
||||
<p className="text-custom-text-300 text-sm">Yes.</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-medium text-xs text-custom-text-400 tracking-tight">
|
||||
Is this for the security of your workspaces?
|
||||
</p>
|
||||
<p className="text-custom-text-300 text-sm">Absolutely!</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-medium text-xs text-custom-text-400 tracking-tight">
|
||||
Are you the god here and still seeing this?
|
||||
</p>
|
||||
<p className="text-custom-text-300 text-sm">
|
||||
Sorry, God.{" "}
|
||||
<a
|
||||
href="https://discord.com/channels/1031547764020084846/1094927053867995176"
|
||||
target="_blank"
|
||||
className="text-custom-primary-100 font-medium hover:underline"
|
||||
rel="noreferrer"
|
||||
>
|
||||
Talk to us.
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center justify-center gap-2">
|
||||
<Link href={`/${redirectWorkspaceSlug}`}>
|
||||
<a>
|
||||
<Button variant="primary" size="sm">
|
||||
<LayoutGrid width={16} height={16} />
|
||||
To the workspace
|
||||
</Button>
|
||||
</a>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
);
|
@ -7,7 +7,7 @@ import { mutate } from "swr";
|
||||
// components
|
||||
import { Menu, Transition } from "@headlessui/react";
|
||||
// icons
|
||||
import { Cog, LogIn, LogOut, Settings } from "lucide-react";
|
||||
import { LogIn, LogOut, Settings, UserCog2 } from "lucide-react";
|
||||
// mobx store
|
||||
import { useMobxStore } from "lib/mobx/store-provider";
|
||||
// hooks
|
||||
@ -64,12 +64,12 @@ export const InstanceSidebarDropdown = observer(() => {
|
||||
<div className="flex items-center gap-x-5 gap-y-2 px-4 py-3.5 max-h-[3.75rem] border-b border-custom-sidebar-border-200">
|
||||
<div className="w-full h-full truncate">
|
||||
<div
|
||||
className={`flex flex-grow items-center gap-x-2 rounded p-1 truncate ${
|
||||
className={`flex flex-grow items-center gap-x-2 rounded py-1 truncate ${
|
||||
sidebarCollapsed ? "justify-center" : ""
|
||||
}`}
|
||||
>
|
||||
<div className="flex-shrink-0 flex items-center justify-center h-7 w-7 rounded bg-custom-sidebar-background-80">
|
||||
<Cog className="h-5 w-5 text-custom-text-200" />
|
||||
<div className="flex-shrink-0 flex items-center justify-center h-7 w-7 rounded bg-custom-sidebar-background-80">
|
||||
<UserCog2 className="h-5 w-5 text-custom-text-200" />
|
||||
</div>
|
||||
|
||||
{!sidebarCollapsed && (
|
||||
|
@ -16,9 +16,9 @@ const INSTANCE_ADMIN_LINKS = [
|
||||
},
|
||||
{
|
||||
Icon: Mail,
|
||||
name: "Mail",
|
||||
name: "Email",
|
||||
description: "Set up emails to your users",
|
||||
href: `/god-mode/mail`,
|
||||
href: `/god-mode/email`,
|
||||
},
|
||||
{
|
||||
Icon: Lock,
|
||||
@ -28,7 +28,7 @@ const INSTANCE_ADMIN_LINKS = [
|
||||
},
|
||||
{
|
||||
Icon: BrainCog,
|
||||
name: "OpenAI",
|
||||
name: "Artificial intelligence",
|
||||
description: "Configure your OpenAI creds",
|
||||
href: `/god-mode/ai`,
|
||||
},
|
||||
|
@ -1,15 +1,9 @@
|
||||
import { FC, ReactNode } from "react";
|
||||
import Link from "next/link";
|
||||
import Image from "next/image";
|
||||
import { observer } from "mobx-react-lite";
|
||||
// icons
|
||||
import { LayoutGrid } from "lucide-react";
|
||||
// ui
|
||||
import { Button } from "@plane/ui";
|
||||
// hooks
|
||||
import { useMobxStore } from "lib/mobx/store-provider";
|
||||
// images
|
||||
import AccessDeniedImg from "public/auth/access-denied.svg";
|
||||
// components
|
||||
import { InstanceAdminRestriction } from "components/instance";
|
||||
|
||||
export interface IAdminAuthWrapper {
|
||||
children: ReactNode;
|
||||
@ -32,71 +26,7 @@ export const AdminAuthWrapper: FC<IAdminAuthWrapper> = observer(({ children }) =
|
||||
|
||||
// if user does not have admin access to the instance
|
||||
if (isUserInstanceAdmin !== undefined && isUserInstanceAdmin === false) {
|
||||
return (
|
||||
<div className={`my-8 w-full flex flex-col gap-4 items-center justify-center overflow-hidden`}>
|
||||
<div className="w-3/5 bg-custom-background-90">
|
||||
<div className="grid h-full place-items-center p-2 pb-0">
|
||||
<div className="text-center">
|
||||
<Image src={AccessDeniedImg} height="250" width="550" alt="AccessDeniedImg" />
|
||||
<h3 className="text-3xl font-semibold">God mode needs a god role</h3>
|
||||
<p className="text-base text-custom-text-300">Doesn’t look like you have that role.</p>
|
||||
</div>
|
||||
<div className="flex flex-col gap-2 my-8 text-center">
|
||||
<div>
|
||||
<p className="font-medium text-xs text-custom-text-400 tracking-tight">Do we have a god role?</p>
|
||||
<p className="text-custom-text-300 text-sm">Yes.</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-medium text-xs text-custom-text-400 tracking-tight">Do we call it god role?</p>
|
||||
<p className="text-custom-text-300 text-sm">No. Obviously not.</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-medium text-xs text-custom-text-400 tracking-tight">Can you get it?</p>
|
||||
<p className="text-custom-text-300 text-sm">Maybe. Ask your god.</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-medium text-xs text-custom-text-400 tracking-tight">
|
||||
Are we being intentionally cryptic?
|
||||
</p>
|
||||
<p className="text-custom-text-300 text-sm">Yes.</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-medium text-xs text-custom-text-400 tracking-tight">
|
||||
Is this for the security of your workspaces?
|
||||
</p>
|
||||
<p className="text-custom-text-300 text-sm">Absolutely!</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-medium text-xs text-custom-text-400 tracking-tight">
|
||||
Are you the god here and still seeing this?
|
||||
</p>
|
||||
<p className="text-custom-text-300 text-sm">
|
||||
Sorry, God.{" "}
|
||||
<a
|
||||
href="https://discord.com/channels/1031547764020084846/1094927053867995176"
|
||||
target="_blank"
|
||||
className="text-custom-primary-100 font-medium hover:underline"
|
||||
rel="noreferrer"
|
||||
>
|
||||
Talk to us.
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center justify-center gap-2">
|
||||
<Link href={`/${redirectWorkspaceSlug}`}>
|
||||
<a>
|
||||
<Button variant="primary" size="sm">
|
||||
<LayoutGrid width={16} height={16} />
|
||||
To the workspace
|
||||
</Button>
|
||||
</a>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
return <InstanceAdminRestriction redirectWorkspaceSlug={redirectWorkspaceSlug} />;
|
||||
}
|
||||
|
||||
return <>{children}</>;
|
||||
|
@ -119,7 +119,7 @@ export const ProfileLayoutSidebar = observer(() => {
|
||||
leaveFrom="transform opacity-100 scale-100"
|
||||
leaveTo="transform opacity-0 scale-95"
|
||||
>
|
||||
<Menu.Items className="absolute left-0 z-20 mt-1 rounded-md border border-custom-sidebar-border-200 bg-custom-sidebar-background-100 px-1 py-2 shadow-custom-shadow-rg text-xs space-y-2 outline-none">
|
||||
<Menu.Items className="absolute left-0 z-20 mt-1 w-52 rounded-md border border-custom-sidebar-border-200 bg-custom-sidebar-background-100 px-1 py-2 shadow-custom-shadow-rg text-xs space-y-2 outline-none">
|
||||
<span className="px-2 text-custom-sidebar-text-200">{currentUser?.email}</span>
|
||||
<Menu.Item
|
||||
as="button"
|
||||
|
@ -12,7 +12,7 @@ import { Loader } from "@plane/ui";
|
||||
// icons
|
||||
import { Lightbulb } from "lucide-react";
|
||||
// components
|
||||
import { InstanceAIForm } from "components/instance/ai-form";
|
||||
import { InstanceAIForm } from "components/instance";
|
||||
|
||||
const InstanceAdminAIPage: NextPageWithLayout = observer(() => {
|
||||
// store
|
||||
@ -46,9 +46,11 @@ const InstanceAdminAIPage: NextPageWithLayout = observer(() => {
|
||||
</>
|
||||
) : (
|
||||
<Loader className="space-y-4">
|
||||
<Loader.Item height="50px" width="50%" />
|
||||
<Loader.Item height="50px" width="50%" />
|
||||
<Loader.Item height="50px" width="25%" />
|
||||
<div className="grid grid-cols-2 gap-y-4 gap-x-8">
|
||||
<Loader.Item height="50px" />
|
||||
<Loader.Item height="50px" />
|
||||
</div>
|
||||
<Loader.Item height="50px" />
|
||||
</Loader>
|
||||
)}
|
||||
</div>
|
||||
|
@ -13,8 +13,7 @@ import useToast from "hooks/use-toast";
|
||||
// ui
|
||||
import { Loader, ToggleSwitch } from "@plane/ui";
|
||||
// components
|
||||
import { InstanceGoogleConfigForm } from "components/instance/google-config-form";
|
||||
import { InstanceGithubConfigForm } from "components/instance/github-config-form";
|
||||
import { InstanceGithubConfigForm, InstanceGoogleConfigForm } from "components/instance";
|
||||
|
||||
const InstanceAdminAuthorizationPage: NextPageWithLayout = observer(() => {
|
||||
// store
|
||||
@ -168,13 +167,11 @@ const InstanceAdminAuthorizationPage: NextPageWithLayout = observer(() => {
|
||||
</>
|
||||
) : (
|
||||
<Loader className="space-y-4">
|
||||
<Loader.Item height="50px" width="50%" />
|
||||
<Loader.Item height="50px" width="50%" />
|
||||
<Loader.Item height="50px" width="50%" />
|
||||
<div className="grid grid-cols-2 gap-y-4 gap-x-8">
|
||||
<Loader.Item height="50px" />
|
||||
<Loader.Item height="50px" />
|
||||
</div>
|
||||
<Loader.Item height="50px" />
|
||||
<Loader.Item height="50px" width="25%" />
|
||||
<Loader.Item height="50px" />
|
||||
<Loader.Item height="50px" width="25%" />
|
||||
</Loader>
|
||||
)}
|
||||
</div>
|
||||
|
@ -10,9 +10,9 @@ import { useMobxStore } from "lib/mobx/store-provider";
|
||||
// ui
|
||||
import { Loader } from "@plane/ui";
|
||||
// components
|
||||
import { InstanceEmailForm } from "components/instance/email-form";
|
||||
import { InstanceEmailForm } from "components/instance";
|
||||
|
||||
const InstanceAdminMailPage: NextPageWithLayout = observer(() => {
|
||||
const InstanceAdminEmailPage: NextPageWithLayout = observer(() => {
|
||||
// store
|
||||
const {
|
||||
instance: { fetchInstanceConfigurations, formattedConfig },
|
||||
@ -36,19 +36,19 @@ const InstanceAdminMailPage: NextPageWithLayout = observer(() => {
|
||||
<InstanceEmailForm config={formattedConfig} />
|
||||
) : (
|
||||
<Loader className="space-y-4">
|
||||
<div className="grid grid-cols-2 gap-y-4 gap-x-8">
|
||||
<Loader.Item height="50px" />
|
||||
<Loader.Item height="50px" />
|
||||
</div>
|
||||
<Loader.Item height="50px" />
|
||||
<Loader.Item height="50px" />
|
||||
<Loader.Item height="50px" width="25%" />
|
||||
<Loader.Item height="50px" width="25%" />
|
||||
<Loader.Item height="50px" width="25%" />
|
||||
</Loader>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
InstanceAdminMailPage.getLayout = function getLayout(page: ReactElement) {
|
||||
InstanceAdminEmailPage.getLayout = function getLayout(page: ReactElement) {
|
||||
return <InstanceAdminLayout>{page}</InstanceAdminLayout>;
|
||||
};
|
||||
|
||||
export default InstanceAdminMailPage;
|
||||
export default InstanceAdminEmailPage;
|
@ -10,7 +10,7 @@ import { useMobxStore } from "lib/mobx/store-provider";
|
||||
// ui
|
||||
import { Loader } from "@plane/ui";
|
||||
// components
|
||||
import { InstanceImageConfigForm } from "components/instance/image-config-form";
|
||||
import { InstanceImageConfigForm } from "components/instance";
|
||||
|
||||
const InstanceAdminImagePage: NextPageWithLayout = observer(() => {
|
||||
// store
|
||||
@ -32,8 +32,11 @@ const InstanceAdminImagePage: NextPageWithLayout = observer(() => {
|
||||
<InstanceImageConfigForm config={formattedConfig} />
|
||||
) : (
|
||||
<Loader className="space-y-4">
|
||||
<div className="grid grid-cols-2 gap-y-4 gap-x-8">
|
||||
<Loader.Item height="50px" />
|
||||
<Loader.Item height="50px" />
|
||||
</div>
|
||||
<Loader.Item height="50px" />
|
||||
<Loader.Item height="50px" width="25%" />
|
||||
</Loader>
|
||||
)}
|
||||
</div>
|
||||
|
@ -34,9 +34,11 @@ const InstanceAdminPage: NextPageWithLayout = observer(() => {
|
||||
<InstanceGeneralForm instance={instance} instanceAdmins={instanceAdmins} />
|
||||
) : (
|
||||
<Loader className="space-y-4">
|
||||
<div className="grid grid-cols-2 gap-y-4 gap-x-8">
|
||||
<Loader.Item height="50px" />
|
||||
<Loader.Item height="50px" />
|
||||
</div>
|
||||
<Loader.Item height="50px" />
|
||||
<Loader.Item height="50px" width="50%" />
|
||||
<Loader.Item height="50px" width="25%" />
|
||||
</Loader>
|
||||
)}
|
||||
</div>
|
||||
|
Loading…
Reference in New Issue
Block a user