diff --git a/services/web/frontend/extracted-translations.json b/services/web/frontend/extracted-translations.json index e72e1ea9d2..958bd4e855 100644 --- a/services/web/frontend/extracted-translations.json +++ b/services/web/frontend/extracted-translations.json @@ -37,6 +37,7 @@ "accept_change_error_title": "", "accept_invitation": "", "accept_or_reject_each_changes_individually": "", + "accept_selected_changes": "", "accept_terms_and_conditions": "", "accepted_invite": "", "accepting_invite_as": "", @@ -258,11 +259,15 @@ "compromised_password": "", "configure_sso": "", "confirm": "", + "confirm_accept_selected_changes": "", + "confirm_accept_selected_changes_plural": "", "confirm_affiliation": "", "confirm_affiliation_to_relink_dropbox": "", "confirm_delete_user_type_email_address": "", "confirm_new_password": "", "confirm_primary_email_change": "", + "confirm_reject_selected_changes": "", + "confirm_reject_selected_changes_plural": "", "confirm_remove_sso_config_enter_email": "", "confirm_your_email": "", "confirming": "", @@ -1203,6 +1208,7 @@ "reject": "", "reject_all": "", "reject_change": "", + "reject_selected_changes": "", "relink_your_account": "", "reload_editor": "", "remind_before_trial_ends": "", diff --git a/services/web/frontend/js/features/ide-react/components/modals/generic-confirm-modal.tsx b/services/web/frontend/js/features/ide-react/components/modals/generic-confirm-modal.tsx new file mode 100644 index 0000000000..de35bee52f --- /dev/null +++ b/services/web/frontend/js/features/ide-react/components/modals/generic-confirm-modal.tsx @@ -0,0 +1,53 @@ +import { useTranslation } from 'react-i18next' +import { memo } from 'react' +import OLModal, { + OLModalBody, + OLModalFooter, + OLModalHeader, + OLModalTitle, +} from '@/features/ui/components/ol/ol-modal' +import OLButton from '@/features/ui/components/ol/ol-button' +import { ButtonProps } from '@/features/ui/components/types/button-props' + +export type GenericConfirmModalOwnProps = { + title: string + message: string + onConfirm: () => void + confirmLabel?: string + primaryVariant?: ButtonProps['variant'] +} + +type GenericConfirmModalProps = React.ComponentProps & + GenericConfirmModalOwnProps + +function GenericConfirmModal({ + title, + message, + confirmLabel, + primaryVariant = 'primary', + ...modalProps +}: GenericConfirmModalProps) { + const { t } = useTranslation() + const handleConfirmClick = modalProps.onConfirm + + return ( + + + {title} + + + {message} + + + modalProps.onHide()}> + {t('cancel')} + + + {confirmLabel || t('ok')} + + + + ) +} + +export default memo(GenericConfirmModal) diff --git a/services/web/frontend/js/features/ide-react/context/modals-context.tsx b/services/web/frontend/js/features/ide-react/context/modals-context.tsx index b1291c4ed9..097ce37120 100644 --- a/services/web/frontend/js/features/ide-react/context/modals-context.tsx +++ b/services/web/frontend/js/features/ide-react/context/modals-context.tsx @@ -12,9 +12,13 @@ import GenericMessageModal, { import OutOfSyncModal, { OutOfSyncModalProps, } from '@/features/ide-react/components/modals/out-of-sync-modal' +import GenericConfirmModal, { + GenericConfirmModalOwnProps, +} from '../components/modals/generic-confirm-modal' type ModalsContextValue = { genericModalVisible: boolean + showGenericConfirmModal: (data: GenericConfirmModalOwnProps) => void showGenericMessageModal: ( title: GenericMessageModalOwnProps['title'], message: GenericMessageModalOwnProps['message'] @@ -28,8 +32,15 @@ const ModalsContext = createContext(undefined) export const ModalsContextProvider: FC = ({ children }) => { const [showGenericModal, setShowGenericModal] = useState(false) + const [showConfirmModal, setShowConfirmModal] = useState(false) const [genericMessageModalData, setGenericMessageModalData] = useState({ title: '', message: '' }) + const [genericConfirmModalData, setGenericConfirmModalData] = + useState({ + title: '', + message: '', + onConfirm: () => {}, + }) const [shouldShowOutOfSyncModal, setShouldShowOutOfSyncModal] = useState(false) @@ -41,6 +52,15 @@ export const ModalsContextProvider: FC = ({ children }) => { setShowGenericModal(false) }, []) + const handleHideGenericConfirmModal = useCallback(() => { + setShowConfirmModal(false) + }, []) + + const handleConfirmGenericConfirmModal = useCallback(() => { + genericConfirmModalData.onConfirm() + setShowConfirmModal(false) + }, [genericConfirmModalData]) + const showGenericMessageModal = useCallback( ( title: GenericMessageModalOwnProps['title'], @@ -52,6 +72,14 @@ export const ModalsContextProvider: FC = ({ children }) => { [] ) + const showGenericConfirmModal = useCallback( + (data: GenericConfirmModalOwnProps) => { + setGenericConfirmModalData(data) + setShowConfirmModal(true) + }, + [] + ) + const handleHideOutOfSyncModal = useCallback(() => { setShouldShowOutOfSyncModal(false) }, []) @@ -64,10 +92,16 @@ export const ModalsContextProvider: FC = ({ children }) => { const value = useMemo( () => ({ showGenericMessageModal, + showGenericConfirmModal, genericModalVisible: showGenericModal, showOutOfSyncModal, }), - [showGenericMessageModal, showGenericModal, showOutOfSyncModal] + [ + showGenericMessageModal, + showGenericConfirmModal, + showGenericModal, + showOutOfSyncModal, + ] ) return ( @@ -78,6 +112,12 @@ export const ModalsContextProvider: FC = ({ children }) => { onHide={handleHideGenericModal} {...genericMessageModalData} /> + { const state = useCodeMirrorStateContext() @@ -72,6 +74,7 @@ const ReviewTooltipMenuContent: FC<{ const { setView } = useReviewPanelViewActionsContext() const ranges = useRangesContext() const { acceptChanges, rejectChanges } = useRangesActionsContext() + const { showGenericConfirmModal } = useModalsContext() const addComment = useCallback(() => { setReviewPanelOpen(true) @@ -109,12 +112,42 @@ const ReviewTooltipMenuContent: FC<{ }, [ranges, state.selection.main]) const acceptChangesHandler = useCallback(() => { - acceptChanges(...changeIdsInSelection) - }, [acceptChanges, changeIdsInSelection]) + const nChanges = numberOfChangesInSelection(ranges, state.selection.main) + showGenericConfirmModal({ + message: t('confirm_accept_selected_changes', { count: nChanges }), + title: t('accept_selected_changes'), + onConfirm: () => { + acceptChanges(...changeIdsInSelection) + }, + primaryVariant: 'danger', + }) + }, [ + acceptChanges, + changeIdsInSelection, + ranges, + showGenericConfirmModal, + state.selection.main, + t, + ]) const rejectChangesHandler = useCallback(() => { - rejectChanges(...changeIdsInSelection) - }, [rejectChanges, changeIdsInSelection]) + const nChanges = numberOfChangesInSelection(ranges, state.selection.main) + showGenericConfirmModal({ + message: t('confirm_reject_selected_changes', { count: nChanges }), + title: t('reject_selected_changes'), + onConfirm: () => { + rejectChanges(...changeIdsInSelection) + }, + primaryVariant: 'danger', + }) + }, [ + showGenericConfirmModal, + t, + ranges, + state.selection.main, + rejectChanges, + changeIdsInSelection, + ]) const showChangesButtons = changeIdsInSelection.length > 0 @@ -130,7 +163,10 @@ const ReviewTooltipMenuContent: FC<{ {showChangesButtons && ( <>
- +