Skip to content
45 changes: 7 additions & 38 deletions src/AgreementHtml.tsx
Original file line number Diff line number Diff line change
@@ -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);
Expand All @@ -35,49 +30,23 @@ function AgreementHtml({
color: textColor,
}}
>
<h2
style={{
flexGrow: 1,
textAlign: "center",
paddingLeft: "34px",
color: textColor,
}}
>
<h2 style={{ flexGrow: 1, textAlign: "center", paddingLeft: "34px", color: textColor }}>
Preview Output
</h2>
{!isModal && <FullScreenModal />}
</div>
<p style={{ textAlign: "center", color: textColor }}>
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.
</p>
{loading ? (
<div
style={{
flex: 1,
display: "flex",
justifyContent: "center",
alignItems: "center",
}}
>
<Spin
indicator={
<LoadingOutlined
style={{ fontSize: 42, color: "#19c6c7" }}
spin
/>
}
/>
<div style={{ flex: 1, display: "flex", justifyContent: "center", alignItems: "center" }}>
<Spin indicator={<LoadingOutlined style={{ fontSize: 42, color: "#19c6c7" }} spin />} />
</div>
) : (
<div
className="agreement"
dangerouslySetInnerHTML={{ __html: agreementHtml }}
style={{
flex: 1,
color: textColor,
backgroundColor: backgroundColor,
}}
style={{ flex: 1, color: textColor, backgroundColor: backgroundColor }}
/>
)}
</div>
Expand Down
35 changes: 35 additions & 0 deletions src/components/useUndoRedo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { useState } from 'react';
function useUndoRedo<T>(initialValue: T, onChange?: (value: T) => void) {
const [past, setPast] = useState<T[]>([]);
const [present, setPresent] = useState<T>(initialValue);
const [future, setFuture] = useState<T[]>([]);

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;
35 changes: 20 additions & 15 deletions src/editors/editorsContainer/AgreementData.tsx
Original file line number Diff line number Diff line change
@@ -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) => {
Expand All @@ -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 (
<div className="column">
<div className="tooltip">
<div className="column" >
<div style={{ display: "flex", alignItems: "center", justifyContent: "space-between" }}>
<h3 style={{ color: textColor }}>Data</h3>
<span style={{ color: textColor }} className="tooltiptext">
JSON data (an instance of the Concerto model) used to preview output
from the template.
</span>
<div>
<FaUndo onClick={undo} title="Undo" style={{ cursor: "pointer", color: textColor, marginRight: "8px" }} />
<FaRedo onClick={redo} title="Redo" style={{ cursor: "pointer", color: textColor }} />
</div>
</div>
<JSONEditor value={editorAgreementData} onChange={handleChange} />
<p style={{ color: textColor }}>
JSON data (an instance of the Concerto model) used to preview output from the template.
</p>
<JSONEditor value={value} onChange={handleChange} />
</div>
);
}

export default AgreementData;
export default AgreementData;
35 changes: 22 additions & 13 deletions src/editors/editorsContainer/TemplateMarkdown.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<div className="column" style={{ backgroundColor: backgroundColor }}>
<h2 style={{ color: textColor }}>TemplateMark</h2>
<div className="column" style={{ backgroundColor }}>
<div style={{ display: "flex", alignItems: "center", justifyContent: "space-between" }}>
<h3 style={{ color: textColor }}>TemplateMark</h3>
<div>
<FaUndo onClick={undo} title="Undo" style={{ cursor: "pointer", color: textColor, marginRight: "8px" }} />
<FaRedo onClick={redo} title="Redo" style={{ cursor: "pointer", color: textColor }} />
</div>
</div>
<p style={{ color: textColor }}>
A natural language template with embedded variables, conditional
sections, and TypeScript code.
A natural language template with embedded variables, conditional sections, and TypeScript code.
</p>
<MarkdownEditor value={editorValue} onChange={handleChange} />
<MarkdownEditor value={value} onChange={handleChange} />
</div>
);
}

export default TemplateMarkdown;
export default TemplateMarkdown;
30 changes: 19 additions & 11 deletions src/editors/editorsContainer/TemplateModel.tsx
Original file line number Diff line number Diff line change
@@ -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);
Expand All @@ -18,22 +22,26 @@ function TemplateModel() {

const handleChange = (value: string | undefined) => {
if (value !== undefined) {
setEditorModelCto(value);
setValue(value);
debouncedSetModelCto(value);
}
};

return (
<div className="column">
<div className="tooltip">
<div className="tooltip" style={{ display: "flex", alignItems: "center", justifyContent: "space-between" }}>
<h3 style={{ color: textColor }}>Concerto Model</h3>
<span style={{ color: textColor }} className="tooltiptext">
Defines the data model for the template and its logic.
</span>
<div>
<FaUndo onClick={undo} title="Undo" style={{ cursor: "pointer", color: textColor, marginRight: "8px" }} />
<FaRedo onClick={redo} title="Redo" style={{ cursor: "pointer", color: textColor }} />
</div>
</div>
<ConcertoEditor value={editorModelCto} onChange={handleChange} />
<span style={{ color: textColor }} className="tooltiptext">
Defines the data model for the template and its logic.
</span>
<ConcertoEditor value={value} onChange={handleChange} />
</div>
);
}

export default TemplateModel;
export default TemplateModel;