From 4de4530fc23f7befd9f43bb62ad2a6ca18b319bd Mon Sep 17 00:00:00 2001 From: mitka Date: Thu, 5 Dec 2024 18:58:16 +0600 Subject: [PATCH 01/14] work in progress --- app/content-panel/blogs/[id]/page.tsx | 26 +++--- .../reusable-components/_Types/index.ts | 36 ++++---- .../blogDetails-card/BlogDetailsCard.tsx | 92 +++++++++---------- 3 files changed, 74 insertions(+), 80 deletions(-) diff --git a/app/content-panel/blogs/[id]/page.tsx b/app/content-panel/blogs/[id]/page.tsx index 37f99f3d..c7c589c5 100644 --- a/app/content-panel/blogs/[id]/page.tsx +++ b/app/content-panel/blogs/[id]/page.tsx @@ -66,21 +66,19 @@ const BlogDetailsPage = ({ params }: { params: { id: string } }) => { return (
{ - // Future delete logic will go here + blogContent={blogDetailsData} + actions={{ + onChange: handleChange, + onImageChange: handleImageChange, + onValidationError: handleValidationError, + onDeleteClick: () => console.log('Future delete logic will go here'), + onCancelClick: handleCancelClick, + imageError: imageError, + }} + states={{ + isEditing, + setIsEditing, }} - onCancelClick={handleCancelClick} - imageError={imageError} - isEditing={isEditing} - setIsEditing={setIsEditing} />
); diff --git a/components/reusable-components/_Types/index.ts b/components/reusable-components/_Types/index.ts index 9027b54e..244d9d07 100644 --- a/components/reusable-components/_Types/index.ts +++ b/components/reusable-components/_Types/index.ts @@ -13,24 +13,6 @@ export interface IPasswordValidation { minLength: boolean; } -export interface BlogDetailsCardProps { - title: string; - imageUrl: string; - content: string; - publishDate: string; - tags: string[]; - onImageChange: (files: File[]) => void; - onChange: ( - event: React.ChangeEvent | { target: { name: string; value: string } } - ) => void; - onDeleteClick: () => void; - onCancelClick: () => void; - imageError: string | null; - onValidationError: (error: string) => void; - isEditing: boolean; - setIsEditing: React.Dispatch>; -} - export interface Author { firstName: string; lastName: string; @@ -44,3 +26,21 @@ export interface BlogDetailsData { publishDate: string; tags: string[]; } + +export interface BlogDetailsCardProps { + blogContent: BlogDetailsData; + states: { + isEditing: boolean; + setIsEditing: React.Dispatch>; + }; + actions: { + onImageChange: (files: File[]) => void; + onChange: ( + event: React.ChangeEvent | { target: { name: string; value: string } } + ) => void; + onDeleteClick: () => void; + onCancelClick: () => void; + imageError: string | null; + onValidationError: (error: string) => void; + }; +} diff --git a/components/reusable-components/blogDetails-card/BlogDetailsCard.tsx b/components/reusable-components/blogDetails-card/BlogDetailsCard.tsx index a1831fae..e52a1b09 100644 --- a/components/reusable-components/blogDetails-card/BlogDetailsCard.tsx +++ b/components/reusable-components/blogDetails-card/BlogDetailsCard.tsx @@ -1,5 +1,3 @@ -/* eslint-disable jsx-a11y/label-has-associated-control */ - import React, { useState, useEffect } from 'react'; import Image from 'next/image'; import { useRouter } from 'next/navigation'; @@ -11,20 +9,13 @@ import { BlogDetailsCardProps } from '../_Types'; import { useEditor } from '../../../app/context/EditorContext'; const BlogDetailsCard: React.FC = ({ - title, - imageUrl, - content, - publishDate, - tags, - onImageChange, - onChange, - onDeleteClick, - onCancelClick, - imageError, - onValidationError, + blogContent: { title, image, content, publishDate, tags }, + actions: { onImageChange, onChange, onDeleteClick, onCancelClick, imageError, onValidationError }, + // states: { isEditing, setIsEditing }, }) => { const { editorState, editorStateChange } = useEditor(); const [isEditable, setIsEditable] = useState(editorState.isEditable); + // console.log('xxxxx', isEditing, isEditable, editorState); const [showModal, setShowModal] = useState(false); const [modalType, setModalType] = useState<'back' | 'cancel'>('back'); const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false); @@ -48,35 +39,41 @@ const BlogDetailsCard: React.FC = ({ } }; - const handleBackClick = () => { - if (hasUnsavedChanges) { - setModalType('back'); - setShowModal(true); - } else { - router.push('/content-panel/blogs'); - } - }; - - const handleCancelClick = () => { - if (hasUnsavedChanges) { - setModalType('cancel'); - setShowModal(true); - } else { - handleConfirm(); - } - }; - - const handleEditClick = () => { - const form = document.querySelector('form') as HTMLFormElement; - - if (form?.checkValidity()) { - const newEditableState = !isEditable; - setIsEditable(newEditableState); - editorStateChange({ isEditable: newEditableState }); - onValidationError(''); - setHasUnsavedChanges(false); - } else { - form?.reportValidity(); + const handleAction = (action: 'back' | 'edit' | 'cancel') => { + switch (action) { + case 'edit': + const form = document.querySelector('form') as HTMLFormElement; + if (form?.checkValidity()) { + const newEditableState = !isEditable; + setIsEditable(newEditableState); + editorStateChange({ isEditable: newEditableState }); + onValidationError(''); + setHasUnsavedChanges(false); + } else { + form?.reportValidity(); + } + break; + + case 'back': + if (hasUnsavedChanges) { + setModalType('back'); + setShowModal(true); + } else { + router.push('/content-panel/blogs'); + } + break; + + case 'cancel': + if (hasUnsavedChanges) { + setModalType('cancel'); + setShowModal(true); + } else { + handleConfirm(); + } + break; + + default: + console.error('Unknown action:', action); } }; @@ -105,23 +102,23 @@ const BlogDetailsCard: React.FC = ({
e.preventDefault()}>
-
{isEditable ? ( <> - - ) : ( <> -
@@ -165,7 +162,6 @@ const BlogDetailsCard: React.FC = ({ } /> ) : ( - // eslint-disable-next-line react/no-danger
)}
From 1c4dfeaf07d71cc469046e1c24f0ad3f07645a0b Mon Sep 17 00:00:00 2001 From: mitka Date: Thu, 5 Dec 2024 19:07:14 +0600 Subject: [PATCH 02/14] '.' --- .../blogDetails-card/BlogDetailsCard.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/components/reusable-components/blogDetails-card/BlogDetailsCard.tsx b/components/reusable-components/blogDetails-card/BlogDetailsCard.tsx index e52a1b09..3a12fdab 100644 --- a/components/reusable-components/blogDetails-card/BlogDetailsCard.tsx +++ b/components/reusable-components/blogDetails-card/BlogDetailsCard.tsx @@ -1,3 +1,5 @@ +/* eslint-disable jsx-a11y/label-has-associated-control */ + import React, { useState, useEffect } from 'react'; import Image from 'next/image'; import { useRouter } from 'next/navigation'; @@ -15,7 +17,7 @@ const BlogDetailsCard: React.FC = ({ }) => { const { editorState, editorStateChange } = useEditor(); const [isEditable, setIsEditable] = useState(editorState.isEditable); - // console.log('xxxxx', isEditing, isEditable, editorState); + // console.log('xxxxxx', isEditing, isEditable, editorState); const [showModal, setShowModal] = useState(false); const [modalType, setModalType] = useState<'back' | 'cancel'>('back'); const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false); @@ -40,9 +42,9 @@ const BlogDetailsCard: React.FC = ({ }; const handleAction = (action: 'back' | 'edit' | 'cancel') => { + const form = document.querySelector('form') as HTMLFormElement; switch (action) { case 'edit': - const form = document.querySelector('form') as HTMLFormElement; if (form?.checkValidity()) { const newEditableState = !isEditable; setIsEditable(newEditableState); From b4dee322922d27fa42199e20e7e8c15a6cbec316 Mon Sep 17 00:00:00 2001 From: mitka Date: Thu, 5 Dec 2024 19:20:27 +0600 Subject: [PATCH 03/14] code base refactored --- .../reusable-components/_Types/index.ts | 6 ++ .../blogDetails-card/BlogDetailsCard.tsx | 72 +++++++++---------- 2 files changed, 41 insertions(+), 37 deletions(-) diff --git a/components/reusable-components/_Types/index.ts b/components/reusable-components/_Types/index.ts index 244d9d07..cf2b152a 100644 --- a/components/reusable-components/_Types/index.ts +++ b/components/reusable-components/_Types/index.ts @@ -27,6 +27,12 @@ export interface BlogDetailsData { tags: string[]; } +export interface IBlogCardState { + showModal: boolean; + modalType: 'back' | 'cancel'; + hasUnsavedChanges: boolean; +} + export interface BlogDetailsCardProps { blogContent: BlogDetailsData; states: { diff --git a/components/reusable-components/blogDetails-card/BlogDetailsCard.tsx b/components/reusable-components/blogDetails-card/BlogDetailsCard.tsx index 3a12fdab..7d980ee8 100644 --- a/components/reusable-components/blogDetails-card/BlogDetailsCard.tsx +++ b/components/reusable-components/blogDetails-card/BlogDetailsCard.tsx @@ -1,41 +1,36 @@ /* eslint-disable jsx-a11y/label-has-associated-control */ -import React, { useState, useEffect } from 'react'; +import React, { useState } from 'react'; import Image from 'next/image'; import { useRouter } from 'next/navigation'; import styles from '../../../app/content-panel/blogs/[id]/BlogDetailsPage.module.scss'; import TiptapEditor from '../../editor/TiptapEditor'; import DropZone from '../drop-zone/DropZone'; import CancelModal from '../modals/CancelModal'; -import { BlogDetailsCardProps } from '../_Types'; -import { useEditor } from '../../../app/context/EditorContext'; +import { BlogDetailsCardProps, IBlogCardState } from '../_Types'; + +const BlogCardInitialState: IBlogCardState = { + showModal: false, + modalType: 'back', + hasUnsavedChanges: false, +}; const BlogDetailsCard: React.FC = ({ blogContent: { title, image, content, publishDate, tags }, actions: { onImageChange, onChange, onDeleteClick, onCancelClick, imageError, onValidationError }, - // states: { isEditing, setIsEditing }, + states: { isEditing, setIsEditing }, }) => { - const { editorState, editorStateChange } = useEditor(); - const [isEditable, setIsEditable] = useState(editorState.isEditable); - // console.log('xxxxxx', isEditing, isEditable, editorState); - const [showModal, setShowModal] = useState(false); - const [modalType, setModalType] = useState<'back' | 'cancel'>('back'); - const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false); + const [state, setState] = useState(BlogCardInitialState); const router = useRouter(); - useEffect(() => { - setIsEditable(editorState.isEditable); - }, [editorState.isEditable]); - const handleConfirm = () => { - setShowModal(false); - if (modalType === 'back') { + setState((prev) => ({ ...prev, showModal: false })); + if (state.modalType === 'back') { router.push('/content-panel/blogs'); } else { - setIsEditable(false); - setHasUnsavedChanges(false); - editorStateChange({ isEditable: false }); + setIsEditing(false); + setState((prev) => ({ ...prev, hasUnsavedChanges: false })); router.replace(window.location.pathname); onCancelClick?.(); } @@ -46,29 +41,25 @@ const BlogDetailsCard: React.FC = ({ switch (action) { case 'edit': if (form?.checkValidity()) { - const newEditableState = !isEditable; - setIsEditable(newEditableState); - editorStateChange({ isEditable: newEditableState }); + setIsEditing(!isEditing); onValidationError(''); - setHasUnsavedChanges(false); + setState((prev) => ({ ...prev, hasUnsavedChanges: false })); } else { form?.reportValidity(); } break; case 'back': - if (hasUnsavedChanges) { - setModalType('back'); - setShowModal(true); + if (state.hasUnsavedChanges) { + setState((prev) => ({ ...prev, modalType: 'back', showModal: true })); } else { router.push('/content-panel/blogs'); } break; case 'cancel': - if (hasUnsavedChanges) { - setModalType('cancel'); - setShowModal(true); + if (state.hasUnsavedChanges) { + setState((prev) => ({ ...prev, modalType: 'cancel', showModal: true })); } else { handleConfirm(); } @@ -83,10 +74,10 @@ const BlogDetailsCard: React.FC = ({ event: React.ChangeEvent | { target: { name: string; value: string } } ) => { onChange(event); - setHasUnsavedChanges(true); + setState((prev) => ({ ...prev, hasUnsavedChanges: true })); }; - const renderInput = (id: string, value: string, disabled: boolean = !isEditable) => ( + const renderInput = (id: string, value: string, disabled: boolean = !isEditing) => ( = ({
- {isEditable ? ( + {isEditing ? ( <>