diff --git a/src/AgreementHtml.tsx b/src/AgreementHtml.tsx index f52a5e80..4e9d2c05 100644 --- a/src/AgreementHtml.tsx +++ b/src/AgreementHtml.tsx @@ -1,15 +1,10 @@ + import { LoadingOutlined } from "@ant-design/icons"; import { Spin } from "antd"; import useAppStore from "./store/store"; import FullScreenModal from "./components/FullScreenModal"; -function AgreementHtml({ - loading, - isModal, -}: { - loading: boolean; - isModal?: boolean; -}) { +function AgreementHtml({ loading, isModal }: { loading: any; isModal?: boolean }) { const agreementHtml = useAppStore((state) => state.agreementHtml); const backgroundColor = useAppStore((state) => state.backgroundColor); const textColor = useAppStore((state) => state.textColor); @@ -35,49 +30,23 @@ function AgreementHtml({ color: textColor, }} > -

+

Preview Output

{!isModal && }

- The result of merging the JSON data with the template. This is - AgreementMark converted to HTML. + The result of merging the JSON data with the template.

{loading ? ( -
- - } - /> +
+ } />
) : (
)}
diff --git a/src/components/useUndoRedo.ts b/src/components/useUndoRedo.ts new file mode 100644 index 00000000..e8a77f14 --- /dev/null +++ b/src/components/useUndoRedo.ts @@ -0,0 +1,35 @@ +import { useState } from 'react'; +function useUndoRedo(initialValue: T, onChange?: (value: T) => void) { + const [past, setPast] = useState([]); + const [present, setPresent] = useState(initialValue); + const [future, setFuture] = useState([]); + + const setValue = (newValue: T) => { + setPast((prevPast) => [...prevPast, present]); + setPresent(newValue); + setFuture([]); + if (onChange) onChange(newValue); // Ensure preview updates + }; + + const undo = () => { + if (past.length === 0) return; + const previous = past[past.length - 1]; + setPast((prevPast) => prevPast.slice(0, -1)); + setFuture((prevFuture) => [present, ...prevFuture]); + setPresent(previous); + if (onChange) onChange(previous); + }; + + const redo = () => { + if (future.length === 0) return; + const next = future[0]; + setFuture((prevFuture) => prevFuture.slice(1)); + setPast((prevPast) => [...prevPast, present]); + setPresent(next); + if (onChange) onChange(next); + }; + + return { value: present, setValue, undo, redo }; +} + +export default useUndoRedo; diff --git a/src/editors/editorsContainer/AgreementData.tsx b/src/editors/editorsContainer/AgreementData.tsx index b278ff2a..27af6ef5 100644 --- a/src/editors/editorsContainer/AgreementData.tsx +++ b/src/editors/editorsContainer/AgreementData.tsx @@ -1,15 +1,17 @@ import JSONEditor from "../JSONEditor"; import useAppStore from "../../store/store"; +import useUndoRedo from "../../components/useUndoRedo"; import { useCallback } from "react"; import { debounce } from "ts-debounce"; +import { FaUndo, FaRedo } from "react-icons/fa"; function AgreementData() { - const editorAgreementData = useAppStore((state) => state.editorAgreementData); - const setEditorAgreementData = useAppStore( - (state) => state.setEditorAgreementData - ); - const setData = useAppStore((state) => state.setData); const textColor = useAppStore((state) => state.textColor); + const setData = useAppStore((state) => state.setData); + const { value, setValue, undo, redo } = useUndoRedo( + useAppStore((state) => state.editorAgreementData), + setData // Pass setData to update the preview when undo/redo happens + ); const debouncedSetData = useCallback( debounce((value: string) => { @@ -20,23 +22,26 @@ function AgreementData() { const handleChange = (value: string | undefined) => { if (value !== undefined) { - setEditorAgreementData(value); // Immediate state update - debouncedSetData(value); // Debounced state update + setValue(value); + debouncedSetData(value); } }; return ( -
-
+
+

Data

- - JSON data (an instance of the Concerto model) used to preview output - from the template. - +
+ + +
- +

+ JSON data (an instance of the Concerto model) used to preview output from the template. +

+
); } -export default AgreementData; \ No newline at end of file +export defaultĀ AgreementData; \ No newline at end of file diff --git a/src/editors/editorsContainer/TemplateMarkdown.tsx b/src/editors/editorsContainer/TemplateMarkdown.tsx index 830bfdf6..230cfdf1 100644 --- a/src/editors/editorsContainer/TemplateMarkdown.tsx +++ b/src/editors/editorsContainer/TemplateMarkdown.tsx @@ -1,39 +1,48 @@ import MarkdownEditor from "../MarkdownEditor"; import useAppStore from "../../store/store"; +import useUndoRedo from "../../components/useUndoRedo"; import { useCallback } from "react"; import { debounce } from "ts-debounce"; +import { FaUndo, FaRedo } from "react-icons/fa"; function TemplateMarkdown() { - const editorValue = useAppStore((state) => state.editorValue); - const setEditorValue = useAppStore((state) => state.setEditorValue); - const setTemplateMarkdown = useAppStore((state) => state.setTemplateMarkdown); - const backgroundColor = useAppStore((state) => state.backgroundColor); const textColor = useAppStore((state) => state.textColor); - + const backgroundColor = useAppStore((state) => state.backgroundColor); + const setTemplateMarkdown = useAppStore((state) => state.setTemplateMarkdown); + const { value, setValue, undo, redo } = useUndoRedo( + useAppStore((state) => state.editorValue), + setTemplateMarkdown // Ensures preview updates when undo/redo happens + ); + const debouncedSetTemplateMarkdown = useCallback( debounce((value: string) => { void setTemplateMarkdown(value); }, 500), - [] + [setTemplateMarkdown] ); const handleChange = (value: string | undefined) => { if (value !== undefined) { - setEditorValue(value); + setValue(value); debouncedSetTemplateMarkdown(value); } }; return ( -
-

TemplateMark

+
+
+

TemplateMark

+
+ + +
+

- A natural language template with embedded variables, conditional - sections, and TypeScript code. + A natural language template with embedded variables, conditional sections, and TypeScript code.

- +
); } -export default TemplateMarkdown; +export default TemplateMarkdown; \ No newline at end of file diff --git a/src/editors/editorsContainer/TemplateModel.tsx b/src/editors/editorsContainer/TemplateModel.tsx index f4a83dff..8c1808c8 100644 --- a/src/editors/editorsContainer/TemplateModel.tsx +++ b/src/editors/editorsContainer/TemplateModel.tsx @@ -1,14 +1,18 @@ import ConcertoEditor from "../ConcertoEditor"; import useAppStore from "../../store/store"; +import useUndoRedo from "../../components/useUndoRedo"; import { useCallback } from "react"; import { debounce } from "ts-debounce"; +import { FaUndo, FaRedo } from "react-icons/fa"; function TemplateModel() { - const editorModelCto = useAppStore((state) => state.editorModelCto); - const setEditorModelCto = useAppStore((state) => state.setEditorModelCto); - const setModelCto = useAppStore((state) => state.setModelCto); const textColor = useAppStore((state) => state.textColor); - + const setModelCto = useAppStore((state) => state.setModelCto); + const { value, setValue, undo, redo } = useUndoRedo( + useAppStore((state) => state.editorModelCto), + setModelCto // Ensures errors and preview update when undo/redo happens + ); + const debouncedSetModelCto = useCallback( debounce((value: string) => { void setModelCto(value); @@ -18,22 +22,26 @@ function TemplateModel() { const handleChange = (value: string | undefined) => { if (value !== undefined) { - setEditorModelCto(value); + setValue(value); debouncedSetModelCto(value); } }; return (
-
+

Concerto Model

- - Defines the data model for the template and its logic. - +
+ + +
- + + Defines the data model for the template and its logic. + +
); } -export default TemplateModel; \ No newline at end of file +export defaultĀ TemplateModel; \ No newline at end of file