👔(app-impress) save the templates editor automatically

On almost each change in the code editor,
the template is saved automatically.
It will restore as well the editor when we arrive
on the template editor page.
This commit is contained in:
Anthony LC
2024-04-16 16:55:59 +02:00
committed by Anthony LC
parent 3aaa3e179d
commit e13ecdd38d
5 changed files with 129 additions and 3 deletions

View File

@@ -26,4 +26,37 @@ test.describe('Template Editor', () => {
page.locator('.gjs-editor .gjs-block[title="Text"]'),
).toBeVisible();
});
test('checks the template editor save on changed', async ({
page,
browserName,
}) => {
const randomTemplate = await createTemplate(
page,
'template-editor',
browserName,
1,
);
await expect(page.locator('h2').getByText(randomTemplate[0])).toBeVisible();
const iframe = page.frameLocator('iFrame.gjs-frame');
await page.getByTitle('Open Blocks').click();
await page
.locator('.gjs-editor .gjs-block[title="Text"]')
.dragTo(iframe.locator('body.gjs-dashed'));
await iframe.getByText('Insert your text here').fill('Hello World');
await iframe.locator('body.gjs-dashed').click();
// Come on the page again to check the changes are saved
await page.locator('menu').first().getByLabel(`Template button`).click();
const panel = page.getByLabel('Templates panel').first();
await panel.locator('li').getByText(randomTemplate[0]).click();
await expect(iframe.getByText('Hello World')).toBeVisible({
timeout: 5000,
});
});
});

View File

@@ -0,0 +1,39 @@
import { useMutation } from '@tanstack/react-query';
import { APIError, errorCauses, fetchAPI } from '@/api';
import { Template } from '../types';
type UpdateTemplateProps = Pick<Template, 'code_editor' | 'id'>;
export const updateTemplateCodeEditor = async ({
code_editor,
id,
}: UpdateTemplateProps): Promise<Template> => {
const response = await fetchAPI(`templates/${id}/`, {
method: 'PATCH',
body: JSON.stringify({
code_editor,
}),
});
if (!response.ok) {
throw new APIError(
'Failed to update the template',
await errorCauses(response),
);
}
return response.json() as Promise<Template>;
};
export function useUpdateTemplateCodeEditor(
onSuccess?: (data: Template) => void,
) {
return useMutation<Template, APIError, UpdateTemplateProps>({
mutationFn: updateTemplateCodeEditor,
onSuccess: (data) => {
onSuccess?.(data);
},
});
}

View File

@@ -2,6 +2,7 @@ import GjsEditor from '@grapesjs/react';
import grapesjs, { Editor, ProjectData } from 'grapesjs';
import 'grapesjs/dist/css/grapes.min.css';
import pluginBlocksBasic from 'grapesjs-blocks-basic';
import { useEffect, useState } from 'react';
import { Card, Text } from '@/components';
@@ -13,7 +14,44 @@ interface TemplateEditorProps {
}
export const TemplateEditor = ({ template }: TemplateEditorProps) => {
const onEditor = (editor: Editor) => {};
const { mutate: updateCodeEditor } = useUpdateTemplateCodeEditor();
const [editor, setEditor] = useState<Editor>();
useEffect(() => {
if (!editor?.loadProjectData) {
return;
}
editor.loadProjectData(template.code_editor);
}, [template.code_editor, editor]);
useEffect(() => {
editor?.Storage.add('remote', {
load() {
return Promise.resolve(template.code_editor);
},
store(data: ProjectData) {
updateCodeEditor({
code_editor: data,
id: template.id,
});
return Promise.resolve();
},
});
}, [editor, template.code_editor, template.id, updateCodeEditor]);
const onEditor = (editor: Editor) => {
setEditor(editor);
editor?.Storage.add('remote', {
load() {
return Promise.resolve(template.code_editor);
},
store() {
return Promise.resolve();
},
});
};
return (
<>
@@ -24,7 +62,9 @@ export const TemplateEditor = ({ template }: TemplateEditorProps) => {
<GjsEditor
grapesjs={grapesjs}
options={{
storageManager: false,
storageManager: {
type: 'remote',
},
}}
plugins={[(editor) => pluginBlocksBasic(editor, {})]}
onEditor={onEditor}

View File

@@ -1,3 +1,5 @@
import { ProjectData } from 'grapesjs';
export enum Role {
MEMBER = 'member',
ADMIN = 'administrator',
@@ -27,5 +29,6 @@ export interface Template {
update: boolean;
};
accesses: Access[];
code_editor: ProjectData;
title: string;
}

View File

@@ -26,7 +26,18 @@ interface TemplateProps {
}
const Template = ({ id }: TemplateProps) => {
const { data: template, isLoading, isError, error } = useTemplate({ id });
const {
data: template,
isLoading,
isError,
error,
} = useTemplate(
{ id },
{
queryKey: ['template', { id }],
staleTime: 0,
},
);
const navigate = useNavigate();
if (isError && error) {