diff --git a/app/client/package.json b/app/client/package.json index 7b9ddb8629..e72c761163 100644 --- a/app/client/package.json +++ b/app/client/package.json @@ -175,6 +175,7 @@ "rc-pagination": "^3.1.3", "rc-select": "^14.1.9", "rc-tree-select": "^5.4.0", + "rc-trigger": "^5.3.4", "re-reselect": "^3.4.0", "react": "^17.0.2", "react-append-to-body": "^2.0.26", @@ -186,6 +187,7 @@ "react-dnd-touch-backend": "11.1.3", "react-documents": "^1.0.4", "react-dom": "^17.0.2", + "react-draggable": "4.4.4", "react-full-screen": "^1.1.0", "react-fusioncharts": "^3.1.2", "react-google-recaptcha": "^2.1.0", @@ -203,6 +205,7 @@ "react-qr-barcode-scanner": "^1.0.6", "react-rating": "^2.0.5", "react-redux": "^7.2.4", + "react-resizable": "^3.0.5", "react-responsive": "^9.0.0-beta.3", "react-router": "^5.1.2", "react-router-dom": "^5.1.2", @@ -291,6 +294,7 @@ "@types/react-instantsearch-dom": "^6.3.0", "@types/react-modal": "^3.13.1", "@types/react-redux": "^7.0.1", + "@types/react-resizable": "^3.0.4", "@types/react-router-dom": "^5.1.2", "@types/react-select": "^3.0.5", "@types/react-syntax-highlighter": "^13.5.2", diff --git a/app/client/public/logo/dm.svg b/app/client/public/logo/dm.svg new file mode 100644 index 0000000000..7ea700eb8e --- /dev/null +++ b/app/client/public/logo/dm.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/client/public/logo/tidb.svg b/app/client/public/logo/tidb.svg new file mode 100644 index 0000000000..3493f73ad5 --- /dev/null +++ b/app/client/public/logo/tidb.svg @@ -0,0 +1,8 @@ + + + + + diff --git a/app/client/src/ce/configs/index.ts b/app/client/src/ce/configs/index.ts index 177052b234..5e9477ae7a 100644 --- a/app/client/src/ce/configs/index.ts +++ b/app/client/src/ce/configs/index.ts @@ -21,6 +21,7 @@ export interface INJECTED_CONFIGS { fusioncharts: { licenseKey: string; }; + enableWeChatOAuth: boolean; enableMixpanel: boolean; google: string; enableTNCPP: boolean; @@ -75,6 +76,9 @@ export const getConfigsFromEnvVars = (): INJECTED_CONFIGS => { fusioncharts: { licenseKey: process.env.REACT_APP_FUSIONCHARTS_LICENSE_KEY || "", }, + enableWeChatOAuth: process.env.APPSMITH_WX_CLIENT_ID + ? process.env.APPSMITH_WX_CLIENT_SECRET.length > 0 + : false, enableMixpanel: process.env.REACT_APP_SEGMENT_KEY ? process.env.REACT_APP_SEGMENT_KEY.length > 0 : false, @@ -244,6 +248,10 @@ export const getAppsmithConfigs = (): AppsmithUIConfigs => { ENV_CONFIG.enableRapidAPI || APPSMITH_FEATURE_CONFIGS?.enableRapidAPI || false, + enableWeChatOAuth: + ENV_CONFIG.enableWeChatOAuth || + APPSMITH_FEATURE_CONFIGS?.enableWeChatOAuth || + false, enableMixpanel: ENV_CONFIG.enableMixpanel || APPSMITH_FEATURE_CONFIGS?.enableMixpanel || diff --git a/app/client/src/ce/configs/types.ts b/app/client/src/ce/configs/types.ts index 42b6b54ca3..3c3b8a18d4 100644 --- a/app/client/src/ce/configs/types.ts +++ b/app/client/src/ce/configs/types.ts @@ -45,6 +45,7 @@ export interface AppsmithUIConfigs { enableRapidAPI: boolean; enableMixpanel: boolean; enableTNCPP: boolean; + enableWeChatOAuth: boolean; cloudHosting: boolean; diff --git a/app/client/src/ce/pages/AdminSettings/config/authentication/index.tsx b/app/client/src/ce/pages/AdminSettings/config/authentication/index.tsx index 1a1a61eef5..7993c665d3 100644 --- a/app/client/src/ce/pages/AdminSettings/config/authentication/index.tsx +++ b/app/client/src/ce/pages/AdminSettings/config/authentication/index.tsx @@ -3,11 +3,13 @@ import { GITHUB_SIGNUP_SETUP_DOC, GOOGLE_SIGNUP_SETUP_DOC, SIGNUP_RESTRICTION_DOC, + WX_SIGNUP_SETUP_DOC, } from "constants/ThirdPartyConstants"; import type { AdminConfigType } from "@appsmith/pages/AdminSettings/config/types"; import { CategoryType, SettingCategories, + SettingSubCategories, SettingSubtype, SettingTypes, } from "@appsmith/pages/AdminSettings/config/types"; @@ -34,7 +36,11 @@ import { import { isSAMLEnabled, isOIDCEnabled } from "@appsmith/utils/planHelpers"; import { selectFeatureFlags } from "@appsmith/selectors/featureFlagsSelectors"; import store from "store"; +import WeChat from "assets/images/WeChat.svg"; +import { getAppsmithConfigs } from "@appsmith/configs"; +import { getWXLoginClientId } from "ce/selectors/settingsSelectors"; +const { enableWeChatOAuth } = getAppsmithConfigs(); const featureFlags = selectFeatureFlags(store.getState()); const FormAuth: AdminConfigType = { @@ -192,6 +198,47 @@ const GithubAuth: AdminConfigType = { ], }; +const WeChatAuth: AdminConfigType = { + type: SettingCategories.WECHAT_AUTH, + controlType: SettingTypes.GROUP, + title: "微信登录", + subText: "让你可以微信账号进行登录", + canSave: true, + isConnected: enableWeChatOAuth, + categoryType: CategoryType.OTHER, + settings: [ + { + id: "APPSMITH_OAUTH2_OIDC_READ_MORE", + category: SettingCategories.WECHAT_AUTH, + subCategory: SettingSubCategories.WECHAT, + controlType: SettingTypes.LINK, + label: "如何配置?", + url: WX_SIGNUP_SETUP_DOC, + }, + { + id: "APPSMITH_WX_CLIENT_REDIRECT_URL", + category: SettingCategories.WECHAT_AUTH, + subCategory: SettingSubCategories.WECHAT, + controlType: SettingTypes.UNEDITABLEFIELD, + label: "Redirect URL", + fieldName: "oidc-redirect-url", + formName: "OidcRedirectUrl", + value: "/api/v1/wxLogin/callback", + helpText: "拷贝到你的认证服务器配置中", + forceHidden: true, + }, + { + id: "APPSMITH_WX_CLIENT_SECRET", + category: SettingCategories.WECHAT_AUTH, + subCategory: SettingSubCategories.WECHAT, + controlType: SettingTypes.TEXTINPUT, + controlSubType: SettingSubtype.TEXT, + label: "Client Secret", + isRequired: true, + }, + ], +}; + export const FormAuthCallout: AuthMethodType = { id: "APPSMITH_FORM_LOGIN_AUTH", category: SettingCategories.FORM_AUTH, @@ -238,17 +285,32 @@ export const OidcAuthCallout: AuthMethodType = { isFeatureEnabled: isOIDCEnabled(featureFlags), }; +const WeChatCallout: AuthMethodType = { + id: "APPSMITH_WECHAT_AUTH", + category: SettingCategories.WECHAT_AUTH, + label: "微信", + subText: `允许使用 微信账号登录你的平台`, + image: WeChat, + isConnected: enableWeChatOAuth, + needsUpgrade: false, + isFeatureEnabled: false, +}; + const AuthMethods = [ // OidcAuthCallout, // SamlAuthCallout, GoogleAuthCallout, GithubAuthCallout, FormAuthCallout, + WeChatCallout, ]; function AuthMain() { + const WXLoginClientId = useSelector(getWXLoginClientId); FormAuthCallout.isConnected = useSelector(getIsFormLoginEnabled); const socialLoginList = useSelector(getThirdPartyAuths); + WeChatAuth.isConnected = WeChatCallout.isConnected = + socialLoginList.includes("wechat") || !!WXLoginClientId; GoogleAuth.isConnected = GoogleAuthCallout.isConnected = socialLoginList.includes("google"); GithubAuth.isConnected = GithubAuthCallout.isConnected = @@ -263,6 +325,6 @@ export const config: AdminConfigType = { controlType: SettingTypes.PAGE, title: "身份认证", canSave: false, - children: [FormAuth, GoogleAuth, GithubAuth], + children: [FormAuth, GoogleAuth, GithubAuth, WeChatAuth], component: AuthMain, }; diff --git a/app/client/src/ce/pages/AdminSettings/config/types.ts b/app/client/src/ce/pages/AdminSettings/config/types.ts index c12178b0b6..79494bf2fb 100644 --- a/app/client/src/ce/pages/AdminSettings/config/types.ts +++ b/app/client/src/ce/pages/AdminSettings/config/types.ts @@ -49,6 +49,13 @@ export enum SettingSubtype { PASSWORD = "password", } +export const SettingSubCategories = { + GOOGLE: "google signup", + GITHUB: "github signup", + FORMLOGIN: "form login", + WECHAT: "微信登录", +}; + export type Setting = ControlType & { id: string; category?: string; @@ -79,6 +86,7 @@ export type Setting = ControlType & { calloutType?: CalloutKind; advanced?: Setting[]; isRequired?: boolean; + forceHidden?: boolean; formName?: string; fieldName?: string; dropdownOptions?: Partial[]; @@ -116,6 +124,7 @@ export const SettingCategories = { ACCESS_CONTROL: "access-control", PROVISIONING: "provisioning", BRANDING: "branding", + WECHAT_AUTH: "wechat-auth", SAML_AUTH: "saml-auth", OIDC_AUTH: "oidc-auth", }; diff --git a/app/client/src/ce/selectors/settingsSelectors.tsx b/app/client/src/ce/selectors/settingsSelectors.tsx new file mode 100644 index 0000000000..45ecad7678 --- /dev/null +++ b/app/client/src/ce/selectors/settingsSelectors.tsx @@ -0,0 +1,4 @@ +import type { AppState } from "@appsmith/reducers"; + +export const getWXLoginClientId = (state: AppState) => + state.settings.config.APPSMITH_WX_CLIENT_ID; diff --git a/app/client/src/ce/utils/adminSettingsHelpers.ts b/app/client/src/ce/utils/adminSettingsHelpers.ts index 8282f35158..b20cba2304 100644 --- a/app/client/src/ce/utils/adminSettingsHelpers.ts +++ b/app/client/src/ce/utils/adminSettingsHelpers.ts @@ -1,32 +1,6 @@ import { tenantConfigConnection } from "@appsmith/constants/tenantConstants"; import { ADMIN_SETTINGS_CATEGORY_DEFAULT_PATH } from "constants/routes"; import type { User } from "constants/userConstants"; -// const { disableLoginForm, enableGithubOAuth, enableGoogleOAuth } = -// getAppsmithConfigs(); - -/* settings is the updated & unsaved settings on Admin settings page */ -// export const saveAllowed = ( -// settings: any, -// isFormLoginEnabled: boolean, -// socialLoginList: string[], -// ) => { -// const connectedMethodsCount = -// socialLoginList.length + (isFormLoginEnabled ? 1 : 0); -// if (connectedMethodsCount === 1) { -// const checkFormLogin = -// !("APPSMITH_FORM_LOGIN_DISABLED" in settings) && isFormLoginEnabled, -// checkGoogleAuth = -// settings["APPSMITH_OAUTH2_GOOGLE_CLIENT_ID"] !== "" && -// enableGoogleOAuth, -// checkGithubAuth = -// settings["APPSMITH_OAUTH2_GITHUB_CLIENT_ID"] !== "" && -// enableGithubOAuth; - -// return checkFormLogin || checkGoogleAuth || checkGithubAuth; -// } else { -// return connectedMethods.length >= 2; -// } -// }; /* settings is the updated & unsaved settings on Admin settings page */ export const saveAllowed = ( settings: any, @@ -37,15 +11,25 @@ export const saveAllowed = ( socialLoginList.length + (isFormLoginEnabled ? 1 : 0); if (connectedMethodsCount === 1) { const checkFormLogin = - !("APPSMITH_FORM_LOGIN_DISABLED" in settings) && isFormLoginEnabled, + !("APPSMITH_FORM_LOGIN_DISABLED" in settings) && isFormLoginEnabled, checkGoogleAuth = settings["APPSMITH_OAUTH2_GOOGLE_CLIENT_ID"] !== "" && socialLoginList.includes("google"), checkGithubAuth = settings["APPSMITH_OAUTH2_GITHUB_CLIENT_ID"] !== "" && - socialLoginList.includes("github"); + socialLoginList.includes("github"), + checkWeChatAuth = + settings["APPSMITH_WX_CLIENT_ID"] !== "" && + socialLoginList.includes("wechat"); + + + return ( + checkFormLogin || + checkGoogleAuth || + checkGithubAuth || + checkWeChatAuth + ); - return checkFormLogin || checkGoogleAuth || checkGithubAuth; } else { return connectedMethodsCount >= 2; } diff --git a/app/client/src/components/editorComponents/CodeEditor/CodeEditorPanel.tsx b/app/client/src/components/editorComponents/CodeEditor/CodeEditorPanel.tsx new file mode 100644 index 0000000000..6d0f017e47 --- /dev/null +++ b/app/client/src/components/editorComponents/CodeEditor/CodeEditorPanel.tsx @@ -0,0 +1,160 @@ +import React, { useMemo, useState } from "react"; +import styled from "styled-components"; +import Trigger from "rc-trigger"; +import { Button } from "antd"; +import { Icon } from "@blueprintjs/core"; +import { Resizable } from "react-resizable"; +import Handle from "./handler"; +import Draggable from "react-draggable"; +import { getPanelStyle, savePanelStyle } from "utils/AppUtils"; + +const Wrapper = styled.div` + position: fixed; + // top: 50%; + left: 12%; + z-index: 10; + box-shadow: rgba(0, 0, 0, 0.1) 0px 0px 10px 0px; + border-radius: 8px; + background: #fff; +`; + +const WrapperOverLay = styled.div` + position: fixed; + z-index: 10; + border: 1px solid pink; + border-radius: 8px; +`; +const HeaderWrapper = styled.div` + cursor: move; + padding: 8px; + display: flex; + justify-content: space-between; + align-items: center; + border-bottom: 1px solid #d7d9e0; + border-radius: 8px 8px 0 0; +`; +const TitleWrapper = styled.div` + display: flex; + align-items: center; + font-weight: 500; + font-size: 16px; + line-height: 16px; + color: #222222; +`; +const BodyWrapper = styled.div` + height: calc(100% - 40px); +`; + +type Position = { + xRate: number; + yRate: number; +}; +const CodeEditorPanel = (props: any) => { + const [visible, setVisible] = useState(false); + const [currentPosition, setCurrentPosition] = useState({ + xRate: 150, + yRate: 150, + }); + const panelStyle = useMemo(() => getPanelStyle(), [props.editor]); + const [size, setSize] = useState({ + w: panelStyle.codeEditor.w, + h: panelStyle.codeEditor.h, + }); + + const onDrag = (e: any, data: any) => { + setCurrentPosition({ xRate: data.lastX, yRate: data.lastY }); + }; + + const onHandleVisible = (v: boolean) => { + setVisible(v); + props.onPanelVisibleChange && props.onPanelVisibleChange(v); + }; + + return ( + onHandleVisible(visible)} + // afterPopupVisibleChange={(visible) => props.onVisibleChange(visible)} + popup={() => ( + + + { + setSize({ w: size.width, h: size.height }); + }} + onResizeStop={() => + savePanelStyle({ + ...panelStyle, + codeEditor: { w: size.w, h: size.h }, + }) + } + handle={Handle} + resizeHandles={["s", "e", "se"]} + minConstraints={[480, 360]} + > + + + + + + {props.source} + + + + +
+ {props.editor} +
+
+
+
+
+ )} + > +
+ +
+
+ ); +}; + +export default CodeEditorPanel; diff --git a/app/client/src/components/editorComponents/CodeEditor/handler.tsx b/app/client/src/components/editorComponents/CodeEditor/handler.tsx new file mode 100644 index 0000000000..ad72ab0df0 --- /dev/null +++ b/app/client/src/components/editorComponents/CodeEditor/handler.tsx @@ -0,0 +1,116 @@ +import styled, { css } from "styled-components"; +// util +export type ReactRef = { + readonly current: T | null; +}; +// Defines which resize handles should be rendered (default: 'se') +// Allows for any combination of: +// 's' - South handle (bottom-center) +// 'w' - West handle (left-center) +// 'e' - East handle (right-center) +// 'n' - North handle (top-center) +// 'sw' - Southwest handle (bottom-left) +// 'nw' - Northwest handle (top-left) +// 'se' - Southeast handle (bottom-right) +// 'ne' - Northeast handle (top-right) +// Custom component for resize handles +export type ResizeHandleAxis = + | "s" + | "w" + | "e" + | "n" + | "sw" + | "nw" + | "se" + | "ne"; +const color = "rgb(118, 136, 150)"; +const EdgeHandle = css` + &::before { + position: absolute; + // background: ${color}; + content: ""; + } + // small middle circles + &::after { + position: absolute; + content: ""; + width: 6px; + height: 6px; + // border-radius: 50%; + // background: ${color}; + top: calc(50% - 2px); + left: calc(50% - 2px); + border: none !important; + } +`; + +const HorizontalHandle = css<{ axis: string }>` + ${EdgeHandle} + ${(props) => (props.axis === "s" ? "bottom: -10px;" : "top: -10px;")} + /* left: -4px; */ + height: 12px !important; + /* width: calc(100% + 8px) !important; */ + width: 100%; + cursor: row-resize; + &::before { + top: 50%; + right: 0px; + left: 0px; + height: 1px; + } +`; + +const VerticalHandleStyles = css<{ axis: string }>` + ${EdgeHandle} + ${(props) => (props.axis === "e" ? "right: -10px;" : "left: -10px;")} + width: 12px !important; + top: 0px; + /* height: calc(100% + 8px) !important; */ + height: 100%; + cursor: col-resize; + &::before { + left: 50%; + bottom: 0px; + top: 0px; + width: 1px; + } +`; + +const CornerHandle = css<{ axis: string }>` + position: absolute; + z-index: 3; + width: 10px !important; + height: 10px !important; + &::after { + width: 10px !important; + height: 10px !important; + border: none !important; + } + cursor: ${(props) => props.axis + "-resize"} !important; + ${(props) => (["nw", "ne"].indexOf(props.axis) >= 0 ? "top: -5px;" : "")}; + ${(props) => (["sw", "se"].indexOf(props.axis) >= 0 ? "bottom: -5px;" : "")}; + ${(props) => (["sw", "nw"].indexOf(props.axis) >= 0 ? "left: -5px;" : "")}; + ${(props) => (["se", "ne"].indexOf(props.axis) >= 0 ? "right: -5px;" : "")}; +`; + +const ResizeHandle = styled.div<{ axis: string }>` + position: absolute; + background-image: none; + ${(props) => (["s", "n"].indexOf(props.axis) >= 0 ? HorizontalHandle : "")}; + ${(props) => + ["w", "e"].indexOf(props.axis) >= 0 ? VerticalHandleStyles : ""}; + ${(props) => + ["sw", "nw", "se", "ne"].indexOf(props.axis) >= 0 ? CornerHandle : ""}; +`; + +const Handle = (axis: ResizeHandleAxis, ref: ReactRef) => { + return ( + + ); +}; + +export default Handle; diff --git a/app/client/src/components/editorComponents/LazyCodeEditor/index.tsx b/app/client/src/components/editorComponents/LazyCodeEditor/index.tsx index 5ccf4b26c9..cb8ce9e4ef 100644 --- a/app/client/src/components/editorComponents/LazyCodeEditor/index.tsx +++ b/app/client/src/components/editorComponents/LazyCodeEditor/index.tsx @@ -11,6 +11,7 @@ import { CODE_EDITOR_LOADING_ERROR } from "@appsmith/constants/messages"; import assertNever from "assert-never/index"; import log from "loglevel"; import { toast } from "design-system"; +import CodeEditorPanel from "../CodeEditor/CodeEditorPanel"; let CachedCodeEditor: typeof CodeEditor | undefined; @@ -173,6 +174,8 @@ function LazyCodeEditor({ "editor" | "editor-focused" | "fallback" >("fallback"); const [showLoadingProgress, setShowLoadingProgress] = useState(false); + const [panelVisible, setPanelVisible] = useState(false); + const [isHovered, setPanelIsHovered] = useState(false); const stateMachine = useRef( new LazyCodeEditorStateMachine((state) => { @@ -213,18 +216,64 @@ function LazyCodeEditor({ throw new Error( "CodeEditor is not loaded. This is likely an issue with the state machine.", ); + const content = (flag?: any) => { + const panelEditorStyle = !flag + ? { + border: "none", + borderLess: true, + folding: true, + height: "100%", + hideEvaluatedValue: true, + mode: "javascript", + showLightningMenu: false, + showLineNumbers: true, + size: "EXTENDED", + tabBehaviour: "INDENT", + theme: "LIGHT", + } + : {}; + return ( +
+ + + +
+ ); + }; + if (otherProps.isJSObject) { + return content(true); + } return ( - setPanelIsHovered(true)} + onMouseLeave={() => setPanelIsHovered(false)} + onFocus={() => setPanelIsHovered(true)} > - { + setPanelVisible(v); + }} + isShow={isHovered} + source={otherProps.evaluatedPopUpLabel} /> - + ); } diff --git a/app/client/src/components/editorComponents/ResizeStyledComponents.tsx b/app/client/src/components/editorComponents/ResizeStyledComponents.tsx index 4ed759b9f8..09b05a60e5 100644 --- a/app/client/src/components/editorComponents/ResizeStyledComponents.tsx +++ b/app/client/src/components/editorComponents/ResizeStyledComponents.tsx @@ -45,7 +45,7 @@ const VerticalResizeIndicators = css<{ border-radius: 50%/16%; background: ${Colors.GREY_1}; top: calc(50% - 8px); - left: calc(50% - 2.5px); + left: calc(50% + 3.5px); border: ${(props) => { return `1px solid ${ props.isHovered ? Colors.PRIMARY_LIGHT : Colors.PRIMARY @@ -142,25 +142,25 @@ export const HorizontalHandleStyles = css<{ &::after { width: ${EDGE_RESIZE_BAR_LONG}px; height: ${EDGE_RESIZE_BAR_SHORT}px; - top: calc(50% - 2px); + top: calc(50% + 2.5px); left: calc(50% - ${EDGE_RESIZE_BAR_LONG / 2}px); } `; export const LeftHandleStyles = styled.div` ${VerticalHandleStyles} - left: ${-EDGE_RESIZE_HANDLE_WIDTH / 2 - WIDGET_PADDING + 1.5}px; + left: ${-EDGE_RESIZE_HANDLE_WIDTH / 2 - WIDGET_PADDING - 8.5}px; `; export const RightHandleStyles = styled.div` ${VerticalHandleStyles}; - right: ${-EDGE_RESIZE_HANDLE_WIDTH / 2 - WIDGET_PADDING + 3.5}px; + right: ${-EDGE_RESIZE_HANDLE_WIDTH / 2 - WIDGET_PADDING + 5.5}px; height: calc(100% + ${2 * WIDGET_PADDING}px); `; export const TopHandleStyles = styled.div` ${HorizontalHandleStyles}; - top: ${-EDGE_RESIZE_HANDLE_WIDTH / 2 - WIDGET_PADDING + 1.5}px; + top: ${-EDGE_RESIZE_HANDLE_WIDTH / 2 - WIDGET_PADDING - 6.5}px; `; export const BottomHandleStyles = styled.div` diff --git a/app/client/src/components/editorComponents/WidgetNameComponent/index.tsx b/app/client/src/components/editorComponents/WidgetNameComponent/index.tsx index 46600e60f7..416a90747c 100644 --- a/app/client/src/components/editorComponents/WidgetNameComponent/index.tsx +++ b/app/client/src/components/editorComponents/WidgetNameComponent/index.tsx @@ -48,7 +48,7 @@ const PositionStyle = styled.div<{ cursor: pointer; top: ${(props) => props.topRow > 2 - ? `${-1 * WidgetNameComponentHeight + 1 + props.positionOffset[0]}px` + ? `${-1 * WidgetNameComponentHeight + props.positionOffset[0] - 4}px` : `calc(100% - ${1 + props.positionOffset[0]}px)`}; height: ${WidgetNameComponentHeight}px; right: ${(props) => props.positionOffset[1]}px; diff --git a/app/client/src/components/propertyControls/JSControl.tsx b/app/client/src/components/propertyControls/JSControl.tsx new file mode 100644 index 0000000000..517c6da911 --- /dev/null +++ b/app/client/src/components/propertyControls/JSControl.tsx @@ -0,0 +1,132 @@ +import React, { useContext } from "react"; +import type { ControlProps } from "./BaseControl"; +import BaseControl from "./BaseControl"; +import { StyledDynamicInput } from "./StyledControls"; +import type { InputType } from "components/constants"; +import type { CodeEditorExpected } from "components/editorComponents/CodeEditor"; +import { + CodeEditorBorder, + EditorModes, + EditorSize, + EditorTheme, + TabBehaviour, +} from "components/editorComponents/CodeEditor/EditorConfig"; +import { CollapseContext } from "pages/Editor/PropertyPane/PropertySection"; +import LazyCodeEditor from "../editorComponents/LazyCodeEditor"; +import type { AdditionalDynamicDataTree } from "utils/autocomplete/customTreeTypeDefCreator"; + +export function JsData(props: { + label: string; + value: string; + onBlur?: () => void; + onChange: (event: React.ChangeEvent | string) => void; + onFocus?: () => void; + evaluatedValue?: any; + expected?: CodeEditorExpected; + placeholder?: string; + dataTreePath?: string; + additionalAutocomplete?: AdditionalDynamicDataTree; + theme?: EditorTheme; + hideEvaluatedValue?: boolean; +}) { + const { + dataTreePath, + evaluatedValue, + expected, + hideEvaluatedValue, + label, + onBlur, + onChange, + onFocus, + placeholder, + value, + } = props; + + //subscribing to context to help re-render component on Property section open or close + const isOpen = useContext(CollapseContext); + + return ( + + + + ); +} + +class JsDataControl extends BaseControl { + render() { + const { + additionalAutoComplete, + dataTreePath, + defaultValue, + expected, + hideEvaluatedValue, + label, + onBlur, + onFocus, + placeholderText, + propertyValue, + } = this.props; + + return ( + + ); + } + + onTextChange = (event: React.ChangeEvent | string) => { + const value = event; + // if (typeof event !== "string") { + // value = event.target.value; + // } + this.updateProperty(this.props.propertyName, value, true); + }; + + static getControlType() { + return "JS_DATA"; + } +} + +export interface JsDataControllProps extends ControlProps { + placeholderText: string; + inputType: InputType; + validationMessage?: string; + isDisabled?: boolean; + defaultValue?: any; + onFocus?: () => void; + onBlur?: () => void; +} + +export default JsDataControl; diff --git a/app/client/src/components/propertyControls/index.ts b/app/client/src/components/propertyControls/index.ts index 56f15fae9c..c9aafef4c2 100644 --- a/app/client/src/components/propertyControls/index.ts +++ b/app/client/src/components/propertyControls/index.ts @@ -70,8 +70,11 @@ import type { OneClickBindingControlProps } from "./OneClickBindingControl"; import OneClickBindingControl from "./OneClickBindingControl"; import type { WrappedCodeEditorControlProps } from "./WrappedCodeEditorControl"; import WrappedCodeEditorControl from "./WrappedCodeEditorControl"; +import type { JsDataControllProps } from "components/propertyControls/JSControl"; +import JsDataControl from "components/propertyControls/JSControl"; export const PropertyControls = { + JsDataControl, InputTextControl, DropDownControl, SwitchControl, @@ -124,6 +127,7 @@ export const PropertyControls = { }; export type PropertyControlPropsType = + | JsDataControllProps | ControlProps | InputControlProps | DropDownControlProps diff --git a/app/client/src/constants/Colors.tsx b/app/client/src/constants/Colors.tsx index d6322d0dbf..3dc74fef2d 100644 --- a/app/client/src/constants/Colors.tsx +++ b/app/client/src/constants/Colors.tsx @@ -52,7 +52,7 @@ export const Colors = { PRIMARY: primaryColor, PRIMARY_DARK: "#3ababc", - PRIMARY_LIGHT: "#CAECDC", + PRIMARY_LIGHT: "#4fb2b4", GREEN: primaryColor, FOAM: "#D9FDED", LIGHT_GREEN_CYAN: "#e5f6ec", diff --git a/app/client/src/constants/ThirdPartyConstants.tsx b/app/client/src/constants/ThirdPartyConstants.tsx index 447ef015a4..faa21f73b0 100644 --- a/app/client/src/constants/ThirdPartyConstants.tsx +++ b/app/client/src/constants/ThirdPartyConstants.tsx @@ -25,6 +25,8 @@ export const SIGNUP_RESTRICTION_DOC = "https://docs.appsmith.com/getting-started/setup/instance-configuration/disable-user-signup#disable-sign-up"; export const EMBED_PRIVATE_APPS_DOC = "https://docs.appsmith.com/advanced-concepts/embed-appsmith-into-existing-application#embedding-private-apps"; +export const WX_SIGNUP_SETUP_DOC = + "https://developers.weixin.qq.com/doc/oplatform/Website_App/WeChat_Login/Wechat_Login.html"; export const PROVISIONING_SETUP_DOC = "http://docs.appsmith.com/advanced-concepts/user-provisioning-group-sync"; export const PRICING_PAGE_URL = ( diff --git a/app/client/src/constants/userConstants.ts b/app/client/src/constants/userConstants.ts index 1eea49c854..a7776f1140 100644 --- a/app/client/src/constants/userConstants.ts +++ b/app/client/src/constants/userConstants.ts @@ -18,6 +18,7 @@ export type User = { adminSettingsVisible?: boolean; isAnonymous?: boolean; isIntercomConsentGiven?: boolean; + bindWeChat: boolean; }; export interface UserApplication { @@ -40,6 +41,7 @@ export const DefaultCurrentUserDetails: User = { enableTelemetry: false, adminSettingsVisible: false, isIntercomConsentGiven: false, + bindWeChat: false, }; // TODO keeping it here instead of the USER_API since it leads to cyclic deps errors during tests diff --git a/app/client/src/index.tsx b/app/client/src/index.tsx index 57adfafbc9..934e55eed7 100755 --- a/app/client/src/index.tsx +++ b/app/client/src/index.tsx @@ -53,7 +53,7 @@ class Empty extends React.Component { // eslint-disable-next-line @typescript-eslint/no-unused-vars const inst = createReactApp(Empty, React, ReactDOM, {}); // add touch emulator -import "@vant/touch-emulator"; +// import "@vant/touch-emulator"; import "react-sortable-tree-patch-react-17/style.css"; const shouldAutoFreeze = process.env.NODE_ENV === "development"; diff --git a/app/client/src/pages/AppViewer/AppViewerPageContainer.tsx b/app/client/src/pages/AppViewer/AppViewerPageContainer.tsx index 55ed64d761..486fa5f548 100644 --- a/app/client/src/pages/AppViewer/AppViewerPageContainer.tsx +++ b/app/client/src/pages/AppViewer/AppViewerPageContainer.tsx @@ -27,13 +27,14 @@ import { import equal from "fast-deep-equal/es6"; const Section = styled.section<{ - height: number; + theight: number; isMobile: boolean; }>` background: ${({ isMobile }) => (isMobile ? "#fff" : "transparent")}; width: ${({ isMobile }) => (isMobile ? "450px" : "100%")}; height: 100%; - min-height: ${({ height, isMobile }) => (isMobile ? `${height}px` : "unset")}; + min-height: ${({ theight, isMobile }) => + isMobile ? `${theight}px` : "unset"}; margin: 0 auto; position: relative; overflow: auto; @@ -115,7 +116,7 @@ function AppViewerPageContainer(props: AppViewerPageContainerProps) { return (
diff --git a/app/client/src/pages/Editor/Explorer/ExplorerIcons.tsx b/app/client/src/pages/Editor/Explorer/ExplorerIcons.tsx index 2d685baaaa..4b31504d88 100644 --- a/app/client/src/pages/Editor/Explorer/ExplorerIcons.tsx +++ b/app/client/src/pages/Editor/Explorer/ExplorerIcons.tsx @@ -11,6 +11,7 @@ import { Icon } from "design-system"; import { getAssetUrl } from "@appsmith/utils/airgapHelpers"; import { importSvg } from "design-system-old"; import { Colors } from "constants/Colors"; +import { replacePluginIcon } from "utils/AppsmithUtils"; const ApiIcon = importSvg(() => import("assets/icons/menu/api-colored.svg")); const CurlIcon = importSvg(() => import("assets/images/Curl-logo.svg")); @@ -101,11 +102,9 @@ const PluginIcon = styled.img` export const getPluginIcon = (plugin?: Plugin) => { if (plugin && plugin.iconLocation) { + const logo_location = replacePluginIcon(plugin.iconLocation); return ( - + ); } return ; diff --git a/app/client/src/pages/Settings/FormGroup/CopyUrlForm.tsx b/app/client/src/pages/Settings/FormGroup/CopyUrlForm.tsx index b064810f0d..bdc68fbf15 100644 --- a/app/client/src/pages/Settings/FormGroup/CopyUrlForm.tsx +++ b/app/client/src/pages/Settings/FormGroup/CopyUrlForm.tsx @@ -10,6 +10,9 @@ export const BodyContainer = styled.div` .ads-v2-input__input-section-icon[data-has-onclick="true"] * { cursor: pointer !important; } + .ur--has-border { + width: 638px; + } `; const HeaderWrapper = styled.div` diff --git a/app/client/src/pages/Settings/FormGroup/group.tsx b/app/client/src/pages/Settings/FormGroup/group.tsx index b26ba724d1..cd836a4eb6 100644 --- a/app/client/src/pages/Settings/FormGroup/group.tsx +++ b/app/client/src/pages/Settings/FormGroup/group.tsx @@ -52,6 +52,7 @@ const GroupBody = styled.div` > div { margin-top: 0px; margin-bottom: 12px; + width: 638px; } } &&&& { diff --git a/app/client/src/pages/Settings/SettingsForm.tsx b/app/client/src/pages/Settings/SettingsForm.tsx index 58e8942dea..1fbd81e1f8 100644 --- a/app/client/src/pages/Settings/SettingsForm.tsx +++ b/app/client/src/pages/Settings/SettingsForm.tsx @@ -77,7 +77,7 @@ export function SettingsForm( const { category, selected: subCategory } = params; const settingsDetails = getSettingsConfig(category, subCategory); const { settings, settingsConfig } = props; - const details = getSettingDetail(category, subCategory); + const details = getSettingDetail(category, subCategory) as any; const dispatch = useDispatch(); const isSavable = AdminConfig.savableCategories.includes( subCategory ?? category, @@ -101,6 +101,9 @@ export function SettingsForm( !isTenantConfig(s.id), ); + const addconfig = details?.settings?.find( + (item: any) => item.label === "Redirect URL", + ); const saveChangedSettings = () => { const settingsKeyLength = Object.keys(props.settings).length; const isOnlyEnvSettings = @@ -110,7 +113,14 @@ export function SettingsForm( updatedTenantSettings.length !== settingsKeyLength; if (isOnlyEnvSettings) { // only env settings - dispatch(saveSettings(props.settings)); + const settings = { ...props.settings }; + // 检查 addconfig 是否为 undefined + if (addconfig !== undefined) { + // 如果不为 undefined,则添加对应的属性 + settings[addconfig.id] = `${window.location.origin}${addconfig.value}`; + } + // 调用 dispatch 保存设置 + dispatch(saveSettings(settings)); } else { // only tenant settings const config: any = {}; diff --git a/app/client/src/pages/UserProfile/General.tsx b/app/client/src/pages/UserProfile/General.tsx index 02be160ade..12194df6a2 100644 --- a/app/client/src/pages/UserProfile/General.tsx +++ b/app/client/src/pages/UserProfile/General.tsx @@ -1,4 +1,5 @@ import React, { useState } from "react"; +import styled from "styled-components"; import { Button, Input, toast, Text } from "design-system"; import { useDispatch, useSelector } from "react-redux"; import { getCurrentUser } from "selectors/usersSelectors"; @@ -17,9 +18,22 @@ import { Wrapper, FieldWrapper, LabelWrapper } from "./StyledComponents"; import { ANONYMOUS_USERNAME } from "constants/userConstants"; import { ALL_LANGUAGE_CHARACTERS_REGEX } from "constants/Regex"; import { createMessage } from "design-system-old/build/constants/messages"; -import { notEmptyValidator } from "design-system-old"; +import { notEmptyValidator, TextType } from "design-system-old"; import { getIsFormLoginEnabled } from "@appsmith/selectors/tenantSelectors"; +type ButtonProps = { + wechatEnable?: boolean; +}; + +const BindButton = styled.button` + width: 96px; + height: 32px; + border: 1px solid ${(props) => (props.wechatEnable ? "#D4DAD9" : "#13c2c2")}; + border-radius: 4px; + padding: 5px 15px; + color: ${(props) => (props.wechatEnable ? "#D4DAD9" : "#13c2c2")}; +`; + const nameValidator = ( value: string, ): { @@ -71,6 +85,11 @@ function General() { }; if (user?.email === ANONYMOUS_USERNAME) return null; + const [wechatEnable, setwechatEnable] = useState(false); + + const onBindwechatClick = () => { + setwechatEnable(!wechatEnable); + }; return ( @@ -138,6 +157,20 @@ function General() { )} + + + 微信 + +
+ { + + 绑定微信 + + } + + {wechatEnable ? 微信已绑定邮箱 : '} +
+
); } diff --git a/app/client/src/utils/AppUtils.ts b/app/client/src/utils/AppUtils.ts index 9a00cfd089..a707d71336 100644 --- a/app/client/src/utils/AppUtils.ts +++ b/app/client/src/utils/AppUtils.ts @@ -47,3 +47,53 @@ export async function readBlob(blobUrl: string): Promise { }; }); } +export type PanelStatus = { left: boolean; bottom: boolean; right: boolean }; + +export type PanelStyle = { + bottom: { + h: number; + }; + codeEditor: { + w: number; + h: number; + }; +}; + +export const DefaultPanelStatus: PanelStatus = { + left: true, + bottom: true, + right: true, +}; +const DefaultPanelStyle: PanelStyle = { + bottom: { + h: 285, + }, + codeEditor: { + w: 744, + h: 468, + }, +}; + +export function savePanelStatus(panelStatus: PanelStatus) { + localStorage.setItem("editor_panel_status", JSON.stringify(panelStatus)); +} + +export function getPanelStatus(): PanelStatus { + const str = localStorage.getItem("editor_panel_status"); + if (!str) { + return DefaultPanelStatus; + } + return { ...DefaultPanelStatus, ...JSON.parse(str) }; +} + +export function savePanelStyle(panelStyle: PanelStyle) { + localStorage.setItem("editor_panel_style", JSON.stringify(panelStyle)); +} + +export function getPanelStyle(): PanelStyle { + const str = localStorage.getItem("editor_panel_style"); + if (!str) { + return DefaultPanelStyle; + } + return { ...DefaultPanelStyle, ...JSON.parse(str) }; +} diff --git a/app/client/src/widgets/TableWidgetV2/component/Constants.ts b/app/client/src/widgets/TableWidgetV2/component/Constants.ts index edcc8c4be5..447fe13854 100644 --- a/app/client/src/widgets/TableWidgetV2/component/Constants.ts +++ b/app/client/src/widgets/TableWidgetV2/component/Constants.ts @@ -23,6 +23,7 @@ export type TableSizes = { TABLE_HEADER_HEIGHT: number; ROW_HEIGHT: number; ROW_FONT_SIZE: number; + TABLE_FOOTER_HEIGHT: number; VERTICAL_PADDING: number; EDIT_ICON_TOP: number; ROW_VIRTUAL_OFFSET: number; @@ -57,6 +58,7 @@ export const TABLE_SIZES: { [key: string]: TableSizes } = { [CompactModeTypes.DEFAULT]: { COLUMN_HEADER_HEIGHT: 32, TABLE_HEADER_HEIGHT: 40, + TABLE_FOOTER_HEIGHT: 40, ROW_HEIGHT: 40, ROW_FONT_SIZE: 14, VERTICAL_PADDING: 6, @@ -67,6 +69,7 @@ export const TABLE_SIZES: { [key: string]: TableSizes } = { [CompactModeTypes.SHORT]: { COLUMN_HEADER_HEIGHT: 32, TABLE_HEADER_HEIGHT: 40, + TABLE_FOOTER_HEIGHT: 40, ROW_HEIGHT: 30, ROW_FONT_SIZE: 12, VERTICAL_PADDING: 0, @@ -77,6 +80,7 @@ export const TABLE_SIZES: { [key: string]: TableSizes } = { [CompactModeTypes.TALL]: { COLUMN_HEADER_HEIGHT: 32, TABLE_HEADER_HEIGHT: 40, + TABLE_FOOTER_HEIGHT: 40, ROW_HEIGHT: 60, ROW_FONT_SIZE: 18, VERTICAL_PADDING: 16, diff --git a/app/client/src/widgets/TableWidgetV2/component/StaticTable.tsx b/app/client/src/widgets/TableWidgetV2/component/StaticTable.tsx index 9abeed07b0..1002efa234 100644 --- a/app/client/src/widgets/TableWidgetV2/component/StaticTable.tsx +++ b/app/client/src/widgets/TableWidgetV2/component/StaticTable.tsx @@ -40,6 +40,7 @@ type StaticTableProps = TableColumnHeaderProps & { scrollContainerStyles: any; useVirtual: boolean; tableBodyRef?: React.MutableRefObject; + isVisiblePagination?: boolean; }; const StaticTable = (props: StaticTableProps, ref: React.Ref) => { @@ -90,6 +91,7 @@ const StaticTable = (props: StaticTableProps, ref: React.Ref) => { tableSizes={props.tableSizes} useVirtual={props.useVirtual} width={props.width - TABLE_SCROLLBAR_WIDTH / 2} + isVisiblePagination={props.isVisiblePagination} /> ); diff --git a/app/client/src/widgets/TableWidgetV2/component/Table.tsx b/app/client/src/widgets/TableWidgetV2/component/Table.tsx index f3ed20088b..fa413e1739 100644 --- a/app/client/src/widgets/TableWidgetV2/component/Table.tsx +++ b/app/client/src/widgets/TableWidgetV2/component/Table.tsx @@ -295,11 +295,20 @@ export function Table(props: TableProps) { height: isHeaderVisible ? props.height - tableSizes.TABLE_HEADER_HEIGHT - + (props.isVisiblePagination ? tableSizes.TABLE_FOOTER_HEIGHT : 0) - TABLE_SCROLLBAR_HEIGHT - SCROLL_BAR_OFFSET - : props.height - TABLE_SCROLLBAR_HEIGHT - SCROLL_BAR_OFFSET, + : props.height - + TABLE_SCROLLBAR_HEIGHT - + SCROLL_BAR_OFFSET - + (props.isVisiblePagination ? tableSizes.TABLE_FOOTER_HEIGHT : 0), }; - }, [isHeaderVisible, props.height, tableSizes.TABLE_HEADER_HEIGHT]); + }, [ + isHeaderVisible, + props.height, + tableSizes.TABLE_HEADER_HEIGHT, + props.isVisiblePagination, + ]); const shouldUseVirtual = props.serverSidePaginationEnabled && @@ -393,7 +402,7 @@ export function Table(props: TableProps) { /> - + )} {/* 表格内容 */}
- {!shouldUseVirtual && ( - - )} + {!shouldUseVirtual && ( + + )} - {shouldUseVirtual && ( - - )} + {shouldUseVirtual && ( + + )}
{/* 表格工具栏-底部 */} - {isHeaderVisible && ( - + {props.isVisiblePagination && ( - )} diff --git a/app/client/src/widgets/TableWidgetV2/component/TableBody/index.tsx b/app/client/src/widgets/TableWidgetV2/component/TableBody/index.tsx index d3de83d004..452b28ea1b 100644 --- a/app/client/src/widgets/TableWidgetV2/component/TableBody/index.tsx +++ b/app/client/src/widgets/TableWidgetV2/component/TableBody/index.tsx @@ -80,6 +80,7 @@ type BodyPropsType = { width?: number; tableSizes: TableSizes; innerElementType?: ReactElementType; + isVisiblePagination?: boolean; }; const TableVirtualBodyComponent = React.forwardRef( @@ -91,6 +92,9 @@ const TableVirtualBodyComponent = React.forwardRef( height={ props.height - props.tableSizes.TABLE_HEADER_HEIGHT - + (props.isVisiblePagination + ? props.tableSizes.TABLE_FOOTER_HEIGHT + : 0) - 2 * WIDGET_PADDING } innerElementType={props.innerElementType} diff --git a/app/client/src/widgets/TableWidgetV2/component/TableStyledWrappers.tsx b/app/client/src/widgets/TableWidgetV2/component/TableStyledWrappers.tsx index ddeb29346e..932bc7b296 100644 --- a/app/client/src/widgets/TableWidgetV2/component/TableStyledWrappers.tsx +++ b/app/client/src/widgets/TableWidgetV2/component/TableStyledWrappers.tsx @@ -684,6 +684,7 @@ export const TableHeaderWrapper = styled.div<{ width: number; tableSizes: TableSizes; backgroundColor?: Color; + topBoxShadow?: boolean; }>` position: relative; display: flex; @@ -694,6 +695,8 @@ export const TableHeaderWrapper = styled.div<{ } height: ${(props) => props.tableSizes.TABLE_HEADER_HEIGHT}px; min-height: ${(props) => props.tableSizes.TABLE_HEADER_HEIGHT}px; + box-shadow: ${(props) => + props.topBoxShadow ? "rgba(33, 35, 38, 0.1) 0px -10px 10px -10px" : ""}; `; export const TableHeaderInnerWrapper = styled.div<{ diff --git a/app/client/src/widgets/TableWidgetV2/component/VirtualTable.tsx b/app/client/src/widgets/TableWidgetV2/component/VirtualTable.tsx index 44d6c8ed07..e6a6b77d98 100644 --- a/app/client/src/widgets/TableWidgetV2/component/VirtualTable.tsx +++ b/app/client/src/widgets/TableWidgetV2/component/VirtualTable.tsx @@ -35,6 +35,7 @@ type VirtualTableProps = TableColumnHeaderProps & { totalColumnsWidth?: number; scrollContainerStyles: any; useVirtual: boolean; + isVisiblePagination?: boolean; }; const VirtualTable = (props: VirtualTableProps, ref: React.Ref) => { @@ -75,6 +76,7 @@ const VirtualTable = (props: VirtualTableProps, ref: React.Ref) => { useVirtual={props.useVirtual} widgetId={props.widgetId} width={props.width} + isVisiblePagination={props.isVisiblePagination} /> )} diff --git a/app/client/src/widgets/TableWidgetV2/component/header/index.tsx b/app/client/src/widgets/TableWidgetV2/component/header/index.tsx index 9d34462729..aca0a7667a 100644 --- a/app/client/src/widgets/TableWidgetV2/component/header/index.tsx +++ b/app/client/src/widgets/TableWidgetV2/component/header/index.tsx @@ -52,16 +52,7 @@ export function TableHeader_Bottom( ...ActionProps } = props; - return isAddRowInProgress ? ( - - ) : ( + return ( = 16.3.0" + react-dom: ">= 16.3.0" + checksum: b8258a58938c261a79f1b9ffd67774283c1ac732423c1c9c9f5fe4d17a06886edd659891e445ba089828ca59f1885e5b909262e24cf60640b8ed05c8499c88bb + languageName: node + linkType: hard + +"react-draggable@npm:^4.0.3": + version: 4.4.6 + resolution: "react-draggable@npm:4.4.6" + dependencies: + clsx: ^1.1.1 + prop-types: ^15.8.1 + peerDependencies: + react: ">= 16.3.0" + react-dom: ">= 16.3.0" + checksum: 9b15aac59244873ac4561c5a2bead43a56e18d406e0a5f242bd4f9d151c074530c02b99387983104bf43417292f9cf8d063e554ed08d88792235e3fbc965f1b8 + languageName: node + linkType: hard + "react-element-to-jsx-string@npm:^15.0.0": version: 15.0.0 resolution: "react-element-to-jsx-string@npm:15.0.0" @@ -27790,6 +27829,18 @@ __metadata: languageName: node linkType: hard +"react-resizable@npm:^3.0.5": + version: 3.0.5 + resolution: "react-resizable@npm:3.0.5" + dependencies: + prop-types: 15.x + react-draggable: ^4.0.3 + peerDependencies: + react: ">= 16.3" + checksum: 616a10205acfaf8cc3aa0824b60f6d037eef87143d8f338cf826deb74a353db9b9baad67a65dc8535fe90840bfc3e1b8a901f9c247033ffeec2f30405ac7528e + languageName: node + linkType: hard + "react-resize-detector@npm:^7.1.2": version: 7.1.2 resolution: "react-resize-detector@npm:7.1.2"