diff --git a/web/components/instance/openai-form.tsx b/web/components/instance/ai-form.tsx similarity index 57% rename from web/components/instance/openai-form.tsx rename to web/components/instance/ai-form.tsx index 1f1ef301c..71c5d8365 100644 --- a/web/components/instance/openai-form.tsx +++ b/web/components/instance/ai-form.tsx @@ -9,17 +9,16 @@ import useToast from "hooks/use-toast"; // mobx store import { useMobxStore } from "lib/mobx/store-provider"; -export interface IInstanceOpenAIForm { +export interface IInstanceAIForm { config: IFormattedInstanceConfiguration; } -export interface OpenAIFormValues { - OPENAI_API_BASE: string; +export interface AIFormValues { OPENAI_API_KEY: string; GPT_ENGINE: string; } -export const InstanceOpenAIForm: FC = (props) => { +export const InstanceAIForm: FC = (props) => { const { config } = props; // store const { instance: instanceStore } = useMobxStore(); @@ -30,16 +29,15 @@ export const InstanceOpenAIForm: FC = (props) => { handleSubmit, control, formState: { errors, isSubmitting }, - } = useForm({ + } = useForm({ defaultValues: { - OPENAI_API_BASE: config["OPENAI_API_BASE"], OPENAI_API_KEY: config["OPENAI_API_KEY"], GPT_ENGINE: config["GPT_ENGINE"], }, }); - const onSubmit = async (formData: OpenAIFormValues) => { - const payload: Partial = { ...formData }; + const onSubmit = async (formData: AIFormValues) => { + const payload: Partial = { ...formData }; await instanceStore .updateInstanceConfigurations(payload) @@ -47,64 +45,15 @@ export const InstanceOpenAIForm: FC = (props) => { setToastAlert({ title: "Success", type: "success", - message: "Open AI Settings updated successfully", + message: "AI Settings updated successfully", }) ) .catch((err) => console.error(err)); }; return ( -
-
-
OpenAI
-
- AI is everywhere make use it as much as you can! Learn more. -
-
-
-
-

OpenAI API Base

- ( - - )} - /> -
- -
-

OpenAI API Key

- ( - - )} - /> -
-
-
+ <> +

GPT Engine

= (props) => { onChange={onChange} ref={ref} hasError={Boolean(errors.GPT_ENGINE)} - placeholder="GPT Engine" + placeholder="gpt-3.5-turbo" className="rounded-md font-medium w-full" /> )} /> +

+ Choose an OpenAI engine.{" "} + + Learn more + +

+
+ +
+

API Key

+ ( + + )} + /> +

+ You will find your API key{" "} + + here. + +

