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
10c52bf89b
commit
06d3cd7e73
@ -1,4 +1,4 @@
|
|||||||
import { FC } from "react";
|
import { FC, useState } from "react";
|
||||||
import { Controller, useForm } from "react-hook-form";
|
import { Controller, useForm } from "react-hook-form";
|
||||||
// ui
|
// ui
|
||||||
import { Button, Input } from "@plane/ui";
|
import { Button, Input } from "@plane/ui";
|
||||||
@ -8,6 +8,8 @@ import { IFormattedInstanceConfiguration } from "types/instance";
|
|||||||
import useToast from "hooks/use-toast";
|
import useToast from "hooks/use-toast";
|
||||||
// mobx store
|
// mobx store
|
||||||
import { useMobxStore } from "lib/mobx/store-provider";
|
import { useMobxStore } from "lib/mobx/store-provider";
|
||||||
|
// icons
|
||||||
|
import { Eye, EyeOff } from "lucide-react";
|
||||||
|
|
||||||
export interface IInstanceAIForm {
|
export interface IInstanceAIForm {
|
||||||
config: IFormattedInstanceConfiguration;
|
config: IFormattedInstanceConfiguration;
|
||||||
@ -20,6 +22,8 @@ export interface AIFormValues {
|
|||||||
|
|
||||||
export const InstanceAIForm: FC<IInstanceAIForm> = (props) => {
|
export const InstanceAIForm: FC<IInstanceAIForm> = (props) => {
|
||||||
const { config } = props;
|
const { config } = props;
|
||||||
|
// states
|
||||||
|
const [showPassword, setShowPassword] = useState(false);
|
||||||
// store
|
// store
|
||||||
const { instance: instanceStore } = useMobxStore();
|
const { instance: instanceStore } = useMobxStore();
|
||||||
// toast
|
// 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="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">
|
<div className="flex flex-col gap-1">
|
||||||
<h4 className="text-sm">GPT Engine</h4>
|
<h4 className="text-sm">GPT_ENGINE</h4>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="GPT_ENGINE"
|
name="GPT_ENGINE"
|
||||||
@ -87,24 +91,41 @@ export const InstanceAIForm: FC<IInstanceAIForm> = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-col gap-1">
|
<div className="flex flex-col gap-1">
|
||||||
<h4 className="text-sm">API Key</h4>
|
<h4 className="text-sm">API key</h4>
|
||||||
<Controller
|
<div className="relative">
|
||||||
control={control}
|
<Controller
|
||||||
name="OPENAI_API_KEY"
|
control={control}
|
||||||
render={({ field: { value, onChange, ref } }) => (
|
name="OPENAI_API_KEY"
|
||||||
<Input
|
render={({ field: { value, onChange, ref } }) => (
|
||||||
id="OPENAI_API_KEY"
|
<Input
|
||||||
name="OPENAI_API_KEY"
|
id="OPENAI_API_KEY"
|
||||||
type="text"
|
name="OPENAI_API_KEY"
|
||||||
value={value}
|
type={showPassword ? "text" : "password"}
|
||||||
onChange={onChange}
|
value={value}
|
||||||
ref={ref}
|
onChange={onChange}
|
||||||
hasError={Boolean(errors.OPENAI_API_KEY)}
|
ref={ref}
|
||||||
placeholder="sk-asddassdfasdefqsdfasd23das3dasdcasd"
|
hasError={Boolean(errors.OPENAI_API_KEY)}
|
||||||
className="rounded-md font-medium w-full"
|
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">
|
<p className="text-xs text-custom-text-400">
|
||||||
You will find your API key{" "}
|
You will find your API key{" "}
|
||||||
<a
|
<a
|
||||||
@ -121,7 +142,7 @@ export const InstanceAIForm: FC<IInstanceAIForm> = (props) => {
|
|||||||
|
|
||||||
<div className="flex items-center py-1">
|
<div className="flex items-center py-1">
|
||||||
<Button variant="primary" onClick={handleSubmit(onSubmit)} loading={isSubmitting}>
|
<Button variant="primary" onClick={handleSubmit(onSubmit)} loading={isSubmitting}>
|
||||||
{isSubmitting ? "Saving..." : "Save Changes"}
|
{isSubmitting ? "Saving..." : "Save changes"}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
@ -25,6 +25,7 @@ export interface EmailFormValues {
|
|||||||
|
|
||||||
export const InstanceEmailForm: FC<IInstanceEmailForm> = (props) => {
|
export const InstanceEmailForm: FC<IInstanceEmailForm> = (props) => {
|
||||||
const { config } = props;
|
const { config } = props;
|
||||||
|
// states
|
||||||
const [showPassword, setShowPassword] = useState(false);
|
const [showPassword, setShowPassword] = useState(false);
|
||||||
// store
|
// store
|
||||||
const { instance: instanceStore } = useMobxStore();
|
const { instance: instanceStore } = useMobxStore();
|
||||||
@ -145,7 +146,7 @@ export const InstanceEmailForm: FC<IInstanceEmailForm> = (props) => {
|
|||||||
ref={ref}
|
ref={ref}
|
||||||
hasError={Boolean(errors.EMAIL_HOST_PASSWORD)}
|
hasError={Boolean(errors.EMAIL_HOST_PASSWORD)}
|
||||||
placeholder="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">
|
<div className="flex items-center py-1 max-w-4xl">
|
||||||
<Button variant="primary" onClick={handleSubmit(onSubmit)} loading={isSubmitting}>
|
<Button variant="primary" onClick={handleSubmit(onSubmit)} loading={isSubmitting}>
|
||||||
{isSubmitting ? "Saving..." : "Save Changes"}
|
{isSubmitting ? "Saving..." : "Save changes"}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
@ -15,7 +15,7 @@ export interface IInstanceGeneralForm {
|
|||||||
|
|
||||||
export interface GeneralFormValues {
|
export interface GeneralFormValues {
|
||||||
instance_name: string;
|
instance_name: string;
|
||||||
is_telemetry_enabled: boolean;
|
// is_telemetry_enabled: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const InstanceGeneralForm: FC<IInstanceGeneralForm> = (props) => {
|
export const InstanceGeneralForm: FC<IInstanceGeneralForm> = (props) => {
|
||||||
@ -32,7 +32,7 @@ export const InstanceGeneralForm: FC<IInstanceGeneralForm> = (props) => {
|
|||||||
} = useForm<GeneralFormValues>({
|
} = useForm<GeneralFormValues>({
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
instance_name: instance.instance_name,
|
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>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center gap-12 pt-4">
|
{/* <div className="flex items-center gap-12 pt-4">
|
||||||
<div>
|
<div>
|
||||||
<div className="text-custom-text-100 font-medium text-sm">Share anonymous usage instance</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">
|
<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" />}
|
render={({ field: { value, onChange } }) => <ToggleSwitch value={value} onChange={onChange} size="sm" />}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div> */}
|
||||||
|
|
||||||
<div className="flex items-center py-1">
|
<div className="flex items-center py-1">
|
||||||
<Button variant="primary" size="md" onClick={handleSubmit(onSubmit)} loading={isSubmitting}>
|
<Button variant="primary" onClick={handleSubmit(onSubmit)} loading={isSubmitting}>
|
||||||
{isSubmitting ? "Saving..." : "Save Changes"}
|
{isSubmitting ? "Saving..." : "Save changes"}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { FC } from "react";
|
import { FC, useState } from "react";
|
||||||
import { Controller, useForm } from "react-hook-form";
|
import { Controller, useForm } from "react-hook-form";
|
||||||
// ui
|
// ui
|
||||||
import { Button, Input } from "@plane/ui";
|
import { Button, Input } from "@plane/ui";
|
||||||
@ -9,7 +9,7 @@ import useToast from "hooks/use-toast";
|
|||||||
// mobx store
|
// mobx store
|
||||||
import { useMobxStore } from "lib/mobx/store-provider";
|
import { useMobxStore } from "lib/mobx/store-provider";
|
||||||
// icons
|
// icons
|
||||||
import { Copy } from "lucide-react";
|
import { Copy, Eye, EyeOff } from "lucide-react";
|
||||||
|
|
||||||
export interface IInstanceGithubConfigForm {
|
export interface IInstanceGithubConfigForm {
|
||||||
config: IFormattedInstanceConfiguration;
|
config: IFormattedInstanceConfiguration;
|
||||||
@ -22,6 +22,8 @@ export interface GithubConfigFormValues {
|
|||||||
|
|
||||||
export const InstanceGithubConfigForm: FC<IInstanceGithubConfigForm> = (props) => {
|
export const InstanceGithubConfigForm: FC<IInstanceGithubConfigForm> = (props) => {
|
||||||
const { config } = props;
|
const { config } = props;
|
||||||
|
// states
|
||||||
|
const [showPassword, setShowPassword] = useState(false);
|
||||||
// store
|
// store
|
||||||
const { instance: instanceStore } = useMobxStore();
|
const { instance: instanceStore } = useMobxStore();
|
||||||
// toast
|
// toast
|
||||||
@ -90,24 +92,42 @@ export const InstanceGithubConfigForm: FC<IInstanceGithubConfigForm> = (props) =
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-1">
|
<div className="flex flex-col gap-1">
|
||||||
<h4 className="text-sm">Client Secret</h4>
|
<h4 className="text-sm">Client secret</h4>
|
||||||
<Controller
|
<div className="relative">
|
||||||
control={control}
|
<Controller
|
||||||
name="GITHUB_CLIENT_SECRET"
|
control={control}
|
||||||
render={({ field: { value, onChange, ref } }) => (
|
name="GITHUB_CLIENT_SECRET"
|
||||||
<Input
|
render={({ field: { value, onChange, ref } }) => (
|
||||||
id="GITHUB_CLIENT_SECRET"
|
<Input
|
||||||
name="GITHUB_CLIENT_SECRET"
|
id="GITHUB_CLIENT_SECRET"
|
||||||
type="text"
|
name="GITHUB_CLIENT_SECRET"
|
||||||
value={value}
|
type={showPassword ? "text" : "password"}
|
||||||
onChange={onChange}
|
value={value}
|
||||||
ref={ref}
|
onChange={onChange}
|
||||||
hasError={Boolean(errors.GITHUB_CLIENT_SECRET)}
|
ref={ref}
|
||||||
placeholder="9b0050f94ec1b744e32ce79ea4ffacd40d4119cb"
|
hasError={Boolean(errors.GITHUB_CLIENT_SECRET)}
|
||||||
className="rounded-md font-medium w-full"
|
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">
|
<p className="text-xs text-custom-text-400">
|
||||||
Your client secret is also found in your{" "}
|
Your client secret is also found in your{" "}
|
||||||
<a
|
<a
|
||||||
@ -153,7 +173,7 @@ export const InstanceGithubConfigForm: FC<IInstanceGithubConfigForm> = (props) =
|
|||||||
<div className="flex flex-col gap-1">
|
<div className="flex flex-col gap-1">
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<Button variant="primary" onClick={handleSubmit(onSubmit)} loading={isSubmitting}>
|
<Button variant="primary" onClick={handleSubmit(onSubmit)} loading={isSubmitting}>
|
||||||
{isSubmitting ? "Saving..." : "Save Changes"}
|
{isSubmitting ? "Saving..." : "Save changes"}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -90,7 +90,7 @@ export const InstanceGoogleConfigForm: FC<IInstanceGoogleConfigForm> = (props) =
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-1">
|
<div className="flex flex-col gap-1">
|
||||||
<h4 className="text-sm">Origin URL</h4>
|
<h4 className="text-sm">JavaScript origin URL</h4>
|
||||||
<Button
|
<Button
|
||||||
variant="neutral-primary"
|
variant="neutral-primary"
|
||||||
className="py-2 flex justify-between items-center"
|
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 flex-col gap-1">
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<Button variant="primary" onClick={handleSubmit(onSubmit)} loading={isSubmitting}>
|
<Button variant="primary" onClick={handleSubmit(onSubmit)} loading={isSubmitting}>
|
||||||
{isSubmitting ? "Saving..." : "Save Changes"}
|
{isSubmitting ? "Saving..." : "Save changes"}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { FC } from "react";
|
import { FC, useState } from "react";
|
||||||
import { Controller, useForm } from "react-hook-form";
|
import { Controller, useForm } from "react-hook-form";
|
||||||
// ui
|
// ui
|
||||||
import { Button, Input } from "@plane/ui";
|
import { Button, Input } from "@plane/ui";
|
||||||
@ -8,6 +8,8 @@ import { IFormattedInstanceConfiguration } from "types/instance";
|
|||||||
import useToast from "hooks/use-toast";
|
import useToast from "hooks/use-toast";
|
||||||
// mobx store
|
// mobx store
|
||||||
import { useMobxStore } from "lib/mobx/store-provider";
|
import { useMobxStore } from "lib/mobx/store-provider";
|
||||||
|
// icons
|
||||||
|
import { Eye, EyeOff } from "lucide-react";
|
||||||
|
|
||||||
export interface IInstanceImageConfigForm {
|
export interface IInstanceImageConfigForm {
|
||||||
config: IFormattedInstanceConfiguration;
|
config: IFormattedInstanceConfiguration;
|
||||||
@ -19,6 +21,8 @@ export interface ImageConfigFormValues {
|
|||||||
|
|
||||||
export const InstanceImageConfigForm: FC<IInstanceImageConfigForm> = (props) => {
|
export const InstanceImageConfigForm: FC<IInstanceImageConfigForm> = (props) => {
|
||||||
const { config } = props;
|
const { config } = props;
|
||||||
|
// states
|
||||||
|
const [showPassword, setShowPassword] = useState(false);
|
||||||
// store
|
// store
|
||||||
const { instance: instanceStore } = useMobxStore();
|
const { instance: instanceStore } = useMobxStore();
|
||||||
// toast
|
// 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="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">
|
<div className="flex flex-col gap-1 max-w-md">
|
||||||
<h4 className="text-sm">Access key from your Unsplash account</h4>
|
<h4 className="text-sm">Access key from your Unsplash account</h4>
|
||||||
<Controller
|
<div className="relative">
|
||||||
control={control}
|
<Controller
|
||||||
name="UNSPLASH_ACCESS_KEY"
|
control={control}
|
||||||
render={({ field: { value, onChange, ref } }) => (
|
name="UNSPLASH_ACCESS_KEY"
|
||||||
<Input
|
render={({ field: { value, onChange, ref } }) => (
|
||||||
id="UNSPLASH_ACCESS_KEY"
|
<Input
|
||||||
name="UNSPLASH_ACCESS_KEY"
|
id="UNSPLASH_ACCESS_KEY"
|
||||||
type="text"
|
name="UNSPLASH_ACCESS_KEY"
|
||||||
value={value}
|
type={showPassword ? "text" : "password"}
|
||||||
onChange={onChange}
|
value={value}
|
||||||
ref={ref}
|
onChange={onChange}
|
||||||
hasError={Boolean(errors.UNSPLASH_ACCESS_KEY)}
|
ref={ref}
|
||||||
placeholder="oXgq-sdfadsaeweqasdfasdf3234234rassd"
|
hasError={Boolean(errors.UNSPLASH_ACCESS_KEY)}
|
||||||
className="rounded-md font-medium w-full"
|
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">
|
<p className="text-xs text-custom-text-400">
|
||||||
You will find your access key in your Unsplash developer console.{" "}
|
You will find your access key in your Unsplash developer console.{" "}
|
||||||
<a
|
<a
|
||||||
@ -87,7 +108,7 @@ export const InstanceImageConfigForm: FC<IInstanceImageConfigForm> = (props) =>
|
|||||||
|
|
||||||
<div className="flex items-center py-1">
|
<div className="flex items-center py-1">
|
||||||
<Button variant="primary" onClick={handleSubmit(onSubmit)} loading={isSubmitting}>
|
<Button variant="primary" onClick={handleSubmit(onSubmit)} loading={isSubmitting}>
|
||||||
{isSubmitting ? "Saving..." : "Save Changes"}
|
{isSubmitting ? "Saving..." : "Save changes"}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
@ -2,3 +2,9 @@ export * from "./help-section";
|
|||||||
export * from "./sidebar-menu";
|
export * from "./sidebar-menu";
|
||||||
export * from "./sidebar-dropdown";
|
export * from "./sidebar-dropdown";
|
||||||
export * from "./general-form";
|
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
|
// components
|
||||||
import { Menu, Transition } from "@headlessui/react";
|
import { Menu, Transition } from "@headlessui/react";
|
||||||
// icons
|
// icons
|
||||||
import { Cog, LogIn, LogOut, Settings } from "lucide-react";
|
import { LogIn, LogOut, Settings, UserCog2 } from "lucide-react";
|
||||||
// mobx store
|
// mobx store
|
||||||
import { useMobxStore } from "lib/mobx/store-provider";
|
import { useMobxStore } from "lib/mobx/store-provider";
|
||||||
// hooks
|
// 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="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="w-full h-full truncate">
|
||||||
<div
|
<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" : ""
|
sidebarCollapsed ? "justify-center" : ""
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<div className="flex-shrink-0 flex items-center justify-center h-7 w-7 rounded bg-custom-sidebar-background-80">
|
<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" />
|
<UserCog2 className="h-5 w-5 text-custom-text-200" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{!sidebarCollapsed && (
|
{!sidebarCollapsed && (
|
||||||
|
@ -16,9 +16,9 @@ const INSTANCE_ADMIN_LINKS = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
Icon: Mail,
|
Icon: Mail,
|
||||||
name: "Mail",
|
name: "Email",
|
||||||
description: "Set up emails to your users",
|
description: "Set up emails to your users",
|
||||||
href: `/god-mode/mail`,
|
href: `/god-mode/email`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Icon: Lock,
|
Icon: Lock,
|
||||||
@ -28,7 +28,7 @@ const INSTANCE_ADMIN_LINKS = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
Icon: BrainCog,
|
Icon: BrainCog,
|
||||||
name: "OpenAI",
|
name: "Artificial intelligence",
|
||||||
description: "Configure your OpenAI creds",
|
description: "Configure your OpenAI creds",
|
||||||
href: `/god-mode/ai`,
|
href: `/god-mode/ai`,
|
||||||
},
|
},
|
||||||
|
@ -1,15 +1,9 @@
|
|||||||
import { FC, ReactNode } from "react";
|
import { FC, ReactNode } from "react";
|
||||||
import Link from "next/link";
|
|
||||||
import Image from "next/image";
|
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
// icons
|
|
||||||
import { LayoutGrid } from "lucide-react";
|
|
||||||
// ui
|
|
||||||
import { Button } from "@plane/ui";
|
|
||||||
// hooks
|
// hooks
|
||||||
import { useMobxStore } from "lib/mobx/store-provider";
|
import { useMobxStore } from "lib/mobx/store-provider";
|
||||||
// images
|
// components
|
||||||
import AccessDeniedImg from "public/auth/access-denied.svg";
|
import { InstanceAdminRestriction } from "components/instance";
|
||||||
|
|
||||||
export interface IAdminAuthWrapper {
|
export interface IAdminAuthWrapper {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
@ -32,71 +26,7 @@ export const AdminAuthWrapper: FC<IAdminAuthWrapper> = observer(({ children }) =
|
|||||||
|
|
||||||
// if user does not have admin access to the instance
|
// if user does not have admin access to the instance
|
||||||
if (isUserInstanceAdmin !== undefined && isUserInstanceAdmin === false) {
|
if (isUserInstanceAdmin !== undefined && isUserInstanceAdmin === false) {
|
||||||
return (
|
return <InstanceAdminRestriction redirectWorkspaceSlug={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>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return <>{children}</>;
|
return <>{children}</>;
|
||||||
|
@ -119,7 +119,7 @@ export const ProfileLayoutSidebar = observer(() => {
|
|||||||
leaveFrom="transform opacity-100 scale-100"
|
leaveFrom="transform opacity-100 scale-100"
|
||||||
leaveTo="transform opacity-0 scale-95"
|
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>
|
<span className="px-2 text-custom-sidebar-text-200">{currentUser?.email}</span>
|
||||||
<Menu.Item
|
<Menu.Item
|
||||||
as="button"
|
as="button"
|
||||||
|
@ -12,7 +12,7 @@ import { Loader } from "@plane/ui";
|
|||||||
// icons
|
// icons
|
||||||
import { Lightbulb } from "lucide-react";
|
import { Lightbulb } from "lucide-react";
|
||||||
// components
|
// components
|
||||||
import { InstanceAIForm } from "components/instance/ai-form";
|
import { InstanceAIForm } from "components/instance";
|
||||||
|
|
||||||
const InstanceAdminAIPage: NextPageWithLayout = observer(() => {
|
const InstanceAdminAIPage: NextPageWithLayout = observer(() => {
|
||||||
// store
|
// store
|
||||||
@ -46,9 +46,11 @@ const InstanceAdminAIPage: NextPageWithLayout = observer(() => {
|
|||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<Loader className="space-y-4">
|
<Loader className="space-y-4">
|
||||||
<Loader.Item height="50px" width="50%" />
|
<div className="grid grid-cols-2 gap-y-4 gap-x-8">
|
||||||
<Loader.Item height="50px" width="50%" />
|
<Loader.Item height="50px" />
|
||||||
<Loader.Item height="50px" width="25%" />
|
<Loader.Item height="50px" />
|
||||||
|
</div>
|
||||||
|
<Loader.Item height="50px" />
|
||||||
</Loader>
|
</Loader>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -13,8 +13,7 @@ import useToast from "hooks/use-toast";
|
|||||||
// ui
|
// ui
|
||||||
import { Loader, ToggleSwitch } from "@plane/ui";
|
import { Loader, ToggleSwitch } from "@plane/ui";
|
||||||
// components
|
// components
|
||||||
import { InstanceGoogleConfigForm } from "components/instance/google-config-form";
|
import { InstanceGithubConfigForm, InstanceGoogleConfigForm } from "components/instance";
|
||||||
import { InstanceGithubConfigForm } from "components/instance/github-config-form";
|
|
||||||
|
|
||||||
const InstanceAdminAuthorizationPage: NextPageWithLayout = observer(() => {
|
const InstanceAdminAuthorizationPage: NextPageWithLayout = observer(() => {
|
||||||
// store
|
// store
|
||||||
@ -168,13 +167,11 @@ const InstanceAdminAuthorizationPage: NextPageWithLayout = observer(() => {
|
|||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<Loader className="space-y-4">
|
<Loader className="space-y-4">
|
||||||
<Loader.Item height="50px" width="50%" />
|
<div className="grid grid-cols-2 gap-y-4 gap-x-8">
|
||||||
<Loader.Item height="50px" width="50%" />
|
<Loader.Item height="50px" />
|
||||||
<Loader.Item height="50px" width="50%" />
|
<Loader.Item height="50px" />
|
||||||
|
</div>
|
||||||
<Loader.Item height="50px" />
|
<Loader.Item height="50px" />
|
||||||
<Loader.Item height="50px" width="25%" />
|
|
||||||
<Loader.Item height="50px" />
|
|
||||||
<Loader.Item height="50px" width="25%" />
|
|
||||||
</Loader>
|
</Loader>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -10,9 +10,9 @@ import { useMobxStore } from "lib/mobx/store-provider";
|
|||||||
// ui
|
// ui
|
||||||
import { Loader } from "@plane/ui";
|
import { Loader } from "@plane/ui";
|
||||||
// components
|
// components
|
||||||
import { InstanceEmailForm } from "components/instance/email-form";
|
import { InstanceEmailForm } from "components/instance";
|
||||||
|
|
||||||
const InstanceAdminMailPage: NextPageWithLayout = observer(() => {
|
const InstanceAdminEmailPage: NextPageWithLayout = observer(() => {
|
||||||
// store
|
// store
|
||||||
const {
|
const {
|
||||||
instance: { fetchInstanceConfigurations, formattedConfig },
|
instance: { fetchInstanceConfigurations, formattedConfig },
|
||||||
@ -36,19 +36,19 @@ const InstanceAdminMailPage: NextPageWithLayout = observer(() => {
|
|||||||
<InstanceEmailForm config={formattedConfig} />
|
<InstanceEmailForm config={formattedConfig} />
|
||||||
) : (
|
) : (
|
||||||
<Loader className="space-y-4">
|
<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" />
|
|
||||||
<Loader.Item height="50px" width="25%" />
|
|
||||||
<Loader.Item height="50px" width="25%" />
|
|
||||||
<Loader.Item height="50px" width="25%" />
|
|
||||||
</Loader>
|
</Loader>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
InstanceAdminMailPage.getLayout = function getLayout(page: ReactElement) {
|
InstanceAdminEmailPage.getLayout = function getLayout(page: ReactElement) {
|
||||||
return <InstanceAdminLayout>{page}</InstanceAdminLayout>;
|
return <InstanceAdminLayout>{page}</InstanceAdminLayout>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default InstanceAdminMailPage;
|
export default InstanceAdminEmailPage;
|
@ -10,7 +10,7 @@ import { useMobxStore } from "lib/mobx/store-provider";
|
|||||||
// ui
|
// ui
|
||||||
import { Loader } from "@plane/ui";
|
import { Loader } from "@plane/ui";
|
||||||
// components
|
// components
|
||||||
import { InstanceImageConfigForm } from "components/instance/image-config-form";
|
import { InstanceImageConfigForm } from "components/instance";
|
||||||
|
|
||||||
const InstanceAdminImagePage: NextPageWithLayout = observer(() => {
|
const InstanceAdminImagePage: NextPageWithLayout = observer(() => {
|
||||||
// store
|
// store
|
||||||
@ -32,8 +32,11 @@ const InstanceAdminImagePage: NextPageWithLayout = observer(() => {
|
|||||||
<InstanceImageConfigForm config={formattedConfig} />
|
<InstanceImageConfigForm config={formattedConfig} />
|
||||||
) : (
|
) : (
|
||||||
<Loader className="space-y-4">
|
<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>
|
</Loader>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -34,9 +34,11 @@ const InstanceAdminPage: NextPageWithLayout = observer(() => {
|
|||||||
<InstanceGeneralForm instance={instance} instanceAdmins={instanceAdmins} />
|
<InstanceGeneralForm instance={instance} instanceAdmins={instanceAdmins} />
|
||||||
) : (
|
) : (
|
||||||
<Loader className="space-y-4">
|
<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="50%" />
|
|
||||||
<Loader.Item height="50px" width="25%" />
|
|
||||||
</Loader>
|
</Loader>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
Reference in New Issue
Block a user