From 823e6da18790f41addd51549eeeae6d461347317 Mon Sep 17 00:00:00 2001 From: Purvesh Date: Sat, 5 Oct 2024 10:33:01 +1300 Subject: [PATCH] page save improvements --- react-admin/.env.example | 2 +- react-admin/src/pages/page/PageCreate.tsx | 329 +---------------- react-admin/src/pages/page/PageEdit.tsx | 126 +------ react-admin/src/pages/page/PageFieldModal.tsx | 341 ++++++++++++++++++ .../src/types/page/CreatablePageType.ts | 8 +- .../src/types/page/EditablePageType.ts | 20 - 6 files changed, 380 insertions(+), 446 deletions(-) create mode 100644 react-admin/src/pages/page/PageFieldModal.tsx delete mode 100644 react-admin/src/types/page/EditablePageType.ts diff --git a/react-admin/.env.example b/react-admin/.env.example index c7e4e9ff..469a34ab 100644 --- a/react-admin/.env.example +++ b/react-admin/.env.example @@ -1,2 +1,2 @@ VITE_AVORED_FRONTEND_BASE_URL=http://localhost:3000 -VITE_AVORED_BACKEND_BASE_URL=http://localhost:8080 \ No newline at end of file +VITE_AVORED_BACKEND_BASE_URL=http://localhost:8081 diff --git a/react-admin/src/pages/page/PageCreate.tsx b/react-admin/src/pages/page/PageCreate.tsx index c09322da..2722a6e6 100644 --- a/react-admin/src/pages/page/PageCreate.tsx +++ b/react-admin/src/pages/page/PageCreate.tsx @@ -2,7 +2,6 @@ import React, { useState } from "react"; import { Link } from "react-router-dom"; import { Cog8ToothIcon, - MinusIcon, PlusIcon, TrashIcon, } from "@heroicons/react/24/solid"; @@ -16,19 +15,16 @@ import ErrorMessage from "../../components/ErrorMessage"; import "easymde/dist/easymde.min.css"; import slug from "slug"; import { - AvoRedPageFieldData, - AvoRedPageFieldSelectFieldDataOptions, - CreatableFieldType, - CreatablePageType, + SaveFieldType, + SavePageType, } from "../../types/page/CreatablePageType"; import { AvoRedPageDataType, AvoRedPageFieldType, } from "../../types/page/IPageModel"; -import AvoredModal from "../../components/AvoredModal"; import _ from "lodash"; -import AvoRedButton from "../../components/AvoRedButton"; import SimpleMdeReact from "react-simplemde-editor"; +import { PageFieldModal } from "./PageFieldModal"; function PageCreate() { const [t] = useTranslation("global"); @@ -43,7 +39,7 @@ function PageCreate() { setValue, getValues, trigger, - } = useForm({ + } = useForm({ resolver: joiResolver(usePageCreateSchema(), { allowUnknown: true }), }); @@ -70,6 +66,8 @@ function PageCreate() { }); await trigger("page_fields"); setCurrentIndex(max_index); + + // @todo fix this one setIsOpen(true); }; @@ -79,7 +77,7 @@ function PageCreate() { setCurrentIndex(0); }; - const submitHandler = async (data: CreatablePageType) => { + const submitHandler = async (data: SavePageType) => { // console.log(data) mutate(data); }; @@ -88,204 +86,12 @@ function PageCreate() { setValue("identifier", slug(e.currentTarget.value || "")); }; - const onPageFieldChange = async ( - index: number, - field_type: AvoRedPageFieldType, - data_type: AvoRedPageDataType, - ) => { - setValue(`page_fields.${index}.field_type`, field_type); - setValue(`page_fields.${index}.data_type`, data_type); - - if (field_type === AvoRedPageFieldType.SELECT) { - setValue(`page_fields.${index}.field_data.select_field_options`, []); - } - - switch (field_type) { - case AvoRedPageFieldType.SELECT: - const empty_option: AvoRedPageFieldSelectFieldDataOptions = { - label: "", - value: "", - }; - - const options: AvoRedPageFieldData = { - select_field_options: [], - }; - if (typeof options.select_field_options == "undefined") { - options.select_field_options = []; - } - options.select_field_options.push(empty_option); - setValue(`page_fields.${index}.field_data`, options); - - break; - default: - break; - } - - await trigger(`page_fields.${index}`); - }; - - const optionAddOnClick = async ( - e: React.MouseEvent, - field_index: number, - ) => { - e.preventDefault(); - const page_field: CreatableFieldType = getValues( - `page_fields.${field_index}`, - ); - const empty_option: AvoRedPageFieldSelectFieldDataOptions = { - label: "", - value: "", - }; - - page_field.field_data?.select_field_options.push(empty_option); - - await trigger("page_fields"); - }; - - const optionRemoveOnClick = async ( - e: React.MouseEvent, - field_index: number, - option_index: number, - ) => { - e.preventDefault(); - const page_field: CreatableFieldType = getValues( - `page_fields.${field_index}`, - ); - page_field.field_data?.select_field_options.splice(option_index, 1); - - await trigger(`page_fields.${field_index}`); - }; - - const optionLabelOnChange = async ( - e: any, - field_index: number, - option_index: number, - ) => { - setValue( - `page_fields.${field_index}.field_data.select_field_options.${option_index}.label`, - e.target.value, - ); - await trigger("page_fields"); - }; - - const optionValueOnChange = async ( - e: any, - field_index: number, - option_index: number, - ) => { - setValue( - `page_fields.${field_index}.field_data.select_field_options.${option_index}.value`, - e.target.value, - ); - await trigger("page_fields"); - }; - const textEditorOnChange = (value: string, field_index: number) => { setValue(`page_fields.${field_index}.field_content`, value); }; - const renderFieldData = (current_index: number) => { - const page_field: CreatableFieldType = getValues( - `page_fields.${current_index}`, - ); - switch (page_field.field_type) { - case AvoRedPageFieldType.SELECT: - return ( - <> - {page_field.field_data?.select_field_options.map( - (option, option_index) => { - return ( -
-
-
-
- - optionLabelOnChange( - e, - current_index, - option_index, - ) - } - placeholder={t("label")} - className="appearance-none rounded-md ring-1 ring-gray-400 - relative border-0 block w-full px-3 py-2 placeholder-gray-500 text-gray-900 - active::ring-primary-500 - focus:ring-primary-500 focus:outline-none focus:z-10 - disabled:bg-gray-200 disabled:opacity-70 - sm:text-sm " - /> -
-
-
-
-
- - optionValueOnChange( - e, - current_index, - option_index, - ) - } - placeholder={t("value")} - className="appearance-none rounded-md ring-1 ring-gray-400 - relative border-0 block w-full px-3 py-2 placeholder-gray-500 text-gray-900 - active::ring-primary-500 - focus:ring-primary-500 focus:outline-none focus:z-10 - disabled:bg-gray-200 disabled:opacity-70 - sm:text-sm " - /> -
-
- {getValues( - `page_fields.${current_index}.field_data.select_field_options`, - ).length === - option_index + 1 ? ( - <> - - - ) : ( - <> - - - )} -
-
-
-
-
- ); - }, - )} - - ); - default: - return <>; - } - }; - const renderField = (field: CreatableFieldType, index: number) => { + const renderField = (field: SaveFieldType, index: number) => { switch (field.field_type) { case AvoRedPageFieldType.TEXTAREA: return ( @@ -374,116 +180,15 @@ function PageCreate() {
{_.size(fields) > 0 ? ( - setIsOpen(false)} - modal_header={`Page Field`} - modal_body={ -
-
-
-
- -
-
- -
- -
- {renderFieldData(currentIndex)} -
-
-
-
-
- onPageFieldChange( - currentIndex, - AvoRedPageFieldType.TEXT, - AvoRedPageDataType.TEXT, - ) - } - className={`${getValues(`page_fields.${currentIndex}.field_type`) === AvoRedPageFieldType.TEXT ? "bg-primary-200" : "bg-gray-300"} - ring-1 ring-gray-300 hover:cursor-pointer hover:ring-primary-300 p-3 rounded`} - > - {t("text_field")} -
-
- onPageFieldChange( - currentIndex, - AvoRedPageFieldType.TEXTAREA, - AvoRedPageDataType.TEXT, - ) - } - className={`${getValues(`page_fields.${currentIndex}.field_type`) === AvoRedPageFieldType.TEXTAREA ? "bg-primary-200" : "bg-gray-300"} - ring-1 mt-2 ring-gray-300 hover:cursor-pointer hover:ring-primary-300 p-3 rounded`} - > - {t("textarea_field")} -
-
- onPageFieldChange( - currentIndex, - AvoRedPageFieldType.SELECT, - AvoRedPageDataType.TEXT, - ) - } - className={`${getValues(`page_fields.${currentIndex}.field_type`) === AvoRedPageFieldType.SELECT ? "bg-primary-200" : "bg-gray-300"} - ring-1 mt-2 ring-gray-300 hover:cursor-pointer hover:ring-primary-300 p-3 rounded`} - > - {t("select_field")} -
- -
- onPageFieldChange( - currentIndex, - AvoRedPageFieldType.TextEditor, - AvoRedPageDataType.TEXT, - ) - } - className={`${getValues(`page_fields.${currentIndex}.field_type`) === AvoRedPageFieldType.TextEditor ? "bg-primary-200" : "bg-gray-300"} - ring-1 mt-2 ring-gray-300 hover:cursor-pointer hover:ring-primary-300 p-3 rounded`} - > - {t("text_editor_field")} -
-
-
-
-
-
-
-
- setIsOpen(false)} - className="bg-primary-500" - label={t("create_page_field")} - /> -
-
- setIsOpen(false)} - label={t("cancel")} - /> -
-
-
-
- } - isOpen={isOpen} - >
+ + ) : ( <> )} diff --git a/react-admin/src/pages/page/PageEdit.tsx b/react-admin/src/pages/page/PageEdit.tsx index f4a8767b..9b3fb176 100644 --- a/react-admin/src/pages/page/PageEdit.tsx +++ b/react-admin/src/pages/page/PageEdit.tsx @@ -17,7 +17,6 @@ import { usePageEditSchema } from "./schemas/page.edit.schema"; import { usePagePutSchema } from "./schemas/page.put.schema"; import { PutPageIdentifierType } from "../../types/page/PutPageIdentifierType"; import { usePutPageIdentifier } from "./hooks/usePutPageIdentifier"; -import { EditablePageType } from "../../types/page/EditablePageType"; import { AvoRedPageDataType, AvoRedPageFieldType, @@ -25,9 +24,11 @@ import { import _ from "lodash"; import { AvoRedPageFieldSelectFieldDataOptions, - CreatableFieldType, + SaveFieldType, + SavePageType, } from "../../types/page/CreatablePageType"; import SimpleMdeReact from "react-simplemde-editor"; +import { PageFieldModal } from "./PageFieldModal"; function PageEdit() { const [isOpen, setIsOpen] = useState(false); @@ -50,7 +51,7 @@ function PageEdit() { getValues, setValue, trigger, - } = useForm({ + } = useForm({ resolver: joiResolver(usePageEditSchema(), { allowUnknown: true }), values, }); @@ -110,7 +111,7 @@ function PageEdit() { field_index: number, ) => { e.preventDefault(); - const page_field: CreatableFieldType = getValues( + const page_field: SaveFieldType = getValues( `page_fields.${field_index}`, ); const empty_option: AvoRedPageFieldSelectFieldDataOptions = { @@ -124,7 +125,7 @@ function PageEdit() { }; const renderFieldData = (current_index: number) => { - const page_field: CreatableFieldType = getValues( + const page_field: SaveFieldType = getValues( `page_fields.${current_index}`, ); @@ -231,7 +232,7 @@ function PageEdit() { option_index: number, ) => { e.preventDefault(); - const page_field: CreatableFieldType = getValues( + const page_field: SaveFieldType = getValues( `page_fields.${field_index}`, ); // if (page_field.field_d) @@ -271,7 +272,7 @@ function PageEdit() { return getValues(`page_fields.${field_index}.field_content`) as string; }; - const renderField = (field: CreatableFieldType, index: number) => { + const renderField = (field: SaveFieldType, index: number) => { switch (field.field_type) { case AvoRedPageFieldType.TEXTAREA: return ( @@ -367,7 +368,7 @@ function PageEdit() { setIsOpen(true); }; - const submitHandler = async (data: EditablePageType) => { + const submitHandler = async (data: SavePageType) => { mutate(data); }; @@ -383,107 +384,14 @@ function PageEdit() { {_.size(fields) > 0 ? ( <> - setIsOpen(false)} - modal_header={`Page Field`} - modal_body={ -
-
-
-
- -
-
- -
- -
- {renderFieldData(currentIndex)} -
-
-
-
-
- onPageFieldChange( - currentIndex, - AvoRedPageFieldType.TEXT, - AvoRedPageDataType.TEXT, - ) - } - className={`${getValues(`page_fields.${currentIndex}.field_type`) === AvoRedPageFieldType.TEXT ? "bg-primary-200" : "bg-gray-300"} - ring-1 ring-gray-300 hover:cursor-pointer hover:ring-primary-300 p-3 rounded`} - > - {t("text_field")} -
-
- onPageFieldChange( - currentIndex, - AvoRedPageFieldType.TEXTAREA, - AvoRedPageDataType.TEXT, - ) - } - className={`${getValues(`page_fields.${currentIndex}.field_type`) === AvoRedPageFieldType.TEXTAREA ? "bg-primary-200" : "bg-gray-300"} - ring-1 mt-2 ring-gray-300 hover:cursor-pointer hover:ring-primary-300 p-3 rounded`} - > - {t("textarea_field")} -
-
- onPageFieldChange( - currentIndex, - AvoRedPageFieldType.SELECT, - AvoRedPageDataType.TEXT, - ) - } - className={`${getValues(`page_fields.${currentIndex}.field_type`) === AvoRedPageFieldType.SELECT ? "bg-primary-200" : "bg-gray-300"} - ring-1 mt-2 ring-gray-300 hover:cursor-pointer hover:ring-primary-300 p-3 rounded`} - > - {t("select_field")} -
-
- onPageFieldChange( - currentIndex, - AvoRedPageFieldType.TextEditor, - AvoRedPageDataType.TEXT, - ) - } - className={`${getValues(`page_fields.${currentIndex}.field_type`) === AvoRedPageFieldType.TextEditor ? "bg-primary-200" : "bg-gray-300"} - ring-1 mt-2 ring-gray-300 hover:cursor-pointer hover:ring-primary-300 p-3 rounded`} - > - {t("text_editor_field")} -
-
-
-
-
-
- -
-
- } - isOpen={isOpen} - >
+ ) : ( <> diff --git a/react-admin/src/pages/page/PageFieldModal.tsx b/react-admin/src/pages/page/PageFieldModal.tsx new file mode 100644 index 00000000..f4d40298 --- /dev/null +++ b/react-admin/src/pages/page/PageFieldModal.tsx @@ -0,0 +1,341 @@ +import AvoredModal from "../../components/AvoredModal"; +import InputField from "../../components/InputField"; +import { useTranslation } from "react-i18next"; +import { AvoRedPageDataType, AvoRedPageFieldType } from "../../types/page/IPageModel"; +import { AvoRedPageFieldData, AvoRedPageFieldSelectFieldDataOptions, SaveFieldType, SavePageType } from "../../types/page/CreatablePageType"; +import { MinusIcon, PlusIcon } from "@heroicons/react/24/solid"; +import AvoRedButton from "../../components/AvoRedButton"; +import { UseFormGetValues, UseFormRegister, UseFormSetValue, UseFormTrigger } from "react-hook-form"; + +type PageFieldProps = { + register: UseFormRegister + currentIndex: number + getValues: UseFormGetValues + setValue: UseFormSetValue + trigger: UseFormTrigger + setIsOpen: React.Dispatch> + isOpen: boolean +} + +export const PageFieldModal = (({ + register, + currentIndex, + getValues, + setValue, + trigger, + setIsOpen, + isOpen +}: PageFieldProps) => { + + const [t] = useTranslation("global"); + + + const optionLabelOnChange = async ( + e: any, + field_index: number, + option_index: number, + ) => { + setValue( + `page_fields.${field_index}.field_data.select_field_options.${option_index}.label`, + e.target.value, + ); + await trigger("page_fields"); + }; + + const optionValueOnChange = async ( + e: any, + field_index: number, + option_index: number, + ) => { + setValue( + `page_fields.${field_index}.field_data.select_field_options.${option_index}.value`, + e.target.value, + ); + await trigger("page_fields"); + }; + + + const optionAddOnClick = async ( + e: React.MouseEvent, + field_index: number, + ) => { + e.preventDefault(); + const page_field: SaveFieldType = getValues( + `page_fields.${field_index}`, + ); + const empty_option: AvoRedPageFieldSelectFieldDataOptions = { + label: "", + value: "", + }; + + page_field.field_data?.select_field_options.push(empty_option); + + await trigger("page_fields"); + }; + + const optionRemoveOnClick = async ( + e: React.MouseEvent, + field_index: number, + option_index: number, + ) => { + e.preventDefault(); + const page_field: SaveFieldType = getValues( + `page_fields.${field_index}`, + ); + page_field.field_data?.select_field_options.splice(option_index, 1); + + await trigger(`page_fields.${field_index}`); + }; + + const renderFieldData = (current_index: number) => { + const page_field: SaveFieldType = getValues( + `page_fields.${current_index}`, + ); + + + switch (page_field.field_type) { + case AvoRedPageFieldType.SELECT: + return ( + <> + {page_field.field_data?.select_field_options.map( + (option, option_index) => { + return ( +
+
+
+
+ + optionLabelOnChange( + e, + current_index, + option_index, + ) + } + placeholder={t("label")} + className="appearance-none rounded-md ring-1 ring-gray-400 + relative border-0 block w-full px-3 py-2 placeholder-gray-500 text-gray-900 + active::ring-primary-500 + focus:ring-primary-500 focus:outline-none focus:z-10 + disabled:bg-gray-200 disabled:opacity-70 + sm:text-sm " + /> +
+
+
+
+
+ + optionValueOnChange( + e, + current_index, + option_index, + ) + } + placeholder={t("value")} + className="appearance-none rounded-md ring-1 ring-gray-400 + relative border-0 block w-full px-3 py-2 placeholder-gray-500 text-gray-900 + active::ring-primary-500 + focus:ring-primary-500 focus:outline-none focus:z-10 + disabled:bg-gray-200 disabled:opacity-70 + sm:text-sm " + /> +
+
+ {getValues( + `page_fields.${current_index}.field_data.select_field_options`, + ).length === + option_index + 1 ? ( + <> + + + ) : ( + <> + + + )} +
+
+
+
+
+ ); + }, + )} + + ); + default: + return <>; + } + }; + + const onPageFieldChange = async ( + index: number, + field_type: AvoRedPageFieldType, + data_type: AvoRedPageDataType, + ) => { + setValue(`page_fields.${index}.field_type`, field_type); + setValue(`page_fields.${index}.data_type`, data_type); + + if (field_type === AvoRedPageFieldType.SELECT) { + setValue(`page_fields.${index}.field_data.select_field_options`, []); + } + + switch (field_type) { + case AvoRedPageFieldType.SELECT: + const empty_option: AvoRedPageFieldSelectFieldDataOptions = { + label: "", + value: "", + }; + + const options: AvoRedPageFieldData = { + select_field_options: [], + }; + if (typeof options.select_field_options == "undefined") { + options.select_field_options = []; + } + options.select_field_options.push(empty_option); + setValue(`page_fields.${index}.field_data`, options); + + break; + default: + break; + } + + await trigger(`page_fields.${index}`); + }; + + return ( + setIsOpen(false)} + modal_header={`Page Field`} + modal_body={ +
+
+
+
+ +
+
+ +
+ +
+ {renderFieldData(currentIndex)} +
+
+
+
+
+ onPageFieldChange( + currentIndex, + AvoRedPageFieldType.TEXT, + AvoRedPageDataType.TEXT, + ) + } + className={`${getValues(`page_fields.${currentIndex}.field_type`) === AvoRedPageFieldType.TEXT ? "bg-primary-200" : "bg-gray-300"} + ring-1 ring-gray-300 hover:cursor-pointer hover:ring-primary-300 p-3 rounded`} + > + {t("text_field")} +
+
+ onPageFieldChange( + currentIndex, + AvoRedPageFieldType.TEXTAREA, + AvoRedPageDataType.TEXT, + ) + } + className={`${getValues(`page_fields.${currentIndex}.field_type`) === AvoRedPageFieldType.TEXTAREA ? "bg-primary-200" : "bg-gray-300"} + ring-1 mt-2 ring-gray-300 hover:cursor-pointer hover:ring-primary-300 p-3 rounded`} + > + {t("textarea_field")} +
+
+ onPageFieldChange( + currentIndex, + AvoRedPageFieldType.SELECT, + AvoRedPageDataType.TEXT, + ) + } + className={`${getValues(`page_fields.${currentIndex}.field_type`) === AvoRedPageFieldType.SELECT ? "bg-primary-200" : "bg-gray-300"} + ring-1 mt-2 ring-gray-300 hover:cursor-pointer hover:ring-primary-300 p-3 rounded`} + > + {t("select_field")} +
+ +
+ onPageFieldChange( + currentIndex, + AvoRedPageFieldType.TextEditor, + AvoRedPageDataType.TEXT, + ) + } + className={`${getValues(`page_fields.${currentIndex}.field_type`) === AvoRedPageFieldType.TextEditor ? "bg-primary-200" : "bg-gray-300"} + ring-1 mt-2 ring-gray-300 hover:cursor-pointer hover:ring-primary-300 p-3 rounded`} + > + {t("text_editor_field")} +
+
+
+
+
+
+
+
+ setIsOpen(false)} + className="bg-primary-500" + label={t("create_page_field")} + /> +
+
+ setIsOpen(false)} + label={t("cancel")} + /> +
+
+
+
+ } + isOpen={isOpen} + >
+ ); +}) diff --git a/react-admin/src/types/page/CreatablePageType.ts b/react-admin/src/types/page/CreatablePageType.ts index 59cf83ed..dc7750ef 100644 --- a/react-admin/src/types/page/CreatablePageType.ts +++ b/react-admin/src/types/page/CreatablePageType.ts @@ -1,12 +1,12 @@ import {AvoRedPageDataType, AvoRedPageFieldType} from "./IPageModel"; -export type CreatablePageType = { +export type SavePageType = { name: string; identifier: string; - page_fields?: Array + page_fields?: Array } -export type CreatableFieldType = { +export type SaveFieldType = { name: string; identifier: string; data_type: AvoRedPageDataType; @@ -23,4 +23,4 @@ export type EmptyPageFieldData = {} export type AvoRedPageFieldSelectFieldDataOptions = { label: string; value: string; -} \ No newline at end of file +} diff --git a/react-admin/src/types/page/EditablePageType.ts b/react-admin/src/types/page/EditablePageType.ts deleted file mode 100644 index 994a3afb..00000000 --- a/react-admin/src/types/page/EditablePageType.ts +++ /dev/null @@ -1,20 +0,0 @@ -import {AvoRedPageFieldData} from "./CreatablePageType"; -import {AvoRedPageDataType, AvoRedPageFieldType} from "./IPageModel"; - - -export type EditablePageType = { - id: string; - name: string; - identifier: string; - page_fields?: Array -} - -export type EditableFieldType = { - name: string; - identifier: string; - data_type: AvoRedPageDataType; - field_type: AvoRedPageFieldType; - field_content: string | number; - field_data?: AvoRedPageFieldData -} -