@@ -132,6 +124,6 @@ export const InstanceOpenAIForm: FC = (props) => { {isSubmitting ? "Saving..." : "Save Changes"}
-
+ ); }; diff --git a/web/components/instance/email-form.tsx b/web/components/instance/email-form.tsx index f0729e925..870512106 100644 --- a/web/components/instance/email-form.tsx +++ b/web/components/instance/email-form.tsx @@ -31,6 +31,7 @@ export const InstanceEmailForm: FC = (props) => { // form data const { handleSubmit, + watch, control, formState: { errors, isSubmitting }, } = useForm({ @@ -60,11 +61,7 @@ export const InstanceEmailForm: FC = (props) => { }; return ( -
-
-
Email
-
Email related settings.
-
+ <>

Host

@@ -80,7 +77,7 @@ export const InstanceEmailForm: FC = (props) => { onChange={onChange} ref={ref} hasError={Boolean(errors.EMAIL_HOST)} - placeholder="Email Host" + placeholder="email.google.com" className="rounded-md font-medium w-full" /> )} @@ -101,7 +98,7 @@ export const InstanceEmailForm: FC = (props) => { onChange={onChange} ref={ref} hasError={Boolean(errors.EMAIL_PORT)} - placeholder="Email Port" + placeholder="8080" className="rounded-md font-medium w-full" /> )} @@ -123,7 +120,7 @@ export const InstanceEmailForm: FC = (props) => { onChange={onChange} ref={ref} hasError={Boolean(errors.EMAIL_HOST_USER)} - placeholder="Username" + placeholder="getitdone@projectplane.so" className="rounded-md font-medium w-full" /> )} @@ -139,7 +136,7 @@ export const InstanceEmailForm: FC = (props) => { = (props) => {
-
-
-
Enable TLS
+
+
+
+
+ Turn TLS {Boolean(parseInt(watch("EMAIL_USE_TLS"))) ? "off" : "on"} +
+
Use this if your email domain supports TLS.
+
+
+ ( + { + Boolean(parseInt(value)) === true ? onChange("0") : onChange("1"); + }} + size="sm" + /> + )} + /> +
-
- ( - { - Boolean(parseInt(value)) === true ? onChange("0") : onChange("1"); - }} - size="sm" - /> - )} - /> -
-
-
-
-
Enable SSL
-
-
- ( - { - Boolean(parseInt(value)) === true ? onChange("0") : onChange("1"); - }} - size="sm" - /> - )} - /> +
+
+
+ Turn SSL {Boolean(parseInt(watch("EMAIL_USE_SSL"))) ? "off" : "on"} +
+
+ Most email domains support SSL. Use this to secure comms between this instance and your users. +
+
+
+ ( + { + Boolean(parseInt(value)) === true ? onChange("0") : onChange("1"); + }} + size="sm" + /> + )} + /> +
@@ -199,6 +206,6 @@ export const InstanceEmailForm: FC = (props) => { {isSubmitting ? "Saving..." : "Save Changes"}
-
+ ); }; diff --git a/web/components/instance/general-form.tsx b/web/components/instance/general-form.tsx index 5703ce99c..d8b533f73 100644 --- a/web/components/instance/general-form.tsx +++ b/web/components/instance/general-form.tsx @@ -14,7 +14,7 @@ export interface IInstanceGeneralForm { export interface GeneralFormValues { instance_name: string; - is_telemetry_enabled: boolean; + // is_telemetry_enabled: boolean; } export const InstanceGeneralForm: FC = (props) => { @@ -31,7 +31,7 @@ export const InstanceGeneralForm: FC = (props) => { } = useForm({ defaultValues: { instance_name: instance.instance_name, - is_telemetry_enabled: instance.is_telemetry_enabled, + // is_telemetry_enabled: instance.is_telemetry_enabled, }, }); @@ -51,13 +51,7 @@ export const InstanceGeneralForm: FC = (props) => { }; return ( -
-
-
General
-
- The usual things like your mail, name of instance and other stuff. -
-
+ <>

Name of instance

@@ -106,7 +100,7 @@ export const InstanceGeneralForm: FC = (props) => {
-
+ {/*
Share anonymous usage instance
@@ -120,13 +114,13 @@ export const InstanceGeneralForm: FC = (props) => { render={({ field: { value, onChange } }) => } />
-
+
*/}
-
+ ); }; diff --git a/web/components/instance/github-config-form.tsx b/web/components/instance/github-config-form.tsx index ddc0f0ea5..a58cf19d7 100644 --- a/web/components/instance/github-config-form.tsx +++ b/web/components/instance/github-config-form.tsx @@ -56,8 +56,8 @@ export const InstanceGithubConfigForm: FC = (props) = const originURL = typeof window !== "undefined" ? window.location.origin : ""; return ( - <> -
+
+

Client ID

= (props) = onChange={onChange} ref={ref} hasError={Boolean(errors.GITHUB_CLIENT_ID)} - placeholder="Github Client ID" + placeholder="70a44354520df8bd9bcd" className="rounded-md font-medium w-full" /> )} /> +

+ You will get this from your{" "} + + GitHub OAuth application settings. + +

Client Secret

@@ -92,14 +103,23 @@ export const InstanceGithubConfigForm: FC = (props) = onChange={onChange} ref={ref} hasError={Boolean(errors.GITHUB_CLIENT_SECRET)} - placeholder="Github Client Secret" + placeholder="9b0050f94ec1b744e32ce79ea4ffacd40d4119cb" className="rounded-md font-medium w-full" /> )} /> +

+ Your client secret is also found in your{" "} + + GitHub OAuth application settings. + +

-
-

Origin URL

-

*paste this URL in your Github console.

-
-
-
- -
+

+ We will auto-generate this. Paste this into the Authorization callback URL field{" "} + + here. + +

