diff --git a/app/client/src/actions/gitSyncActions.ts b/app/client/src/actions/gitSyncActions.ts index 6fe90fab01..6adf1cd12f 100644 --- a/app/client/src/actions/gitSyncActions.ts +++ b/app/client/src/actions/gitSyncActions.ts @@ -1,10 +1,14 @@ import { ReduxActionTypes } from "constants/ReduxActionConstants"; import { ConnectToGitPayload } from "api/GitSyncAPI"; import { ReduxActionWithCallbacks } from "../constants/ReduxActionConstants"; +import { GitSyncModalTab } from "entities/GitSync"; -export const setIsGitSyncModalOpen = (isOpen: boolean) => ({ +export const setIsGitSyncModalOpen = (payload: { + isOpen: boolean; + tab?: GitSyncModalTab; +}) => ({ type: ReduxActionTypes.SET_IS_GIT_SYNC_MODAL_OPEN, - payload: isOpen, + payload, }); export const commitToRepoInit = (payload: { @@ -58,3 +62,12 @@ export const createNewBranchInit = (branchName: string) => ({ type: ReduxActionTypes.CREATE_NEW_BRANCH_INIT, payload: branchName, }); + +export const setIsGitErrorPopupVisible = (payload: { isVisible: boolean }) => ({ + type: ReduxActionTypes.SHOW_ERROR_POPUP, + payload, +}); + +export const showCreateBranchPopup = () => ({ + type: ReduxActionTypes.SHOW_CREATE_GIT_BRANCH_POPUP, +}); diff --git a/app/client/src/assets/icons/ads/git-commit-line.svg b/app/client/src/assets/icons/ads/git-commit-line.svg new file mode 100644 index 0000000000..1ec8cb4ae6 --- /dev/null +++ b/app/client/src/assets/icons/ads/git-commit-line.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/client/src/components/editorComponents/Debugger/index.tsx b/app/client/src/components/editorComponents/Debugger/index.tsx index a4fecc30f8..95abf17fae 100644 --- a/app/client/src/components/editorComponents/Debugger/index.tsx +++ b/app/client/src/components/editorComponents/Debugger/index.tsx @@ -12,7 +12,7 @@ import { Colors } from "constants/Colors"; import { getTypographyByKey } from "constants/DefaultTheme"; import { Layers } from "constants/Layers"; import { stopEventPropagation } from "utils/AppsmithUtils"; -import { getFilteredErrors } from "selectors/debuggerSelectors"; +import { getMessageCount } from "selectors/debuggerSelectors"; import getFeatureFlags from "utils/featureFlags"; const Container = styled.div<{ errorCount: number; warningCount: number }>` @@ -59,13 +59,7 @@ const Container = styled.div<{ errorCount: number; warningCount: number }>` function Debugger() { const dispatch = useDispatch(); - const messageCounters = useSelector((state) => { - const errorKeys = Object.keys(getFilteredErrors(state)); - const warnings = errorKeys.filter((key: string) => key.includes("warning")) - .length; - const errors = errorKeys.length - warnings; - return { errors, warnings }; - }); + const messageCounters = useSelector(getMessageCount); const totalMessageCount = messageCounters.errors + messageCounters.warnings; const showDebugger = useSelector( @@ -101,10 +95,34 @@ function Debugger() { ) : null; } -const TriggerContainer = styled.div` +const TriggerContainer = styled.div<{ + errorCount: number; + warningCount: number; +}>` + position: relative; + overflow: visible; display: flex; align-items: center; - margin-right: ${(props) => props.theme.spaces[4]}px; + margin-right: ${(props) => props.theme.spaces[9]}px; + + .debugger-count { + color: ${Colors.WHITE}; + ${(props) => getTypographyByKey(props, "p3")} + height: 16px; + width: 16px; + background-color: ${(props) => + props.errorCount + props.warningCount > 0 + ? props.errorCount === 0 + ? props.theme.colors.debugger.floatingButton.warningCount + : props.theme.colors.debugger.floatingButton.errorCount + : props.theme.colors.debugger.floatingButton.noErrorCount}; + position: absolute; + display: flex; + align-items: center; + justify-content: center; + top: 0; + left: 100%; + } `; export function DebuggerTrigger() { @@ -113,6 +131,10 @@ export function DebuggerTrigger() { (state: AppState) => state.ui.debugger.isOpen, ); + const messageCounters = useSelector(getMessageCount); + + const totalMessageCount = messageCounters.errors + messageCounters.warnings; + const onClick = (e: any) => { if (!showDebugger) AnalyticsUtil.logEvent("OPEN_DEBUGGER", { @@ -123,8 +145,16 @@ export function DebuggerTrigger() { }; return ( - + + {!!messageCounters.errors && ( + + {totalMessageCount > 9 ? "9+" : totalMessageCount} + + )} ); } diff --git a/app/client/src/constants/ReduxActionConstants.tsx b/app/client/src/constants/ReduxActionConstants.tsx index cc707622b6..a0c97cc2ab 100644 --- a/app/client/src/constants/ReduxActionConstants.tsx +++ b/app/client/src/constants/ReduxActionConstants.tsx @@ -9,6 +9,8 @@ export const ReduxSagaChannels: { [key: string]: string } = { }; export const ReduxActionTypes = { + SHOW_CREATE_GIT_BRANCH_POPUP: "SHOW_CREATE_GIT_BRANCH_POPUP", + SHOW_ERROR_POPUP: "SHOW_ERROR_POPUP", CONNECT_TO_GIT_INIT: "CONNECT_TO_GIT_INIT", CONNECT_TO_GIT_SUCCESS: "CONNECT_TO_GIT_SUCCESS", CREATE_NEW_BRANCH_INIT: "CREATE_NEW_BRANCH_INIT", diff --git a/app/client/src/constants/messages.ts b/app/client/src/constants/messages.ts index b91669d6cc..ddd714d50e 100644 --- a/app/client/src/constants/messages.ts +++ b/app/client/src/constants/messages.ts @@ -521,6 +521,10 @@ export const PULL = () => "PULL"; export const PUSH_CHANGES_IMMEDIATELY_TO = () => "Push changes immediately to"; export const COMMIT_AND_PUSH = () => "Commit and push"; export const COMMITTED_SUCCESSFULLY = () => "Committed Successfully"; +export const CONNECT_GIT = () => "Connect Git"; +export const RETRY = () => "RETRY"; +export const CREATE_NEW_BRANCH = () => "CREATE NEW BRANCH"; +export const ERROR_WHILE_PULLING_CHANGES = () => "ERROR WHILE PULLING CHANGES"; export const SNIPPET_DESCRIPTION = () => `Search and Insert code snippets to perform complex actions quickly.`; diff --git a/app/client/src/entities/GitSync.ts b/app/client/src/entities/GitSync.ts new file mode 100644 index 0000000000..4b72ced80b --- /dev/null +++ b/app/client/src/entities/GitSync.ts @@ -0,0 +1,5 @@ +export enum GitSyncModalTab { + GIT_CONNECTION, + DEPLOY, + MERGE, +} diff --git a/app/client/src/pages/Editor/EditorHeader.tsx b/app/client/src/pages/Editor/EditorHeader.tsx index 550a3adb4f..6f1f572205 100644 --- a/app/client/src/pages/Editor/EditorHeader.tsx +++ b/app/client/src/pages/Editor/EditorHeader.tsx @@ -225,7 +225,7 @@ export function EditorHeader(props: EditorHeaderProps) { ); const showGitSyncModal = useCallback(() => { - dispatch(setIsGitSyncModalOpen(true)); + dispatch(setIsGitSyncModalOpen({ isOpen: true })); }, [dispatch, setIsGitSyncModalOpen]); const handleClickDeploy = useCallback(() => { diff --git a/app/client/src/pages/Editor/gitSync/GitSyncModal.tsx b/app/client/src/pages/Editor/gitSync/GitSyncModal.tsx index 0a05ec16d1..51f91a9473 100644 --- a/app/client/src/pages/Editor/gitSync/GitSyncModal.tsx +++ b/app/client/src/pages/Editor/gitSync/GitSyncModal.tsx @@ -1,6 +1,9 @@ -import React, { useState } from "react"; +import React from "react"; import Dialog from "components/ads/DialogComponent"; -import { getIsGitSyncModalOpen } from "selectors/gitSyncSelectors"; +import { + getActiveGitSyncModalTab, + getIsGitSyncModalOpen, +} from "selectors/gitSyncSelectors"; import { useDispatch, useSelector } from "react-redux"; import { useCallback } from "react"; import { setIsGitSyncModalOpen } from "actions/gitSyncActions"; @@ -14,6 +17,8 @@ import Icon from "components/ads/Icon"; import { Colors } from "constants/Colors"; import { Classes } from "./constants"; +import GitErrorPopup from "./components/GitErrorPopup"; + const Container = styled.div` height: 600px; width: 100%; @@ -64,35 +69,44 @@ function GitSyncModal() { const dispatch = useDispatch(); const isModalOpen = useSelector(getIsGitSyncModalOpen); const handleClose = useCallback(() => { - dispatch(setIsGitSyncModalOpen(false)); + dispatch(setIsGitSyncModalOpen({ isOpen: false })); }, [dispatch, setIsGitSyncModalOpen]); - const [activeTabIndex, setActiveTabIndex] = useState(0); + const activeTabIndex = useSelector(getActiveGitSyncModalTab); + const setActiveTabIndex = (index: number) => + dispatch(setIsGitSyncModalOpen({ isOpen: true, tab: index })); + const BodyComponent = ComponentsByTab[MENU_ITEMS[activeTabIndex].key as MENU_ITEM]; return ( - - - - - - - - - - - - - + <> + + + + + + + + + + + + + + + > ); } diff --git a/app/client/src/pages/Editor/gitSync/QuickGitActions/index.tsx b/app/client/src/pages/Editor/gitSync/QuickGitActions/index.tsx index 305efaa7ba..5e7f3e9569 100644 --- a/app/client/src/pages/Editor/gitSync/QuickGitActions/index.tsx +++ b/app/client/src/pages/Editor/gitSync/QuickGitActions/index.tsx @@ -8,10 +8,28 @@ import { ReactComponent as DownArrow } from "assets/icons/ads/down-arrow.svg"; import { ReactComponent as Plus } from "assets/icons/ads/plus.svg"; import { ReactComponent as GitBranch } from "assets/icons/ads/git-branch.svg"; -import { COMMIT, PUSH, PULL, MERGE, createMessage } from "constants/messages"; +import { + COMMIT, + PUSH, + PULL, + MERGE, + CONNECT_GIT, + createMessage, +} from "constants/messages"; import { noop } from "lodash"; +import Tooltip from "components/ads/Tooltip"; +import { Colors } from "constants/Colors"; +import { getTypographyByKey } from "constants/DefaultTheme"; +import { getIsGitRepoSetup } from "selectors/gitSyncSelectors"; +import { useDispatch, useSelector } from "react-redux"; +import { ReactComponent as GitCommitLine } from "assets/icons/ads/git-commit-line.svg"; +import Button, { Category, Size } from "components/ads/Button"; +import { setIsGitSyncModalOpen } from "actions/gitSyncActions"; +import { GitSyncModalTab } from "entities/GitSync"; + type QuickActionButtonProps = { + count?: number; icon: React.ReactNode; onClick: () => void; tooltipText: string; @@ -26,17 +44,41 @@ const QuickActionButtonContainer = styled.div` background-color: ${(props) => props.theme.colors.editorBottomBar.buttonBackgroundHover}; } + position: relative; + overflow: visible; + z-index: 0; /* fix z-index on hover */ + .count { + position: absolute; + width: 18px; + height: 18px; + display: flex; + justify-content: center; + align-items: center; + color: ${Colors.WHITE}; + background-color: ${Colors.BLACK}; + top: -3px; + left: 15px; + border-radius: 50%; + ${(props) => getTypographyByKey(props, "p3")}; + z-index: 1; + } `; function QuickActionButton({ + count = 0, icon, onClick, -}: // tooltipText, -QuickActionButtonProps) { + tooltipText, +}: QuickActionButtonProps) { return ( - - {icon} - + + + {icon} + {count > 0 && ( + {count > 9 ? `${9}+` : count} + )} + + ); } @@ -79,19 +121,56 @@ const Container = styled.div` align-items: center; `; -export default function QuickGitActions() { - const quickActionButtons = getQuickActionButtons({ - commit: noop, - push: noop, - pull: noop, - merge: noop, - }); +function ConnectGitPlaceholder() { + const dispatch = useDispatch(); + return ( - - {quickActionButtons.map((button) => ( - - ))} + + { + dispatch(setIsGitSyncModalOpen({ isOpen: true })); + }} + size={Size.small} + text={createMessage(CONNECT_GIT)} + /> ); } + +export default function QuickGitActions() { + const isGitRepoSetup = useSelector(getIsGitRepoSetup); + const dispatch = useDispatch(); + + const quickActionButtons = getQuickActionButtons({ + commit: () => { + dispatch( + setIsGitSyncModalOpen({ + isOpen: true, + tab: GitSyncModalTab.GIT_CONNECTION, + }), + ); + }, + push: noop, + pull: noop, + merge: () => { + dispatch( + setIsGitSyncModalOpen({ + isOpen: true, + tab: GitSyncModalTab.MERGE, + }), + ); + }, + }); + return isGitRepoSetup ? ( + + + {quickActionButtons.map((button) => ( + + ))} + + ) : ( + + ); +} diff --git a/app/client/src/pages/Editor/gitSync/components/GitErrorPopup.tsx b/app/client/src/pages/Editor/gitSync/components/GitErrorPopup.tsx new file mode 100644 index 0000000000..16e3e3bc3f --- /dev/null +++ b/app/client/src/pages/Editor/gitSync/components/GitErrorPopup.tsx @@ -0,0 +1,124 @@ +import React from "react"; +import styled from "styled-components"; +import { Overlay, Classes } from "@blueprintjs/core"; +import { useDispatch, useSelector } from "react-redux"; +import { + setIsGitErrorPopupVisible, + showCreateBranchPopup, +} from "actions/gitSyncActions"; +import { + getGitError, + getIsGitErrorPopupVisible, +} from "selectors/gitSyncSelectors"; +import Icon from "components/ads/Icon"; + +import { + createMessage, + RETRY, + CREATE_NEW_BRANCH, + ERROR_WHILE_PULLING_CHANGES, +} from "constants/messages"; +import Button, { Category, Size } from "components/ads/Button"; +import { Space } from "./StyledComponents"; +import { debug } from "loglevel"; +import { Colors } from "constants/Colors"; +import { getTypographyByKey } from "constants/DefaultTheme"; + +const StyledGitErrorPopup = styled.div` + & { + .${Classes.OVERLAY} { + position: fixed; + bottom: 0; + left: 0; + right: 0; + display: flex; + justify-content: center; + .${Classes.OVERLAY_CONTENT} { + overflow: hidden; + bottom: ${(props) => + `calc(${props.theme.bottomBarHeight} + ${props.theme.spaces[3]}px)`}; + left: ${(props) => props.theme.spaces[3]}px; + background-color: ${Colors.WHITE}; + } + } + .git-error-popup { + width: 364px; + min-height: 164px; + padding: ${(props) => props.theme.spaces[7]}px; + + display: flex; + flex-direction: column; + } + } +`; + +const CloseBtnContainer = styled.div` + position: absolute; + right: ${(props) => props.theme.spaces[6]}px; + top: ${(props) => props.theme.spaces[6]}px; +`; + +const Title = styled.div` + ${(props) => getTypographyByKey(props, "btnMedium")}; + color: ${Colors.POMEGRANATE2}; +`; + +const Error = styled.div` + flex: 1; + overflow-wrap: anywhere; + word-break: break-word; + overflow-y: auto; +`; + +function GitErrorPopup() { + const dispatch = useDispatch(); + const isGitErrorPopupVisible = useSelector(getIsGitErrorPopupVisible); + const hidePopup = () => { + dispatch(setIsGitErrorPopupVisible({ isVisible: false })); + }; + const gitError = useSelector(getGitError); + + return ( + + + + + {createMessage(ERROR_WHILE_PULLING_CHANGES)} + + {gitError} + + { + debug("retry GIT operation"); + }} + size={Size.medium} + tag="button" + text={createMessage(RETRY)} + /> + + dispatch(showCreateBranchPopup())} + size={Size.medium} + tag="button" + text={createMessage(CREATE_NEW_BRANCH)} + /> + + + + + + + + + ); +} + +export default GitErrorPopup; diff --git a/app/client/src/reducers/uiReducers/gitSyncReducer.ts b/app/client/src/reducers/uiReducers/gitSyncReducer.ts index 0db8d68245..70f3b07fd5 100644 --- a/app/client/src/reducers/uiReducers/gitSyncReducer.ts +++ b/app/client/src/reducers/uiReducers/gitSyncReducer.ts @@ -4,20 +4,35 @@ import { ReduxActionErrorTypes, ReduxActionTypes, } from "constants/ReduxActionConstants"; +import { GitSyncModalTab } from "entities/GitSync"; const initialState: GitSyncReducerState = { isGitSyncModalOpen: false, isCommitting: false, + activeGitSyncModalTab: GitSyncModalTab.GIT_CONNECTION, + isErrorPopupVisible: false, + gitError: ` + README.md app/client/cypress/support/commands.js + app/client/src/comments/CommentsShowcaseCarousel/CommentsCarouselModal.tsx + `, }; const gitSyncReducer = createReducer(initialState, { [ReduxActionTypes.SET_IS_GIT_SYNC_MODAL_OPEN]: ( state: GitSyncReducerState, - action: ReduxAction, - ) => ({ - ...state, - isGitSyncModalOpen: action.payload, - }), + action: ReduxAction<{ + isOpen: boolean; + tab: GitSyncModalTab; + }>, + ) => { + const activeGitSyncModalTab = + action.payload.tab || state.activeGitSyncModalTab; + return { + ...state, + isGitSyncModalOpen: action.payload.isOpen, + activeGitSyncModalTab, + }; + }, [ReduxActionTypes.COMMIT_TO_GIT_REPO_INIT]: (state: GitSyncReducerState) => ({ ...state, isCommitting: true, @@ -34,11 +49,21 @@ const gitSyncReducer = createReducer(initialState, { ...state, isCommitting: false, }), + [ReduxActionTypes.SHOW_ERROR_POPUP]: ( + state: GitSyncReducerState, + action: ReduxAction<{ isVisible: boolean }>, + ) => ({ + ...state, + isErrorPopupVisible: action.payload.isVisible, + }), }); export type GitSyncReducerState = { isGitSyncModalOpen: boolean; isCommitting: boolean; + activeGitSyncModalTab: GitSyncModalTab; + isErrorPopupVisible: boolean; + gitError: string; }; export default gitSyncReducer; diff --git a/app/client/src/selectors/debuggerSelectors.tsx b/app/client/src/selectors/debuggerSelectors.tsx index 4e115f7036..3783894de1 100644 --- a/app/client/src/selectors/debuggerSelectors.tsx +++ b/app/client/src/selectors/debuggerSelectors.tsx @@ -11,3 +11,12 @@ export const getFilteredErrors = createSelector( return errors; }, ); + +export const getMessageCount = createSelector(getFilteredErrors, (errors) => { + const errorKeys = Object.keys(errors); + const warningsCount = errorKeys.filter((key: string) => + key.includes("warning"), + ).length; + const errorsCount = errorKeys.length - warningsCount; + return { errors: errorsCount, warnings: warningsCount }; +}); diff --git a/app/client/src/selectors/gitSyncSelectors.tsx b/app/client/src/selectors/gitSyncSelectors.tsx index 21deee5bc2..b2561d6cf3 100644 --- a/app/client/src/selectors/gitSyncSelectors.tsx +++ b/app/client/src/selectors/gitSyncSelectors.tsx @@ -3,9 +3,17 @@ import { AppState } from "reducers"; export const getIsGitSyncModalOpen = (state: AppState) => state.ui.gitSync.isGitSyncModalOpen; -export const getIsGitRepoSetup = () => false; +export const getIsGitRepoSetup = () => true; export const getCurrentGitBranch = () => "master"; export const getIsCommittingInProgress = (state: AppState) => state.ui.gitSync.isCommitting; + +export const getActiveGitSyncModalTab = (state: AppState) => + state.ui.gitSync.activeGitSyncModalTab; + +export const getIsGitErrorPopupVisible = (state: AppState) => + state.ui.gitSync.isErrorPopupVisible; + +export const getGitError = (state: AppState) => state.ui.gitSync.gitError;