- +
+
+ +
+
+
); }; diff --git a/web/components/instance/google-config-form.tsx b/web/components/instance/google-config-form.tsx index a8c8f63ea..5637b76fc 100644 --- a/web/components/instance/google-config-form.tsx +++ b/web/components/instance/google-config-form.tsx @@ -56,8 +56,8 @@ export const InstanceGoogleConfigForm: FC = (props) = const originURL = typeof window !== "undefined" ? window.location.origin : ""; return ( - <> -
+
+

Client ID

= (props) = onChange={onChange} ref={ref} hasError={Boolean(errors.GOOGLE_CLIENT_ID)} - placeholder="Google Client ID" + placeholder="840195096245-0p2tstej9j5nc4l8o1ah2dqondscqc1g.apps.googleusercontent.com" className="rounded-md font-medium w-full" /> )} /> +

+ Your client ID lives in your Google API Console.{" "} + + Learn more + +

Client Secret

@@ -92,14 +103,23 @@ export const InstanceGoogleConfigForm: FC = (props) = onChange={onChange} ref={ref} hasError={Boolean(errors.GOOGLE_CLIENT_SECRET)} - placeholder="Google Client Secret" + placeholder="GOCShX-ADp4cI0kPqav1gGCBg5bE02E" className="rounded-md font-medium w-full" /> )} /> +

+ Your client secret should also be in your Google API Console.{" "} + + Learn more + +

-
-

Origin URL

-

*paste this URL in your Google developer console.

-
-
-
- -
+

+ We will auto-generate this. Paste this into your Authorized JavaScript origins field. For this OAuth client{" "} + + here. + +

- +
+
+ +
+
+
); }; diff --git a/web/components/instance/image-config-form.tsx b/web/components/instance/image-config-form.tsx new file mode 100644 index 000000000..b8a111cf9 --- /dev/null +++ b/web/components/instance/image-config-form.tsx @@ -0,0 +1,95 @@ +import { FC } from "react"; +import { Controller, useForm } from "react-hook-form"; +// ui +import { Button, Input } from "@plane/ui"; +// types +import { IFormattedInstanceConfiguration } from "types/instance"; +// hooks +import useToast from "hooks/use-toast"; +// mobx store +import { useMobxStore } from "lib/mobx/store-provider"; + +export interface IInstanceImageConfigForm { + config: IFormattedInstanceConfiguration; +} + +export interface ImageConfigFormValues { + UNSPLASH_ACCESS_KEY: string; +} + +export const InstanceImageConfigForm: FC = (props) => { + const { config } = props; + // store + const { instance: instanceStore } = useMobxStore(); + // toast + const { setToastAlert } = useToast(); + // form data + const { + handleSubmit, + control, + formState: { errors, isSubmitting }, + } = useForm({ + defaultValues: { + UNSPLASH_ACCESS_KEY: config["UNSPLASH_ACCESS_KEY"], + }, + }); + + const onSubmit = async (formData: ImageConfigFormValues) => { + const payload: Partial = { ...formData }; + + await instanceStore + .updateInstanceConfigurations(payload) + .then(() => + setToastAlert({ + title: "Success", + type: "success", + message: "Image Configuration Settings updated successfully", + }) + ) + .catch((err) => console.error(err)); + }; + + return ( + <> +
+
+

Access key from your Unsplash account

+ ( + + )} + /> +

+ You will find your access key in your Unsplash developer console.{" "} + + Learn more. + +

+
+
+ +
+ +
+ + ); +}; diff --git a/web/components/instance/sidebar-dropdown.tsx b/web/components/instance/sidebar-dropdown.tsx index 2da53c21b..fb89550ed 100644 --- a/web/components/instance/sidebar-dropdown.tsx +++ b/web/components/instance/sidebar-dropdown.tsx @@ -3,9 +3,11 @@ import { useRouter } from "next/router"; import { useTheme } from "next-themes"; import { observer } from "mobx-react-lite"; import Link from "next/link"; -import { Menu, Transition } from "@headlessui/react"; -import { Cog, LogIn, LogOut, Settings } from "lucide-react"; import { mutate } from "swr"; +// components +import { Menu, Transition } from "@headlessui/react"; +// icons +import { LogIn, LogOut, Settings, UserCog2 } from "lucide-react"; // mobx store import { useMobxStore } from "lib/mobx/store-provider"; // hooks @@ -64,28 +66,21 @@ export const InstanceSidebarDropdown = observer(() => { }; return ( -
+
-
- +
+
- {!sidebarCollapsed &&

Instance Admin

} -
-
- - {!sidebarCollapsed && ( - - - {!sidebarCollapsed && ( - + {!sidebarCollapsed && ( + + )} +
+
+ + {!sidebarCollapsed && ( + + { leaveTo="transform opacity-0 scale-95" >
{currentUser?.email} @@ -145,8 +147,8 @@ export const InstanceSidebarDropdown = observer(() => {
- - Normal Mode + + Exit God Mode diff --git a/web/components/instance/sidebar-menu.tsx b/web/components/instance/sidebar-menu.tsx index 76819403c..07389a1c5 100644 --- a/web/components/instance/sidebar-menu.tsx +++ b/web/components/instance/sidebar-menu.tsx @@ -1,7 +1,7 @@ import Link from "next/link"; import { useRouter } from "next/router"; // icons -import { BrainCog, Cog, Lock, Mail } from "lucide-react"; +import { Image, BrainCog, Cog, Lock, Mail } from "lucide-react"; // mobx store import { useMobxStore } from "lib/mobx/store-provider"; // ui @@ -11,26 +11,32 @@ const INSTANCE_ADMIN_LINKS = [ { Icon: Cog, name: "General", - description: "General settings here", + description: "Identify your instances and get key details", href: `/god-mode`, }, { Icon: Mail, name: "Email", - description: "Email related settings will go here", + description: "Set up emails to your users", href: `/god-mode/email`, }, { Icon: Lock, - name: "Authorization", - description: "Autorization", + name: "SSO and OAuth", + description: "Configure your Google and GitHub SSOs", href: `/god-mode/authorization`, }, { Icon: BrainCog, - name: "OpenAI", - description: "OpenAI configurations", - href: `/god-mode/openai`, + name: "Artificial intelligence", + description: "Configure your OpenAI creds", + href: `/god-mode/ai`, + }, + { + Icon: Image, + name: "Images in Plane", + description: "Allow third-party image libraries", + href: `/god-mode/image`, }, ]; @@ -68,7 +74,9 @@ export const InstanceAdminSidebarMenu = () => { {item.name} {item.description} diff --git a/web/components/workspace/sidebar-dropdown.tsx b/web/components/workspace/sidebar-dropdown.tsx index 90b06de2c..c94589401 100644 --- a/web/components/workspace/sidebar-dropdown.tsx +++ b/web/components/workspace/sidebar-dropdown.tsx @@ -303,8 +303,8 @@ export const WorkspaceSidebarDropdown = observer(() => {
- - God Mode + + Enter God Mode diff --git a/web/layouts/admin-layout/header.tsx b/web/layouts/admin-layout/header.tsx index 90028f6ae..1a0b8241a 100644 --- a/web/layouts/admin-layout/header.tsx +++ b/web/layouts/admin-layout/header.tsx @@ -7,26 +7,28 @@ import { Breadcrumbs } from "@plane/ui"; import { Settings } from "lucide-react"; export interface IInstanceAdminHeader { - title: string; + title?: string; } export const InstanceAdminHeader: FC = observer((props) => { const { title } = props; return ( -
+
-
- - } - label="Settings" - link="/god-mode" - /> - - -
+ {title && ( +
+ + } + label="Settings" + link="/god-mode" + /> + + +
+ )}
); diff --git a/web/layouts/admin-layout/layout.tsx b/web/layouts/admin-layout/layout.tsx index 9d908a91a..4943bdabc 100644 --- a/web/layouts/admin-layout/layout.tsx +++ b/web/layouts/admin-layout/layout.tsx @@ -3,14 +3,14 @@ import { FC, ReactNode } from "react"; import { AdminAuthWrapper, UserAuthWrapper } from "layouts/auth-layout"; // components import { InstanceAdminSidebar } from "./sidebar"; +import { InstanceAdminHeader } from "./header"; export interface IInstanceAdminLayout { children: ReactNode; - header: ReactNode; } export const InstanceAdminLayout: FC = (props) => { - const { children, header } = props; + const { children } = props; return ( <> @@ -19,7 +19,7 @@ export const InstanceAdminLayout: FC = (props) => {
- {header} +
<>{children} diff --git a/web/layouts/auth-layout/admin-wrapper.tsx b/web/layouts/auth-layout/admin-wrapper.tsx index 37eb06a43..0311afb93 100644 --- a/web/layouts/auth-layout/admin-wrapper.tsx +++ b/web/layouts/auth-layout/admin-wrapper.tsx @@ -33,33 +33,68 @@ export const AdminAuthWrapper: FC = observer(({ children }) = // if user does not have admin access to the instance if (isUserInstanceAdmin !== undefined && isUserInstanceAdmin === false) { return ( -
-
-
-
-
- AccessDeniedImg -

Access denied!

-
-

Sorry, but you do not have permission to view this page.

-

- If you think there{"’"}s a mistake contact support. -

-
+
+ ); } diff --git a/web/layouts/settings-layout/profile/sidebar.tsx b/web/layouts/settings-layout/profile/sidebar.tsx index 6466f2b93..1867f70a3 100644 --- a/web/layouts/settings-layout/profile/sidebar.tsx +++ b/web/layouts/settings-layout/profile/sidebar.tsx @@ -41,7 +41,7 @@ export const ProfileLayoutSidebar = observer(() => { const { theme: { sidebarCollapsed, toggleSidebar }, workspace: { workspaces }, - user: { currentUser, currentUserSettings }, + user: { currentUser, currentUserSettings, isUserInstanceAdmin }, } = useMobxStore(); // redirect url for normal mode @@ -135,6 +135,17 @@ export const ProfileLayoutSidebar = observer(() => { Sign out + {isUserInstanceAdmin && ( + + )}
diff --git a/web/pages/god-mode/ai.tsx b/web/pages/god-mode/ai.tsx new file mode 100644 index 000000000..0cd0df47f --- /dev/null +++ b/web/pages/god-mode/ai.tsx @@ -0,0 +1,62 @@ +import { ReactElement } from "react"; +import useSWR from "swr"; +import { observer } from "mobx-react-lite"; +// layouts +import { InstanceAdminLayout } from "layouts/admin-layout"; +// types +import { NextPageWithLayout } from "types/app"; +// store +import { useMobxStore } from "lib/mobx/store-provider"; +// ui +import { Loader } from "@plane/ui"; +// icons +import { Lightbulb } from "lucide-react"; +// components +import { InstanceAIForm } from "components/instance/ai-form"; + +const InstanceAdminAIPage: NextPageWithLayout = observer(() => { + // store + const { + instance: { fetchInstanceConfigurations, formattedConfig }, + } = useMobxStore(); + + useSWR("INSTANCE_CONFIGURATIONS", () => fetchInstanceConfigurations()); + + return ( +
+
+
AI features for all your workspaces
+
+ Configure your AI API credentials so Plane AI features are turned on for all your workspaces. +
+
+ {formattedConfig ? ( + <> +
+
OpenAI
+
If you use ChatGPT, this is for you.
+
+ +
+
+ +
If you have a preferred AI models vendor, please get in touch with us.
+
+
+ + ) : ( + + + + + + )} +
+ ); +}); + +InstanceAdminAIPage.getLayout = function getLayout(page: ReactElement) { + return {page}; +}; + +export default InstanceAdminAIPage; diff --git a/web/pages/god-mode/authorization.tsx b/web/pages/god-mode/authorization.tsx index 7f5fadbe8..1e3f4f4f6 100644 --- a/web/pages/god-mode/authorization.tsx +++ b/web/pages/god-mode/authorization.tsx @@ -1,19 +1,17 @@ import { ReactElement, useState } from "react"; +import Link from "next/link"; import useSWR from "swr"; import { observer } from "mobx-react-lite"; // layouts -import { InstanceAdminHeader, InstanceAdminLayout } from "layouts/admin-layout"; +import { InstanceAdminLayout } from "layouts/admin-layout"; // types import { NextPageWithLayout } from "types/app"; // store import { useMobxStore } from "lib/mobx/store-provider"; // hooks import useToast from "hooks/use-toast"; -// icons -import { ChevronDown, ChevronRight } from "lucide-react"; // ui import { Loader, ToggleSwitch } from "@plane/ui"; -import { Disclosure, Transition } from "@headlessui/react"; // components import { InstanceGoogleConfigForm } from "components/instance/google-config-form"; import { InstanceGithubConfigForm } from "components/instance/github-config-form"; @@ -33,12 +31,17 @@ const InstanceAdminAuthorizationPage: NextPageWithLayout = observer(() => { const [isSubmitting, setIsSubmitting] = useState(false); const enableSignup = formattedConfig?.ENABLE_SIGNUP ?? "0"; + const enableMagicLogin = formattedConfig?.ENABLE_MAGIC_LINK_LOGIN ?? "0"; + // const enableEmailPassword = formattedConfig?.ENABLE_EMAIL_PASSWORD ?? "0"; - const updateConfig = async (value: string) => { + const updateConfig = async ( + key: "ENABLE_SIGNUP" | "ENABLE_MAGIC_LINK_LOGIN" | "ENABLE_EMAIL_PASSWORD", + value: string + ) => { setIsSubmitting(true); const payload = { - ENABLE_SIGNUP: value, + [key]: value, }; await updateInstanceConfigurations(payload) @@ -46,7 +49,7 @@ const InstanceAdminAuthorizationPage: NextPageWithLayout = observer(() => { setToastAlert({ title: "Success", type: "success", - message: "Authorization Settings updated successfully", + message: "SSO and OAuth Settings updated successfully", }); setIsSubmitting(false); }) @@ -55,102 +58,121 @@ const InstanceAdminAuthorizationPage: NextPageWithLayout = observer(() => { setToastAlert({ title: "Error", type: "error", - message: "Failed to update Authorization Settings", + message: "Failed to update SSO and OAuth Settings", }); setIsSubmitting(false); }); }; return ( -
+
+
+
Single sign-on and OAuth
+
+ Make your teams life easy by letting them sign-up with their Google and GitHub accounts, and below are the + settings. +
+
{formattedConfig ? ( -
-
-
Authorization
-
- Make your teams life easy by letting them sign-up with their Google and GitHub accounts, and below are the - settings. -
-
-
-
-
Enable sign-up
-
- Keep the doors open so people can join your workspaces. + <> +
+
+
+
+ Turn Magic Links {Boolean(parseInt(enableMagicLogin)) ? "off" : "on"} +
+
+

Slack-like emails for authentication.

+ You need to have set up email{" "} + + here + {" "} + to enable this. +
+
+
+ { + Boolean(parseInt(enableMagicLogin)) === true + ? updateConfig("ENABLE_MAGIC_LINK_LOGIN", "0") + : updateConfig("ENABLE_MAGIC_LINK_LOGIN", "1"); + }} + size="sm" + disabled={isSubmitting} + />
-
- { - Boolean(parseInt(enableSignup)) === true ? updateConfig("0") : updateConfig("1"); - }} - size="sm" - disabled={isSubmitting} - /> +
+
+
+ Let your users log in via the methods below +
+
+ Toggling this off will disable all previous configs. Users will only be able to login with an e-mail + and password combo. +
+
+
+ { + Boolean(parseInt(enableSignup)) === true + ? updateConfig("ENABLE_SIGNUP", "0") + : updateConfig("ENABLE_SIGNUP", "1"); + }} + size="sm" + disabled={isSubmitting} + /> +
+ {/*
+
+
+ Turn Email Password {Boolean(parseInt(enableEmailPassword)) ? "off" : "on"} +
+
UX Copy Required!
+
+
+ { + Boolean(parseInt(enableEmailPassword)) === true + ? updateConfig("ENABLE_EMAIL_PASSWORD", "0") + : updateConfig("ENABLE_EMAIL_PASSWORD", "1"); + }} + size="sm" + disabled={isSubmitting} + /> +
+
*/}
- - {({ open }) => ( -
- - Google - {open ? : } - - - - - - -
- )} -
- - {({ open }) => ( -
- - Github - {open ? : } - - - - - - -
- )} -
+
+
+ Google +
+
+ +
+
+
+
+ Github +
+
+ +
+
-
+ ) : ( - - + + + + + @@ -160,7 +182,7 @@ const InstanceAdminAuthorizationPage: NextPageWithLayout = observer(() => { }); InstanceAdminAuthorizationPage.getLayout = function getLayout(page: ReactElement) { - return }>{page}; + return {page}; }; export default InstanceAdminAuthorizationPage; diff --git a/web/pages/god-mode/email.tsx b/web/pages/god-mode/email.tsx index 03eab3318..989cffe66 100644 --- a/web/pages/god-mode/email.tsx +++ b/web/pages/god-mode/email.tsx @@ -2,7 +2,7 @@ import { ReactElement } from "react"; import useSWR from "swr"; import { observer } from "mobx-react-lite"; // layouts -import { InstanceAdminHeader, InstanceAdminLayout } from "layouts/admin-layout"; +import { InstanceAdminLayout } from "layouts/admin-layout"; // types import { NextPageWithLayout } from "types/app"; // store @@ -21,11 +21,21 @@ const InstanceAdminEmailPage: NextPageWithLayout = observer(() => { useSWR("INSTANCE_CONFIGURATIONS", () => fetchInstanceConfigurations()); return ( -
+
+
+
Secure emails from your own instance
+
+ Plane can send useful emails to you and your users from your own instance without talking to the Internet. +
+
+ Set it up below and please test your settings before you save them.{" "} + Misconfigs can lead to email bounces and errors. +
+
{formattedConfig ? ( ) : ( - + @@ -38,7 +48,7 @@ const InstanceAdminEmailPage: NextPageWithLayout = observer(() => { }); InstanceAdminEmailPage.getLayout = function getLayout(page: ReactElement) { - return }>{page}; + return {page}; }; export default InstanceAdminEmailPage; diff --git a/web/pages/god-mode/image.tsx b/web/pages/god-mode/image.tsx new file mode 100644 index 000000000..57223e81d --- /dev/null +++ b/web/pages/god-mode/image.tsx @@ -0,0 +1,47 @@ +import { ReactElement } from "react"; +import useSWR from "swr"; +import { observer } from "mobx-react-lite"; +// layouts +import { InstanceAdminLayout } from "layouts/admin-layout"; +// types +import { NextPageWithLayout } from "types/app"; +// store +import { useMobxStore } from "lib/mobx/store-provider"; +// ui +import { Loader } from "@plane/ui"; +// components +import { InstanceImageConfigForm } from "components/instance/image-config-form"; + +const InstanceAdminImagePage: NextPageWithLayout = observer(() => { + // store + const { + instance: { fetchInstanceConfigurations, formattedConfig }, + } = useMobxStore(); + + useSWR("INSTANCE_CONFIGURATIONS", () => fetchInstanceConfigurations()); + + return ( +
+
+
Third-party image libraries
+
+ Let your users search and choose images from third-party libraries +
+
+ {formattedConfig ? ( + + ) : ( + + + + + )} +
+ ); +}); + +InstanceAdminImagePage.getLayout = function getLayout(page: ReactElement) { + return {page}; +}; + +export default InstanceAdminImagePage; diff --git a/web/pages/god-mode/index.tsx b/web/pages/god-mode/index.tsx index ba6113a4b..7c203d9ad 100644 --- a/web/pages/god-mode/index.tsx +++ b/web/pages/god-mode/index.tsx @@ -2,7 +2,7 @@ import { ReactElement } from "react"; import useSWR from "swr"; import { observer } from "mobx-react-lite"; // layouts -import { InstanceAdminHeader, InstanceAdminLayout } from "layouts/admin-layout"; +import { InstanceAdminLayout } from "layouts/admin-layout"; // types import { NextPageWithLayout } from "types/app"; // store @@ -21,11 +21,18 @@ const InstanceAdminPage: NextPageWithLayout = observer(() => { useSWR("INSTANCE_INFO", () => fetchInstanceInfo()); return ( -
+
+
+
ID your instance easily
+
+ Change the name of your instance and instance admin e-mail addresses. If you have a paid subscription, you + will find your license key here. +
+
{instance ? ( ) : ( - + @@ -36,7 +43,7 @@ const InstanceAdminPage: NextPageWithLayout = observer(() => { }); InstanceAdminPage.getLayout = function getLayout(page: ReactElement) { - return }>{page}; + return {page}; }; export default InstanceAdminPage; diff --git a/web/pages/god-mode/openai.tsx b/web/pages/god-mode/openai.tsx deleted file mode 100644 index 214a14c46..000000000 --- a/web/pages/god-mode/openai.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import { ReactElement } from "react"; -import useSWR from "swr"; -import { observer } from "mobx-react-lite"; -// layouts -import { InstanceAdminHeader, InstanceAdminLayout } from "layouts/admin-layout"; -// types -import { NextPageWithLayout } from "types/app"; -// store -import { useMobxStore } from "lib/mobx/store-provider"; -// ui -import { Loader } from "@plane/ui"; -// components -import { InstanceOpenAIForm } from "components/instance/openai-form"; - -const InstanceAdminOpenAIPage: NextPageWithLayout = observer(() => { - // store - const { - instance: { fetchInstanceConfigurations, formattedConfig }, - } = useMobxStore(); - - useSWR("INSTANCE_CONFIGURATIONS", () => fetchInstanceConfigurations()); - - return ( -
- {formattedConfig ? ( - - ) : ( - - - - - - )} -
- ); -}); - -InstanceAdminOpenAIPage.getLayout = function getLayout(page: ReactElement) { - return }>{page}; -}; - -export default InstanceAdminOpenAIPage;