From ce73389fccff11dba1d971254c6c2543997654ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20Feij=C3=B3=20de=20Almeida=20Pereira=20Lima?= Date: Mon, 11 Sep 2023 13:36:43 -0300 Subject: [PATCH 01/23] fix: tree item style --- src/components/Menu/Items/NodeTreeView.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Menu/Items/NodeTreeView.tsx b/src/components/Menu/Items/NodeTreeView.tsx index 5d00344..2b7f302 100644 --- a/src/components/Menu/Items/NodeTreeView.tsx +++ b/src/components/Menu/Items/NodeTreeView.tsx @@ -63,7 +63,7 @@ export const NodeTreeView = ({ nodeId={node.id.toString()} label={node.label} sx={{ - '& > .MuiTreeItem-content > .MuiTreeItem-label': { + '& > .MuiTreeItem-content': { ...baseSX, color, fontWeight, @@ -80,7 +80,7 @@ export const NodeTreeView = ({ nodeId={node.id.toString()} label={node.label} sx={{ - '& .MuiTreeItem-content .MuiTreeItem-label': { + '& .MuiTreeItem-content': { ...baseSX, color, fontWeight, From d9c7889b22d05f6065928991488d6f63cdf86263 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20Feij=C3=B3=20de=20Almeida=20Pereira=20Lima?= Date: Mon, 11 Sep 2023 13:39:47 -0300 Subject: [PATCH 02/23] feat: tree item arrow downward --- src/components/Menu/Items/NodeTreeView.tsx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/components/Menu/Items/NodeTreeView.tsx b/src/components/Menu/Items/NodeTreeView.tsx index 2b7f302..7affeb7 100644 --- a/src/components/Menu/Items/NodeTreeView.tsx +++ b/src/components/Menu/Items/NodeTreeView.tsx @@ -1,10 +1,11 @@ import React from 'react'; import { Box } from '@mui/material'; import { TreeItem, TreeView } from '@mui/lab'; -import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; -import ExpandLessIcon from '@mui/icons-material/ExpandLess'; +import ArrowForwardIosIcon from '@mui/icons-material/ArrowForwardIos'; import { EnumInputAction, IEditingNode, INode } from '../../../types'; +const ArrowDownwardIcon = () => ; + interface Props { nodes: INode[]; editingNode: IEditingNode; @@ -104,8 +105,8 @@ export const NodeTreeView = ({ }} > } - defaultCollapseIcon={} + defaultExpandIcon={} + defaultCollapseIcon={} defaultExpanded={['0']} expanded={expanded} selected={selected} From 6796cfc403b45c10b0051935dacf802a8c16670d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20Feij=C3=B3=20de=20Almeida=20Pereira=20Lima?= Date: Mon, 11 Sep 2023 13:46:38 -0300 Subject: [PATCH 03/23] refactor: unecessary if --- src/components/Menu/Items/NodeTreeView.tsx | 24 ++++------------------ 1 file changed, 4 insertions(+), 20 deletions(-) diff --git a/src/components/Menu/Items/NodeTreeView.tsx b/src/components/Menu/Items/NodeTreeView.tsx index 7affeb7..63c8ee4 100644 --- a/src/components/Menu/Items/NodeTreeView.tsx +++ b/src/components/Menu/Items/NodeTreeView.tsx @@ -57,37 +57,21 @@ export const NodeTreeView = ({ color = 'red'; break; } - if (node.children?.length > 0) { - return ( - .MuiTreeItem-content': { - ...baseSX, - color, - fontWeight, - }, - }} - > - {renderNodes(node.children)} - - ); - } return ( .MuiTreeItem-content': { ...baseSX, color, fontWeight, }, }} - /> + > + {node.children?.length > 0 && renderNodes(node.children)} + ); }); }, From 403ef5cee2800fa5d6ba4fe2a74a4c57c23bd3a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20Feij=C3=B3=20de=20Almeida=20Pereira=20Lima?= Date: Mon, 11 Sep 2023 15:40:31 -0300 Subject: [PATCH 04/23] refactor: iinternal CustomTreeItem component --- src/components/Menu/Items/NodeTreeView.tsx | 81 +++++++++++++++------- 1 file changed, 55 insertions(+), 26 deletions(-) diff --git a/src/components/Menu/Items/NodeTreeView.tsx b/src/components/Menu/Items/NodeTreeView.tsx index 63c8ee4..013a5ab 100644 --- a/src/components/Menu/Items/NodeTreeView.tsx +++ b/src/components/Menu/Items/NodeTreeView.tsx @@ -1,9 +1,54 @@ import React from 'react'; -import { Box } from '@mui/material'; +import { Box, IconButton } from '@mui/material'; import { TreeItem, TreeView } from '@mui/lab'; import ArrowForwardIosIcon from '@mui/icons-material/ArrowForwardIos'; +import MoreVertIcon from '@mui/icons-material/MoreVert'; import { EnumInputAction, IEditingNode, INode } from '../../../types'; +interface CustomTreeItemProps { + key: string | number; + node: INode; + color: string; + fontWeight: string; + renderNodes: (nodes: INode[]) => JSX.Element[]; +} + +const CustomTreeItem = ({ key, node, color, fontWeight, renderNodes }: CustomTreeItemProps) => { + const { id, label, children } = node; + + const baseSX = { + border: '1px solid #eaeaec', + margin: '3px 0px', + backgroundColor: '#fff', + borderRadius: '4px', + padding: '12px 0px 13px 25px', + }; + + return ( + + {label} + + + + + } + sx={{ + '& > .MuiTreeItem-content': { + ...baseSX, + color, + fontWeight, + }, + }} + > + {children?.length > 0 && renderNodes(children)} + + ); +}; + const ArrowDownwardIcon = () => ; interface Props { @@ -34,16 +79,8 @@ export const NodeTreeView = ({ }; const renderNodes = React.useCallback( - (nodes: INode[]) => { - const baseSX = { - border: '1px solid #eaeaec', - margin: '3px 0px', - backgroundColor: '#fff', - borderRadius: '4px', - padding: '12px 0px 13px 25px', - }; - - return nodes.map(node => { + (nodes: INode[]) => + nodes.map(node => { let color = 'black'; const fontWeight = node.id === editingNode.id ? 'bold' : 'normal'; switch (node.action) { @@ -58,23 +95,15 @@ export const NodeTreeView = ({ break; } return ( - .MuiTreeItem-content': { - ...baseSX, - color, - fontWeight, - }, - }} - > - {node.children?.length > 0 && renderNodes(node.children)} - + node={node} + color={color} + fontWeight={fontWeight} + renderNodes={renderNodes} + /> ); - }); - }, + }), [editingNode.id], ); From da2e567bdd13b3689ae0f530b50b44a5d58b779d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20Feij=C3=B3=20de=20Almeida=20Pereira=20Lima?= Date: Mon, 11 Sep 2023 16:18:01 -0300 Subject: [PATCH 05/23] feat: tree item context menu --- src/components/Menu/Items/NodeTreeView.tsx | 46 +++++++++++++++++++++- src/i18n/locales/en/translation.json | 3 +- src/i18n/locales/pt-BR/translation.json | 3 +- 3 files changed, 48 insertions(+), 4 deletions(-) diff --git a/src/components/Menu/Items/NodeTreeView.tsx b/src/components/Menu/Items/NodeTreeView.tsx index 013a5ab..c396bd2 100644 --- a/src/components/Menu/Items/NodeTreeView.tsx +++ b/src/components/Menu/Items/NodeTreeView.tsx @@ -1,8 +1,9 @@ import React from 'react'; -import { Box, IconButton } from '@mui/material'; +import { Box, Divider, IconButton, ListItemText, Menu, MenuItem, MenuList } from '@mui/material'; import { TreeItem, TreeView } from '@mui/lab'; import ArrowForwardIosIcon from '@mui/icons-material/ArrowForwardIos'; import MoreVertIcon from '@mui/icons-material/MoreVert'; +import { useTranslation } from 'react-i18next'; import { EnumInputAction, IEditingNode, INode } from '../../../types'; interface CustomTreeItemProps { @@ -24,6 +25,21 @@ const CustomTreeItem = ({ key, node, color, fontWeight, renderNodes }: CustomTre padding: '12px 0px 13px 25px', }; + const { t } = useTranslation(); + + const [anchorEl, setAnchorEl] = React.useState(null); + + const handleClick = (event: React.SyntheticEvent) => { + event.stopPropagation(); + event.preventDefault(); + setAnchorEl(event.currentTarget as HTMLElement); + }; + const handleClose = (event: React.SyntheticEvent) => { + event.stopPropagation(); + event.preventDefault(); + setAnchorEl(null); + }; + return ( {label} - + + + + + {t('menu.preview.actions.insertChild')} + + + + {t('menu.preview.actions.editTemplate')} + + + {t('buttons.delete')} + + } sx={{ diff --git a/src/i18n/locales/en/translation.json b/src/i18n/locales/en/translation.json index 28f9f69..7077a45 100644 --- a/src/i18n/locales/en/translation.json +++ b/src/i18n/locales/en/translation.json @@ -259,7 +259,8 @@ "insert": "Insert", "edit": "Edit", "delete": "Delete", - "editTemplate": "Edit Template" + "editTemplate": "Edit Template", + "insertChild": "Insert Child" }, "parent": "Parent", "newItem": "New Item {{order}}", diff --git a/src/i18n/locales/pt-BR/translation.json b/src/i18n/locales/pt-BR/translation.json index 2f2daa7..e687fac 100644 --- a/src/i18n/locales/pt-BR/translation.json +++ b/src/i18n/locales/pt-BR/translation.json @@ -260,7 +260,8 @@ "insert": "Inserir", "edit": "Editar", "delete": "Deletar", - "editTemplate": "Editar Template" + "editTemplate": "Editar Template", + "insertChild": "Inserir Item Filho" }, "parent": "Item Pai", "newItem": "Novo Item {{order}}", From 84c23893c4e7334e5b6af60fd5f12fd0ffdd80f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20Feij=C3=B3=20de=20Almeida=20Pereira=20Lima?= Date: Mon, 11 Sep 2023 16:39:51 -0300 Subject: [PATCH 06/23] feat: menu item delete confirm dialog --- src/components/Menu/Items/NodeTreeView.tsx | 9 ++++++++- src/i18n/locales/en/translation.json | 6 ++++++ src/i18n/locales/pt-BR/translation.json | 6 ++++++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/components/Menu/Items/NodeTreeView.tsx b/src/components/Menu/Items/NodeTreeView.tsx index c396bd2..3f822d7 100644 --- a/src/components/Menu/Items/NodeTreeView.tsx +++ b/src/components/Menu/Items/NodeTreeView.tsx @@ -40,6 +40,13 @@ const CustomTreeItem = ({ key, node, color, fontWeight, renderNodes }: CustomTre setAnchorEl(null); }; + const handleDelete = (event: React.SyntheticEvent) => { + event.stopPropagation(); + event.preventDefault(); + window.confirm(t('menu.preview.alerts.deleteItem.message')); + setAnchorEl(null); + }; + return ( {t('menu.preview.actions.editTemplate')} - {t('buttons.delete')} + {t('buttons.delete')} diff --git a/src/i18n/locales/en/translation.json b/src/i18n/locales/en/translation.json index 7077a45..1df03f3 100644 --- a/src/i18n/locales/en/translation.json +++ b/src/i18n/locales/en/translation.json @@ -284,6 +284,12 @@ }, "errors": { "noItemSelected": "No item selected" + }, + "alerts": { + "deleteItem": { + "title": "Delete Item", + "message": "Are you sure you want to delete this item?" + } } }, "list": { diff --git a/src/i18n/locales/pt-BR/translation.json b/src/i18n/locales/pt-BR/translation.json index e687fac..02041b7 100644 --- a/src/i18n/locales/pt-BR/translation.json +++ b/src/i18n/locales/pt-BR/translation.json @@ -285,6 +285,12 @@ }, "errors": { "noItemSelected": "Nenhum item selecionado" + }, + "alerts": { + "deleteItem": { + "title": "Deletar Item", + "message": "Tem certeza que deseja deletar esse item?" + } } }, "list": { From 0fe7afcb64db76b72ea5d5b5b618c03e8fff7a12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20Feij=C3=B3=20de=20Almeida=20Pereira=20Lima?= Date: Mon, 11 Sep 2023 16:57:27 -0300 Subject: [PATCH 07/23] feat: handleEditTemplate --- src/components/Menu/Items/NodeTreeView.tsx | 13 ++++++++++++- src/pages/Menu/Items/EditTemplate.tsx | 2 +- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/components/Menu/Items/NodeTreeView.tsx b/src/components/Menu/Items/NodeTreeView.tsx index 3f822d7..672aec7 100644 --- a/src/components/Menu/Items/NodeTreeView.tsx +++ b/src/components/Menu/Items/NodeTreeView.tsx @@ -4,6 +4,7 @@ import { TreeItem, TreeView } from '@mui/lab'; import ArrowForwardIosIcon from '@mui/icons-material/ArrowForwardIos'; import MoreVertIcon from '@mui/icons-material/MoreVert'; import { useTranslation } from 'react-i18next'; +import { useNavigate, useParams } from 'react-router-dom'; import { EnumInputAction, IEditingNode, INode } from '../../../types'; interface CustomTreeItemProps { @@ -25,6 +26,8 @@ const CustomTreeItem = ({ key, node, color, fontWeight, renderNodes }: CustomTre padding: '12px 0px 13px 25px', }; + const { id: menuId } = useParams(); + const navigate = useNavigate(); const { t } = useTranslation(); const [anchorEl, setAnchorEl] = React.useState(null); @@ -43,10 +46,18 @@ const CustomTreeItem = ({ key, node, color, fontWeight, renderNodes }: CustomTre const handleDelete = (event: React.SyntheticEvent) => { event.stopPropagation(); event.preventDefault(); + // eslint-disable-next-line no-alert window.confirm(t('menu.preview.alerts.deleteItem.message')); setAnchorEl(null); }; + const handleEditTemplate = (event: React.SyntheticEvent) => { + event.stopPropagation(); + event.preventDefault(); + navigate(`/menus/${menuId}/items/${id}`); + setAnchorEl(null); + }; + return ( {t('menu.preview.actions.insertChild')} - + {t('menu.preview.actions.editTemplate')} diff --git a/src/pages/Menu/Items/EditTemplate.tsx b/src/pages/Menu/Items/EditTemplate.tsx index 9d1168f..67abbeb 100644 --- a/src/pages/Menu/Items/EditTemplate.tsx +++ b/src/pages/Menu/Items/EditTemplate.tsx @@ -286,7 +286,7 @@ export const EditTemplateItems = () => { items={[ { label: t('menu.title', { count: 2 }), navigateTo: '/' }, { label: data?.menuItem.menu.name, navigateTo: '../../' }, - { label: t('menu.preview.title'), navigateTo: '../edit' }, + { label: t('menu.preview.title'), navigateTo: '../../edit' }, { label: data?.menuItem.label }, ]} onBack={onBackClickHandler} From 27f754ab8c9ab51a77e485029ca39852ff00942b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20Feij=C3=B3=20de=20Almeida=20Pereira=20Lima?= Date: Mon, 11 Sep 2023 18:59:12 -0300 Subject: [PATCH 08/23] feat: reimplementing delete menu item from context menu --- src/components/Menu/Items/NodeTreeView.tsx | 64 +++++++- src/components/Menu/Items/OperationScreen.tsx | 151 +---------------- src/pages/Menu/Items/Items.tsx | 152 +++++++++++++++++- 3 files changed, 212 insertions(+), 155 deletions(-) diff --git a/src/components/Menu/Items/NodeTreeView.tsx b/src/components/Menu/Items/NodeTreeView.tsx index 672aec7..58da370 100644 --- a/src/components/Menu/Items/NodeTreeView.tsx +++ b/src/components/Menu/Items/NodeTreeView.tsx @@ -8,14 +8,26 @@ import { useNavigate, useParams } from 'react-router-dom'; import { EnumInputAction, IEditingNode, INode } from '../../../types'; interface CustomTreeItemProps { - key: string | number; node: INode; color: string; fontWeight: string; renderNodes: (nodes: INode[]) => JSX.Element[]; + setSelected: (selected: string) => void; + emptyEditingNode: IEditingNode; + setEditingNode: (editingNode: IEditingNode) => void; + handleUpdate: () => Promise; } -const CustomTreeItem = ({ key, node, color, fontWeight, renderNodes }: CustomTreeItemProps) => { +const CustomTreeItem = ({ + node, + color, + fontWeight, + renderNodes, + setSelected, + emptyEditingNode, + setEditingNode, + handleUpdate, +}: CustomTreeItemProps) => { const { id, label, children } = node; const baseSX = { @@ -32,6 +44,8 @@ const CustomTreeItem = ({ key, node, color, fontWeight, renderNodes }: CustomTre const [anchorEl, setAnchorEl] = React.useState(null); + const [confirmedDelete, setConfirmedDelete] = React.useState(false); + const handleClick = (event: React.SyntheticEvent) => { event.stopPropagation(); event.preventDefault(); @@ -43,14 +57,37 @@ const CustomTreeItem = ({ key, node, color, fontWeight, renderNodes }: CustomTre setAnchorEl(null); }; - const handleDelete = (event: React.SyntheticEvent) => { + const handleDelete = async (event: React.SyntheticEvent) => { event.stopPropagation(); event.preventDefault(); - // eslint-disable-next-line no-alert - window.confirm(t('menu.preview.alerts.deleteItem.message')); + if (id === 0) { + setSelected(''); + return; + } + setEditingNode({ + ...node, + action: EnumInputAction.DELETE, + original: node, + }); setAnchorEl(null); + // eslint-disable-next-line no-promise-executor-return + await new Promise(resolve => setTimeout(resolve, 100)); + // eslint-disable-next-line no-alert + const confirmed = window.confirm(t('menu.preview.alerts.deleteItem.message')); + if (!confirmed) { + setEditingNode(emptyEditingNode); + // TODO: not working setSelected after cancel delete + setSelected(id.toString()); + } + setConfirmedDelete(confirmed); }; + React.useEffect(() => { + if (confirmedDelete) { + handleUpdate(); + } + }, [confirmedDelete, handleUpdate]); + const handleEditTemplate = (event: React.SyntheticEvent) => { event.stopPropagation(); event.preventDefault(); @@ -60,11 +97,12 @@ const CustomTreeItem = ({ key, node, color, fontWeight, renderNodes }: CustomTre return ( - {label} + + {label} + @@ -113,22 +151,28 @@ const ArrowDownwardIcon = () => void; expanded: string[]; setExpanded: (expanded: string[]) => void; selected: string; setSelected: (selected: string) => void; preview: (nodes: INode[], editingNode: IEditingNode) => INode[]; + handleUpdate: () => Promise; } export const NodeTreeView = ({ nodes, + emptyEditingNode, editingNode, + setEditingNode, expanded, setExpanded, selected, setSelected, preview, + handleUpdate, }: Props) => { const handleToggle = (event: React.SyntheticEvent, nodeIds: string[]) => { setExpanded(nodeIds); @@ -161,10 +205,14 @@ export const NodeTreeView = ({ color={color} fontWeight={fontWeight} renderNodes={renderNodes} + setSelected={setSelected} + emptyEditingNode={emptyEditingNode} + setEditingNode={setEditingNode} + handleUpdate={handleUpdate} /> ); }), - [editingNode.id], + [editingNode.id, emptyEditingNode, handleUpdate, setEditingNode, setSelected], ); return ( diff --git a/src/components/Menu/Items/OperationScreen.tsx b/src/components/Menu/Items/OperationScreen.tsx index a0fca33..ca5030d 100644 --- a/src/components/Menu/Items/OperationScreen.tsx +++ b/src/components/Menu/Items/OperationScreen.tsx @@ -13,12 +13,9 @@ import { AdapterLuxon } from '@mui/x-date-pickers/AdapterLuxon'; import { DatePicker, DateTimePicker, LocalizationProvider } from '@mui/x-date-pickers'; import AddIcon from '@mui/icons-material/Add'; import EditIcon from '@mui/icons-material/Edit'; -import DeleteIcon from '@mui/icons-material/Delete'; -import SummarizeOutlinedIcon from '@mui/icons-material/SummarizeOutlined'; import { DateTime } from 'luxon'; import { useMutation } from '@apollo/client'; import { useTranslation } from 'react-i18next'; -import { useNavigate } from 'react-router-dom'; import { EnumInputAction, IEditingNode, @@ -34,6 +31,7 @@ import { openDefaultErrorNotification, } from '../../../contexts/NotificationContext'; import MenuService from '../../../api/services/MenuService'; +import { EnumInputActionScreen } from '../../../pages/Menu/Items'; const Form = styled('form')({ display: 'flex', @@ -44,13 +42,6 @@ const Form = styled('form')({ overflowY: 'auto', }); -enum EnumInputActionScreen { - SELECTING_ACTION, - INSERT, - UPDATE, - DELETE, -} - interface Props { id: string; data: { menu: IMenu }; @@ -62,9 +53,12 @@ interface Props { setExpanded: (expanded: string[]) => void; selected: string; setSelected: (selected: string) => void; + operationScreen: EnumInputActionScreen; + setOperationScreen: (operationScreen: EnumInputActionScreen) => void; findNodeById: (nodes: INode[], id: number) => INode | undefined; preview: (nodes: INode[], editingNode: IEditingNode) => INode[]; setUpdatedMenu: (menu: IMenu) => void; + handleUpdate: () => Promise; } export const OperationScreen = ({ @@ -77,6 +71,8 @@ export const OperationScreen = ({ setExpanded, selected, setSelected, + operationScreen, + setOperationScreen, findNodeById, preview, setEditingNode, @@ -84,12 +80,8 @@ export const OperationScreen = ({ }: Props) => { const { dispatch } = React.useContext(NotificationContext); - const navigate = useNavigate(); const { i18n, t } = useTranslation(); - const [operationScreen, setOperationScreen] = React.useState( - EnumInputActionScreen.SELECTING_ACTION, - ); const [labelError, setLabelError] = React.useState(''); const [startPublicationError, setStartPublicationError] = React.useState(''); const [endPublicationError, setEndPublicationError] = React.useState(''); @@ -273,17 +265,6 @@ export const OperationScreen = ({ } }; - const handleDeleteSubmit = async (e: React.FormEvent) => { - e.preventDefault(); - e.stopPropagation(); - - try { - await handleUpdate(); - } catch (error) { - console.error(error); - } - }; - const handleActionChange = React.useCallback( (action: EnumInputActionScreen) => { const selectedNode = findNodeById(nodes, Number(selected)); @@ -351,18 +332,6 @@ export const OperationScreen = ({ }); setSelected(selectedNode.id.toString()); break; - case EnumInputActionScreen.DELETE: - if (!selectedNode || selectedNode.id === 0) { - !selectedNode && setSelected(''); - return; - } - setEditingNode({ - ...selectedNode, - action: EnumInputAction.DELETE, - original: selectedNode, - }); - setSelected(selectedNode.id.toString()); - break; } setOperationScreen(action); }, @@ -377,6 +346,7 @@ export const OperationScreen = ({ setEditingNode, setExpanded, setSelected, + setOperationScreen, ], ); @@ -606,50 +576,7 @@ export const OperationScreen = ({ {t('buttons.edit')} - - ); case EnumInputActionScreen.INSERT: @@ -1268,70 +1195,6 @@ export const OperationScreen = ({ ); - case EnumInputActionScreen.DELETE: - return ( - - - {t('menu.preview.actions.delete')} - - - - - - - - - ); default: return null; } diff --git a/src/pages/Menu/Items/Items.tsx b/src/pages/Menu/Items/Items.tsx index dfce96c..b196b7b 100644 --- a/src/pages/Menu/Items/Items.tsx +++ b/src/pages/Menu/Items/Items.tsx @@ -1,9 +1,8 @@ -import { Box, Typography } from '@mui/material'; +import { Box } from '@mui/material'; import React, { useCallback, useMemo, useState } from 'react'; import { useParams } from 'react-router-dom'; -import WidgetsIcon from '@mui/icons-material/Widgets'; import { useTranslation } from 'react-i18next'; -import { useQuery } from '@apollo/client'; +import { useMutation, useQuery } from '@apollo/client'; import { DateTime } from 'luxon'; import Loading from '../../../components/Loading'; import MenuService from '../../../api/services/MenuService'; @@ -17,6 +16,11 @@ import { } from '../../../types'; import DefaultErrorPage from '../../../components/DefaultErrorPage'; import { NodeTreeView, OperationScreen } from '../../../components/Menu/Items'; +import { + ActionTypes, + NotificationContext, + openDefaultErrorNotification, +} from '../../../contexts/NotificationContext'; const emptyEditingNode: IEditingNode = { id: 0, @@ -39,7 +43,15 @@ const emptyEditingNode: IEditingNode = { }, }; +export enum EnumInputActionScreen { + SELECTING_ACTION, + INSERT, + UPDATE, +} + export const ItemsPreview = () => { + const { dispatch } = React.useContext(NotificationContext); + const { t } = useTranslation(); const { id } = useParams(); @@ -47,12 +59,17 @@ export const ItemsPreview = () => { const { loading, error, data } = useQuery(MenuService.GET_MENU, { variables: { id: Number(id) }, }); + const [updateMenu] = useMutation(MenuService.UPDATE_MENU); const [updatedMenu, setUpdatedMenu] = useState(); const [expanded, setExpanded] = useState(['0']); const [selected, setSelected] = useState('0'); + const [operationScreen, setOperationScreen] = React.useState( + EnumInputActionScreen.SELECTING_ACTION, + ); + const nodes = useMemo(() => { const items: GraphQLData[] = updatedMenu?.items || data?.menu.items || []; const getChildren = (parent: IMenuItem): INode[] => { @@ -129,6 +146,129 @@ export const ItemsPreview = () => { return undefined; }, []); + const handleUpdate = async (): Promise => { + const formatNodes = (nodes: INode[]) => + nodes + .map(node => { + const { + id, + children, + original, + parentId, + createdAt, + updatedAt, + version, + menuId, + defaultTemplate, + action, + rules, + ...rest + } = node; + let meta; + if (rest.meta) { + meta = Object.keys(rest.meta).reduce((acc, key) => { + const name = data.menu.meta.find(meta => meta.id === Number(key))?.name || key; + const meta = rest.meta[key]; + const originalMetaMap = original?.meta || {}; + const originalMeta = originalMetaMap[key] || originalMetaMap[name]; + if (meta == null || meta === '' || meta === originalMeta) { + return acc; + } + return { + ...acc, + [name]: meta, + }; + }, {}); + if (Object.keys(meta).length === 0) meta = undefined; + } + let startPublication; + if (rest.startPublication) { + if ( + (original?.startPublication !== rest.startPublication || + !original?.startPublication) && + rest.startPublication.isValid + ) + startPublication = rest.startPublication.toISO(); + } else { + startPublication = null; + } + let endPublication; + if (rest.endPublication) { + if ( + (original?.endPublication !== rest.endPublication || !original?.endPublication) && + rest.endPublication.isValid + ) + endPublication = rest.endPublication.toISO(); + } else { + endPublication = null; + } + let label; + if (action === EnumInputAction.CREATE) label = rest.label; + else if (rest.label && (original?.label !== rest.label || !original?.label)) + label = rest.label; + let order; + if (action === EnumInputAction.CREATE) order = rest.order; + else if (rest.order && (original?.order !== rest.order || !original?.order)) + order = rest.order; + let enabled = action === EnumInputAction.CREATE ? true : undefined; + if (rest.enabled && (original?.enabled !== rest.enabled || !original?.enabled)) + enabled = rest.enabled; + return { + action, + label, + order, + enabled, + startPublication, + endPublication, + meta, + rules, + children: children && formatNodes(children), + id: id === -1 ? undefined : id, + }; + }) + .filter(node => !!node.action); + + const items = formatNodes(preview(nodes, editingNode)[0].children).map(node => { + const id = node.id === -1 ? undefined : node.id; + return { + ...node, + id, + }; + }); + + await updateMenu({ + variables: { menu: { id: Number(id), items } }, + onCompleted: data => { + dispatch({ + type: ActionTypes.OPEN_NOTIFICATION, + message: `${t('notification.editSuccess', { + resource: t('menu.title', { count: 1 }), + context: 'male', + })}!`, + }); + switch (editingNode.action) { + case EnumInputAction.CREATE: + case EnumInputAction.DELETE: + setEditingNode(emptyEditingNode); + setSelected(''); + break; + case EnumInputAction.UPDATE: + setSelected(editingNode.id.toString()); + break; + } + setUpdatedMenu(data.updateMenu); + setOperationScreen(EnumInputActionScreen.SELECTING_ACTION); + setEditingNode(emptyEditingNode); + return Promise.resolve(); + }, + onError: error => { + openDefaultErrorNotification(error, dispatch); + return Promise.reject(); + }, + }); + return Promise.resolve(); + }; + const preview = useCallback((nodes: INode[], editingNode: IEditingNode): INode[] => { if (!editingNode.id) return nodes; if (nodes[0].id === 0 && nodes[0].children?.length === 0) @@ -399,6 +539,9 @@ export const ItemsPreview = () => { selected={selected} setSelected={setSelected} preview={preview} + emptyEditingNode={emptyEditingNode} + setEditingNode={setEditingNode} + handleUpdate={handleUpdate} /> @@ -425,6 +568,9 @@ export const ItemsPreview = () => { findNodeById={findNodeById} preview={preview} setUpdatedMenu={setUpdatedMenu} + operationScreen={operationScreen} + setOperationScreen={setOperationScreen} + handleUpdate={handleUpdate} /> From 6b442e133cde6bc504d9215549b2e03892af9a09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20Feij=C3=B3=20de=20Almeida=20Pereira=20Lima?= Date: Mon, 11 Sep 2023 19:35:37 -0300 Subject: [PATCH 09/23] feat: menu item show update operation screen when item is clicked --- src/components/Menu/Items/NodeTreeView.tsx | 55 ++++++++++++++++++- src/components/Menu/Items/OperationScreen.tsx | 49 ----------------- src/pages/Menu/Items/Items.tsx | 2 + 3 files changed, 54 insertions(+), 52 deletions(-) diff --git a/src/components/Menu/Items/NodeTreeView.tsx b/src/components/Menu/Items/NodeTreeView.tsx index 58da370..7f97f07 100644 --- a/src/components/Menu/Items/NodeTreeView.tsx +++ b/src/components/Menu/Items/NodeTreeView.tsx @@ -5,7 +5,8 @@ import ArrowForwardIosIcon from '@mui/icons-material/ArrowForwardIos'; import MoreVertIcon from '@mui/icons-material/MoreVert'; import { useTranslation } from 'react-i18next'; import { useNavigate, useParams } from 'react-router-dom'; -import { EnumInputAction, IEditingNode, INode } from '../../../types'; +import { EnumInputAction, IEditingNode, INode, MenuMetaType } from '../../../types'; +import { EnumInputActionScreen } from '../../../pages/Menu/Items'; interface CustomTreeItemProps { node: INode; @@ -16,6 +17,8 @@ interface CustomTreeItemProps { emptyEditingNode: IEditingNode; setEditingNode: (editingNode: IEditingNode) => void; handleUpdate: () => Promise; + data: any; + setOperationScreen: (operationScreen: EnumInputActionScreen) => void; } const CustomTreeItem = ({ @@ -27,8 +30,10 @@ const CustomTreeItem = ({ emptyEditingNode, setEditingNode, handleUpdate, + data, + setOperationScreen, }: CustomTreeItemProps) => { - const { id, label, children } = node; + const { id, label, children, meta } = node; const baseSX = { border: '1px solid #eaeaec', @@ -95,9 +100,39 @@ const CustomTreeItem = ({ setAnchorEl(null); }; + const onNodeClick = (event: React.SyntheticEvent) => { + if (id === 0) { + !node && setSelected(''); + return; + } + const itemMeta = { ...meta }; + data?.menu.meta?.forEach(m => { + const defaultValue = (meta || {})[m.id] || m.defaultValue; + switch (m.type) { + case MenuMetaType.TEXT: + case MenuMetaType.NUMBER: + case MenuMetaType.DATE: + itemMeta[m.name] = defaultValue || ''; + break; + case MenuMetaType.BOOLEAN: + itemMeta[m.name] = defaultValue || false; + break; + } + }); + setEditingNode({ + ...node, + meta: itemMeta, + action: EnumInputAction.UPDATE, + original: node, + }); + setSelected(id.toString()); + setOperationScreen(EnumInputActionScreen.UPDATE); + }; + return ( @@ -160,6 +195,8 @@ interface Props { setSelected: (selected: string) => void; preview: (nodes: INode[], editingNode: IEditingNode) => INode[]; handleUpdate: () => Promise; + data: any; + setOperationScreen: (operationScreen: EnumInputActionScreen) => void; } export const NodeTreeView = ({ @@ -173,6 +210,8 @@ export const NodeTreeView = ({ setSelected, preview, handleUpdate, + data, + setOperationScreen, }: Props) => { const handleToggle = (event: React.SyntheticEvent, nodeIds: string[]) => { setExpanded(nodeIds); @@ -209,10 +248,20 @@ export const NodeTreeView = ({ emptyEditingNode={emptyEditingNode} setEditingNode={setEditingNode} handleUpdate={handleUpdate} + data={data} + setOperationScreen={setOperationScreen} /> ); }), - [editingNode.id, emptyEditingNode, handleUpdate, setEditingNode, setSelected], + [ + editingNode.id, + emptyEditingNode, + handleUpdate, + setEditingNode, + setSelected, + data, + setOperationScreen, + ], ); return ( diff --git a/src/components/Menu/Items/OperationScreen.tsx b/src/components/Menu/Items/OperationScreen.tsx index ca5030d..9dd173a 100644 --- a/src/components/Menu/Items/OperationScreen.tsx +++ b/src/components/Menu/Items/OperationScreen.tsx @@ -12,7 +12,6 @@ import { import { AdapterLuxon } from '@mui/x-date-pickers/AdapterLuxon'; import { DatePicker, DateTimePicker, LocalizationProvider } from '@mui/x-date-pickers'; import AddIcon from '@mui/icons-material/Add'; -import EditIcon from '@mui/icons-material/Edit'; import { DateTime } from 'luxon'; import { useMutation } from '@apollo/client'; import { useTranslation } from 'react-i18next'; @@ -306,32 +305,6 @@ export const OperationScreen = ({ setEditingNode({ ...node, action: EnumInputAction.CREATE, original: node }); setSelected('-1'); break; - case EnumInputActionScreen.UPDATE: - if (!selectedNode || selectedNode.id === 0) { - !selectedNode && setSelected(''); - return; - } - data?.menu.meta?.forEach(m => { - const defaultValue = (selectedNode.meta || {})[m.id] || m.defaultValue; - switch (m.type) { - case MenuMetaType.TEXT: - case MenuMetaType.NUMBER: - case MenuMetaType.DATE: - meta[m.name] = defaultValue || ''; - break; - case MenuMetaType.BOOLEAN: - meta[m.name] = defaultValue || false; - break; - } - }); - setEditingNode({ - ...selectedNode, - meta, - action: EnumInputAction.UPDATE, - original: selectedNode, - }); - setSelected(selectedNode.id.toString()); - break; } setOperationScreen(action); }, @@ -554,28 +527,6 @@ export const OperationScreen = ({ {t('buttons.insert')} - ); diff --git a/src/pages/Menu/Items/Items.tsx b/src/pages/Menu/Items/Items.tsx index b196b7b..aa8a9d7 100644 --- a/src/pages/Menu/Items/Items.tsx +++ b/src/pages/Menu/Items/Items.tsx @@ -542,6 +542,8 @@ export const ItemsPreview = () => { emptyEditingNode={emptyEditingNode} setEditingNode={setEditingNode} handleUpdate={handleUpdate} + data={data} + setOperationScreen={setOperationScreen} /> From 279b29b37d9948d623b7912418f4ac5f9778ed05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20Feij=C3=B3=20de=20Almeida=20Pereira=20Lima?= Date: Mon, 11 Sep 2023 20:04:12 -0300 Subject: [PATCH 10/23] fix: style --- src/pages/Menu/Edit/Edit.tsx | 4 ++-- src/pages/Menu/Items/Items.tsx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pages/Menu/Edit/Edit.tsx b/src/pages/Menu/Edit/Edit.tsx index 183d081..b5389cd 100644 --- a/src/pages/Menu/Edit/Edit.tsx +++ b/src/pages/Menu/Edit/Edit.tsx @@ -260,7 +260,7 @@ export const EditMenu = () => { - + @@ -301,7 +301,7 @@ export const EditMenu = () => { action={FormAction.UPDATE} /> - + diff --git a/src/pages/Menu/Items/Items.tsx b/src/pages/Menu/Items/Items.tsx index aa8a9d7..d9a2c21 100644 --- a/src/pages/Menu/Items/Items.tsx +++ b/src/pages/Menu/Items/Items.tsx @@ -467,11 +467,11 @@ export const ItemsPreview = () => { ); return ( - + Date: Mon, 11 Sep 2023 21:21:00 -0300 Subject: [PATCH 11/23] feat: menu item insert child from context menu --- src/components/Menu/Items/NodeTreeView.tsx | 51 ++++++++++++++++++++-- 1 file changed, 48 insertions(+), 3 deletions(-) diff --git a/src/components/Menu/Items/NodeTreeView.tsx b/src/components/Menu/Items/NodeTreeView.tsx index 7f97f07..adcb173 100644 --- a/src/components/Menu/Items/NodeTreeView.tsx +++ b/src/components/Menu/Items/NodeTreeView.tsx @@ -15,6 +15,7 @@ interface CustomTreeItemProps { renderNodes: (nodes: INode[]) => JSX.Element[]; setSelected: (selected: string) => void; emptyEditingNode: IEditingNode; + editingNode: IEditingNode; setEditingNode: (editingNode: IEditingNode) => void; handleUpdate: () => Promise; data: any; @@ -28,6 +29,7 @@ const CustomTreeItem = ({ renderNodes, setSelected, emptyEditingNode, + editingNode, setEditingNode, handleUpdate, data, @@ -62,6 +64,42 @@ const CustomTreeItem = ({ setAnchorEl(null); }; + const handleInsert = (event: React.SyntheticEvent) => { + event.stopPropagation(); + event.preventDefault(); + setAnchorEl(null); + const itemMeta = { ...meta }; + data?.menu.meta?.forEach(m => { + const defaultValue = (meta || {})[m.id] || m.defaultValue; + switch (m.type) { + case MenuMetaType.TEXT: + case MenuMetaType.NUMBER: + case MenuMetaType.DATE: + itemMeta[m.name] = defaultValue || ''; + break; + case MenuMetaType.BOOLEAN: + itemMeta[m.name] = defaultValue || false; + break; + } + }); + const itemNode = { + id: -1, + label: t('menu.preview.newItem', { + order: children?.length ? children.length + 1 : 1, + }), + order: children?.length ? children.length + 1 : 1, + parentId: id, + meta: itemMeta, + enabled: true, + children: [], + startPublication: null, + endPublication: null, + }; + setEditingNode({ ...itemNode, action: EnumInputAction.CREATE, original: itemNode }); + setSelected('-1'); + setOperationScreen(EnumInputActionScreen.INSERT); + }; + const handleDelete = async (event: React.SyntheticEvent) => { event.stopPropagation(); event.preventDefault(); @@ -101,8 +139,14 @@ const CustomTreeItem = ({ }; const onNodeClick = (event: React.SyntheticEvent) => { + event.stopPropagation(); + event.preventDefault(); if (id === 0) { - !node && setSelected(''); + setSelected(''); + return; + } + if (editingNode.id === -1 || id === -1) { + setSelected('-1'); return; } const itemMeta = { ...meta }; @@ -156,7 +200,7 @@ const CustomTreeItem = ({ }} > - + {t('menu.preview.actions.insertChild')} @@ -246,6 +290,7 @@ export const NodeTreeView = ({ renderNodes={renderNodes} setSelected={setSelected} emptyEditingNode={emptyEditingNode} + editingNode={editingNode} setEditingNode={setEditingNode} handleUpdate={handleUpdate} data={data} @@ -254,8 +299,8 @@ export const NodeTreeView = ({ ); }), [ - editingNode.id, emptyEditingNode, + editingNode, handleUpdate, setEditingNode, setSelected, From 54bcf73244ca40472c736dfa39b32bfa8847785b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20Feij=C3=B3=20de=20Almeida=20Pereira=20Lima?= Date: Mon, 11 Sep 2023 21:33:28 -0300 Subject: [PATCH 12/23] feat: removing root from tree view --- src/components/Menu/Items/OperationScreen.tsx | 10 ---------- src/pages/Menu/Items/Items.tsx | 13 ++----------- 2 files changed, 2 insertions(+), 21 deletions(-) diff --git a/src/components/Menu/Items/OperationScreen.tsx b/src/components/Menu/Items/OperationScreen.tsx index 9dd173a..306da51 100644 --- a/src/components/Menu/Items/OperationScreen.tsx +++ b/src/components/Menu/Items/OperationScreen.tsx @@ -868,16 +868,6 @@ export const OperationScreen = ({ > {t('menu.preview.actions.edit')} - { }) .filter(item => !item.parentId) .sort((a, b) => a.order - b.order) || []; - return [ - { - id: 0, - label: t('menu.preview.root'), - order: 1, - enabled: true, - children: root, - meta: {}, - }, - ]; - }, [updatedMenu, data?.menu, t]); + return root; + }, [updatedMenu, data?.menu]); const [editingNode, setEditingNode] = useState(emptyEditingNode); From ee66dd198b27eed4320aeda0f738d346d38cd14c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20Feij=C3=B3=20de=20Almeida=20Pereira=20Lima?= Date: Mon, 11 Sep 2023 23:19:50 -0300 Subject: [PATCH 13/23] feat: insert menu item root --- src/components/Icons/IconPlus.tsx | 29 ++++++++++++++++++++++ src/components/Menu/Items/NodeTreeView.tsx | 26 ++++++++++++++++++- src/i18n/locales/en/translation.json | 3 ++- src/i18n/locales/pt-BR/translation.json | 3 ++- 4 files changed, 58 insertions(+), 3 deletions(-) create mode 100644 src/components/Icons/IconPlus.tsx diff --git a/src/components/Icons/IconPlus.tsx b/src/components/Icons/IconPlus.tsx new file mode 100644 index 0000000..3a078a7 --- /dev/null +++ b/src/components/Icons/IconPlus.tsx @@ -0,0 +1,29 @@ +import React from 'react'; + +interface IconPlusProps { + fill?: string; + width?: string; + height?: string; + className?: string; +} + +export const IconPlus = ({ + fill = '#000', + width = '20', + height = '20', + className = '', +}: IconPlusProps) => ( + + + +); diff --git a/src/components/Menu/Items/NodeTreeView.tsx b/src/components/Menu/Items/NodeTreeView.tsx index adcb173..e651e1b 100644 --- a/src/components/Menu/Items/NodeTreeView.tsx +++ b/src/components/Menu/Items/NodeTreeView.tsx @@ -1,5 +1,14 @@ import React from 'react'; -import { Box, Divider, IconButton, ListItemText, Menu, MenuItem, MenuList } from '@mui/material'; +import { + Box, + Divider, + IconButton, + ListItemText, + Menu, + MenuItem, + MenuList, + Typography, +} from '@mui/material'; import { TreeItem, TreeView } from '@mui/lab'; import ArrowForwardIosIcon from '@mui/icons-material/ArrowForwardIos'; import MoreVertIcon from '@mui/icons-material/MoreVert'; @@ -7,6 +16,7 @@ import { useTranslation } from 'react-i18next'; import { useNavigate, useParams } from 'react-router-dom'; import { EnumInputAction, IEditingNode, INode, MenuMetaType } from '../../../types'; import { EnumInputActionScreen } from '../../../pages/Menu/Items'; +import { IconPlus } from '../../Icons/IconPlus'; interface CustomTreeItemProps { node: INode; @@ -43,6 +53,7 @@ const CustomTreeItem = ({ backgroundColor: '#fff', borderRadius: '4px', padding: '12px 0px 13px 25px', + my: '0.5rem', }; const { id: menuId } = useParams(); @@ -257,6 +268,8 @@ export const NodeTreeView = ({ data, setOperationScreen, }: Props) => { + const { t } = useTranslation(); + const handleToggle = (event: React.SyntheticEvent, nodeIds: string[]) => { setExpanded(nodeIds); }; @@ -319,6 +332,17 @@ export const NodeTreeView = ({ overflowY: 'auto', }} > + + + + {t('menu.preview.actions.insertRoot')} + + } defaultCollapseIcon={} diff --git a/src/i18n/locales/en/translation.json b/src/i18n/locales/en/translation.json index 1df03f3..97ee0a0 100644 --- a/src/i18n/locales/en/translation.json +++ b/src/i18n/locales/en/translation.json @@ -260,7 +260,8 @@ "edit": "Edit", "delete": "Delete", "editTemplate": "Edit Template", - "insertChild": "Insert Child" + "insertChild": "Insert Child", + "insertRoot": "New Menu Item" }, "parent": "Parent", "newItem": "New Item {{order}}", diff --git a/src/i18n/locales/pt-BR/translation.json b/src/i18n/locales/pt-BR/translation.json index 02041b7..2e1a5ab 100644 --- a/src/i18n/locales/pt-BR/translation.json +++ b/src/i18n/locales/pt-BR/translation.json @@ -261,7 +261,8 @@ "edit": "Editar", "delete": "Deletar", "editTemplate": "Editar Template", - "insertChild": "Inserir Item Filho" + "insertChild": "Inserir Item Filho", + "insertRoot": "Novo Item de Menu" }, "parent": "Item Pai", "newItem": "Novo Item {{order}}", From 1041443dbd4f5285b5914883e497b877edda3c3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20Feij=C3=B3=20de=20Almeida=20Pereira=20Lima?= Date: Tue, 12 Sep 2023 17:36:36 -0300 Subject: [PATCH 14/23] feat: insert menu item from treeview --- src/components/Menu/Items/NodeTreeView.tsx | 35 +++- src/components/Menu/Items/OperationScreen.tsx | 174 +----------------- src/pages/Menu/Items/Items.tsx | 9 +- 3 files changed, 38 insertions(+), 180 deletions(-) diff --git a/src/components/Menu/Items/NodeTreeView.tsx b/src/components/Menu/Items/NodeTreeView.tsx index e651e1b..95786f2 100644 --- a/src/components/Menu/Items/NodeTreeView.tsx +++ b/src/components/Menu/Items/NodeTreeView.tsx @@ -322,6 +322,38 @@ export const NodeTreeView = ({ ], ); + const handleInsertRoot = (event: React.SyntheticEvent) => { + const meta = {}; + data?.menu.meta?.forEach(m => { + switch (m.type) { + case MenuMetaType.TEXT: + case MenuMetaType.NUMBER: + case MenuMetaType.DATE: + meta[m.name] = m.defaultValue || ''; + break; + case MenuMetaType.BOOLEAN: + meta[m.name] = m.defaultValue || false; + break; + } + }); + const node: INode = { + id: -1, + label: t('menu.preview.newItem', { + order: nodes.length ? nodes.length + 1 : 1, + }), + order: nodes.length ? nodes.length + 1 : 1, + parentId: 0, + meta, + enabled: true, + children: [], + startPublication: null, + endPublication: null, + }; + setEditingNode({ ...node, action: EnumInputAction.CREATE, original: node }); + setSelected('-1'); + setOperationScreen(EnumInputActionScreen.INSERT); + }; + return ( void; findNodeById: (nodes: INode[], id: number) => INode | undefined; - preview: (nodes: INode[], editingNode: IEditingNode) => INode[]; - setUpdatedMenu: (menu: IMenu) => void; handleUpdate: () => Promise; } @@ -73,142 +64,15 @@ export const OperationScreen = ({ operationScreen, setOperationScreen, findNodeById, - preview, setEditingNode, - setUpdatedMenu, + handleUpdate, }: Props) => { - const { dispatch } = React.useContext(NotificationContext); - const { i18n, t } = useTranslation(); const [labelError, setLabelError] = React.useState(''); const [startPublicationError, setStartPublicationError] = React.useState(''); const [endPublicationError, setEndPublicationError] = React.useState(''); - const [updateMenu] = useMutation(MenuService.UPDATE_MENU); - - const handleUpdate = async (): Promise => { - const formatNodes = (nodes: INode[]) => - nodes - .map(node => { - const { - id, - children, - original, - parentId, - createdAt, - updatedAt, - version, - menuId, - defaultTemplate, - action, - rules, - ...rest - } = node; - let meta; - if (rest.meta) { - meta = Object.keys(rest.meta).reduce((acc, key) => { - const name = data.menu.meta.find(meta => meta.id === Number(key))?.name || key; - const meta = rest.meta[key]; - const originalMetaMap = original?.meta || {}; - const originalMeta = originalMetaMap[key] || originalMetaMap[name]; - if (meta == null || meta === '' || meta === originalMeta) { - return acc; - } - return { - ...acc, - [name]: meta, - }; - }, {}); - if (Object.keys(meta).length === 0) meta = undefined; - } - let startPublication; - if (rest.startPublication) { - if ( - (original?.startPublication !== rest.startPublication || - !original?.startPublication) && - rest.startPublication.isValid - ) - startPublication = rest.startPublication.toISO(); - } else { - startPublication = null; - } - let endPublication; - if (rest.endPublication) { - if ( - (original?.endPublication !== rest.endPublication || !original?.endPublication) && - rest.endPublication.isValid - ) - endPublication = rest.endPublication.toISO(); - } else { - endPublication = null; - } - let label; - if (action === EnumInputAction.CREATE) label = rest.label; - else if (rest.label && (original?.label !== rest.label || !original?.label)) - label = rest.label; - let order; - if (action === EnumInputAction.CREATE) order = rest.order; - else if (rest.order && (original?.order !== rest.order || !original?.order)) - order = rest.order; - let enabled = action === EnumInputAction.CREATE ? true : undefined; - if (rest.enabled && (original?.enabled !== rest.enabled || !original?.enabled)) - enabled = rest.enabled; - return { - action, - label, - order, - enabled, - startPublication, - endPublication, - meta, - rules, - children: children && formatNodes(children), - id: id === -1 ? undefined : id, - }; - }) - .filter(node => !!node.action); - - const items = formatNodes(preview(nodes, editingNode)[0].children).map(node => { - const id = node.id === -1 ? undefined : node.id; - return { - ...node, - id, - }; - }); - - await updateMenu({ - variables: { menu: { id: Number(id), items } }, - onCompleted: data => { - dispatch({ - type: ActionTypes.OPEN_NOTIFICATION, - message: `${t('notification.editSuccess', { - resource: t('menu.title', { count: 1 }), - context: 'male', - })}!`, - }); - switch (editingNode.action) { - case EnumInputAction.CREATE: - case EnumInputAction.DELETE: - setSelected(''); - break; - case EnumInputAction.UPDATE: - setSelected(editingNode.id.toString()); - break; - } - setUpdatedMenu(data.updateMenu); - setOperationScreen(EnumInputActionScreen.SELECTING_ACTION); - setEditingNode(emptyEditingNode); - return Promise.resolve(); - }, - onError: error => { - openDefaultErrorNotification(error, dispatch); - return Promise.reject(); - }, - }); - return Promise.resolve(); - }; - const handleInsertSubmit = async (e: React.FormEvent) => { e.preventDefault(); e.stopPropagation(); @@ -547,42 +411,6 @@ export const OperationScreen = ({ > {t('menu.preview.actions.insert')} - - - - { }) .filter(node => !!node.action); - const items = formatNodes(preview(nodes, editingNode)[0].children).map(node => { + const items = formatNodes(preview(nodes, editingNode)).map(node => { const id = node.id === -1 ? undefined : node.id; return { ...node, @@ -262,8 +262,7 @@ export const ItemsPreview = () => { const preview = useCallback((nodes: INode[], editingNode: IEditingNode): INode[] => { if (!editingNode.id) return nodes; - if (nodes[0].id === 0 && nodes[0].children?.length === 0) - return [{ ...nodes[0], children: [editingNode] }]; + if (nodes.length === 0) return [editingNode]; // console.log('nodes', nodes); // console.log('editing node', editingNode); const nodesPreview: INode[] = []; @@ -433,7 +432,7 @@ export const ItemsPreview = () => { } } }); - // console.log('nodes preview', nodesPreview); + console.log('nodes preview', nodesPreview, editingNode); return nodesPreview; }, []); @@ -559,8 +558,6 @@ export const ItemsPreview = () => { selected={selected} setSelected={setSelected} findNodeById={findNodeById} - preview={preview} - setUpdatedMenu={setUpdatedMenu} operationScreen={operationScreen} setOperationScreen={setOperationScreen} handleUpdate={handleUpdate} From 66cba38974e8fddef2e04da06ad9d0c03c19f8bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20Feij=C3=B3=20de=20Almeida=20Pereira=20Lima?= Date: Tue, 12 Sep 2023 18:27:22 -0300 Subject: [PATCH 15/23] fix: insert item preview --- src/components/Menu/Items/OperationScreen.tsx | 22 +- src/pages/Menu/Items/Items.tsx | 195 ++++++++++-------- 2 files changed, 122 insertions(+), 95 deletions(-) diff --git a/src/components/Menu/Items/OperationScreen.tsx b/src/components/Menu/Items/OperationScreen.tsx index 6f3390f..f05859a 100644 --- a/src/components/Menu/Items/OperationScreen.tsx +++ b/src/components/Menu/Items/OperationScreen.tsx @@ -440,10 +440,13 @@ export const OperationScreen = ({ value={editingNode.order} onChange={e => { const parent = findNodeById(nodes, editingNode.parentId); - const order = Math.min( - Math.max(Number(e.target.value), 1), - parent.children?.length ? parent.children.length + 1 : 1, - ); + let maxOrder = 1; + if (parent?.children?.length) { + maxOrder = parent.children.length + 1; + } else if (nodes.length) { + maxOrder = nodes.length + 1; + } + const order = Math.min(Math.max(Number(e.target.value), 1), maxOrder); if (order === editingNode.order) return; setEditingNode({ ...editingNode, @@ -725,10 +728,13 @@ export const OperationScreen = ({ value={editingNode.order} onChange={e => { const parent = findNodeById(nodes, editingNode.parentId); - const order = Math.min( - Math.max(Number(e.target.value), 1), - parent.children?.length ? parent.children.length : 1, - ); + let maxOrder = 1; + if (parent?.children?.length) { + maxOrder = parent.children.length; + } else if (nodes.length) { + maxOrder = nodes.length; + } + const order = Math.min(Math.max(Number(e.target.value), 1), maxOrder); if (order === editingNode.order) return; setEditingNode({ ...editingNode, diff --git a/src/pages/Menu/Items/Items.tsx b/src/pages/Menu/Items/Items.tsx index 13b594f..fd13a5f 100644 --- a/src/pages/Menu/Items/Items.tsx +++ b/src/pages/Menu/Items/Items.tsx @@ -274,6 +274,109 @@ export const ItemsPreview = () => { return { ...child, action: EnumInputAction.DELETE }; }); }; + const reorder = (nodes: INode[]): INode[] => { + const siblings = nodes.map(sibling => { + if (sibling.id === editingNode.id) { + // sibling is editing node + // console.log('child is editing node', child); + if (sibling.order === editingNode.order) { + // sibling is in same position + // console.log('sibling is in same position'); + if (editingNode.action === EnumInputAction.DELETE) { + // editing node is DELETE + // console.log('editing node is DELETE'); + // set sibling as DELETE + return { + ...sibling, + action: EnumInputAction.DELETE, + children: deleteChildren(sibling), + }; + } + // editing node is UPDATE + // console.log('editing node is UPDATE'); + // set sibling as UPDATE + return { + ...sibling, + ...editingNode, + action: EnumInputAction.UPDATE, + }; + } + // sibling is in different position + // console.log('sibling is in different position'); + // set sibling as UPDATE and set order as editing node order + return { + ...sibling, + ...editingNode, + action: EnumInputAction.UPDATE, + }; + } + // sibling is not editing node + if (sibling.order === editingNode.order) { + // sibling is in same position as editing node + // console.log('sibling is in same position as editing node', sibling); + // set order based on existing node order diff (moving up or down) + const diff = editingNode.order - editingNode.original.order; + let order = diff >= 0 ? sibling.order - 1 : sibling.order + 1; + if (editingNode.action === EnumInputAction.DELETE) { + // editing node is DELETE + // console.log('editing node is DELETE'); + // decrease order + order = sibling.order - 1; + } + // set sibling as UPDATE and decrease order + return { ...sibling, action: EnumInputAction.UPDATE, order }; + } + if (sibling.order > editingNode.order) { + // sibling is after editing node + // console.log('sibling is after editing node', sibling); + // set sibling as UPDATE + let action = EnumInputAction.UPDATE; + let { order } = sibling; + if (editingNode.action === EnumInputAction.UPDATE) { + if (sibling.order < editingNode.original.order) { + // sibling order is less than editing node original order + // increase sibling order + order += 1; + } else { + // sibling order is greater than editing node original order + // keep sibling order as is and set action as undefined + action = undefined; + } + } else if (editingNode.action === EnumInputAction.DELETE) { + order -= 1; + } else if (editingNode.action === EnumInputAction.CREATE) { + order += 1; + } + return { ...sibling, order, action }; + } + // sibling is before editing node + // console.log('sibling is before editing node', sibling); + if ( + editingNode.action === EnumInputAction.UPDATE && + sibling.order > editingNode.original.order + ) { + // sibling order is greater than editing node original order + // decrease sibling order + return { ...sibling, action: EnumInputAction.UPDATE, order: sibling.order - 1 }; + } + return sibling; + }); + return siblings; + }; + if (editingNode.parentId === 0) { + // editing node is root + // console.log('editing node is root'); + // reordering root + let root = reorder(nodes); + if (editingNode.action === EnumInputAction.CREATE) { + // editing node is CREATE + // console.log('editing node is CREATE'); + // add editing node to root + root.splice(editingNode.order - 1, 0, editingNode); + } + root = root.sort((a, b) => a.order - b.order); + return root; + } nodes.forEach(node => { // console.log('current node', node); if (node.id === editingNode.id) { @@ -311,92 +414,10 @@ export const ItemsPreview = () => { action: EnumInputAction.UPDATE, }); } else { - let children = node.children.map(child => { - if (child.id === editingNode.id) { - // child is editing node - // console.log('child is editing node', child); - if (child.order === editingNode.order) { - // child is in same position - // console.log('child is in same position'); - if (editingNode.action === EnumInputAction.DELETE) { - // editing node is DELETE - // console.log('editing node is DELETE'); - // set child as DELETE - return { - ...child, - action: EnumInputAction.DELETE, - children: deleteChildren(child), - }; - } - // editing node is UPDATE - // console.log('editing node is UPDATE'); - // set child as UPDATE - return { - ...child, - ...editingNode, - action: EnumInputAction.UPDATE, - }; - } - // child is in different position - // console.log('child is in different position'); - // set child as UPDATE and set order as editing node order - return { - ...child, - ...editingNode, - action: EnumInputAction.UPDATE, - }; - } - // child is not editing node - if (child.order === editingNode.order) { - // child is in same position as editing node - // console.log('child is in same position as editing node', child); - // set order based on existing node order diff (moving up or down) - const diff = editingNode.order - editingNode.original.order; - let order = diff >= 0 ? child.order - 1 : child.order + 1; - if (editingNode.action === EnumInputAction.DELETE) { - // editing node is DELETE - // console.log('editing node is DELETE'); - // decrease order - order = child.order - 1; - } - // set child as UPDATE and decrease order - return { ...child, action: EnumInputAction.UPDATE, order }; - } - if (child.order > editingNode.order) { - // child is after editing node - // console.log('child is after editing node', child); - // set child as UPDATE - let action = EnumInputAction.UPDATE; - let { order } = child; - if (editingNode.action === EnumInputAction.UPDATE) { - if (child.order < editingNode.original.order) { - // child order is less than editing node original order - // increase child order - order += 1; - } else { - // child order is greater than editing node original order - // keep child order as is and set action as undefined - action = undefined; - } - } else if (editingNode.action === EnumInputAction.DELETE) { - order -= 1; - } else if (editingNode.action === EnumInputAction.CREATE) { - order += 1; - } - return { ...child, order, action }; - } - // child is before editing node - // console.log('child is before editing node', child); - if ( - editingNode.action === EnumInputAction.UPDATE && - child.order > editingNode.original.order - ) { - // child order is greater than editing node original order - // decrease child order - return { ...child, action: EnumInputAction.UPDATE, order: child.order - 1 }; - } - return child; - }); + // parent has children + // console.log('parent has children'); + // reordering children + let children = reorder(node.children); if (editingNode.action === EnumInputAction.CREATE) { // editing node is CREATE // console.log('editing node is CREATE'); @@ -432,7 +453,7 @@ export const ItemsPreview = () => { } } }); - console.log('nodes preview', nodesPreview, editingNode); + // console.log('nodes preview', nodesPreview); return nodesPreview; }, []); From 6226f9efcb3d9e04100eee336b5cbe8e10de9942 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20Feij=C3=B3=20de=20Almeida=20Pereira=20Lima?= Date: Tue, 12 Sep 2023 18:38:00 -0300 Subject: [PATCH 16/23] refactor: removing unused code --- src/components/Menu/Items/OperationScreen.tsx | 141 +----------------- src/pages/Menu/Items/Items.tsx | 6 +- 2 files changed, 6 insertions(+), 141 deletions(-) diff --git a/src/components/Menu/Items/OperationScreen.tsx b/src/components/Menu/Items/OperationScreen.tsx index f05859a..51f9dd0 100644 --- a/src/components/Menu/Items/OperationScreen.tsx +++ b/src/components/Menu/Items/OperationScreen.tsx @@ -11,17 +11,9 @@ import { } from '@mui/material'; import { AdapterLuxon } from '@mui/x-date-pickers/AdapterLuxon'; import { DatePicker, DateTimePicker, LocalizationProvider } from '@mui/x-date-pickers'; -import AddIcon from '@mui/icons-material/Add'; import { DateTime } from 'luxon'; import { useTranslation } from 'react-i18next'; -import { - EnumInputAction, - IEditingNode, - IMenu, - IMenuMeta, - INode, - MenuMetaType, -} from '../../../types'; +import { IEditingNode, IMenu, IMenuMeta, INode, MenuMetaType } from '../../../types'; import { MENU_ITEM_VALIDATION } from '../../../constants'; import { EnumInputActionScreen } from '../../../pages/Menu/Items'; @@ -128,65 +120,6 @@ export const OperationScreen = ({ } }; - const handleActionChange = React.useCallback( - (action: EnumInputActionScreen) => { - const selectedNode = findNodeById(nodes, Number(selected)); - if (expanded.find(id => id === selectedNode?.id.toString())) { - setExpanded([...expanded, selectedNode.id.toString()]); - } - let node: INode; - const meta = {}; - switch (action) { - case EnumInputActionScreen.SELECTING_ACTION: - setEditingNode(emptyEditingNode); - break; - case EnumInputActionScreen.INSERT: - data?.menu.meta?.forEach(m => { - switch (m.type) { - case MenuMetaType.TEXT: - case MenuMetaType.NUMBER: - case MenuMetaType.DATE: - meta[m.name] = m.defaultValue || ''; - break; - case MenuMetaType.BOOLEAN: - meta[m.name] = m.defaultValue || false; - break; - } - }); - node = { - id: -1, - label: t('menu.preview.newItem', { - order: selectedNode.children?.length ? selectedNode.children.length + 1 : 1, - }), - order: selectedNode.children?.length ? selectedNode.children.length + 1 : 1, - parentId: selectedNode.id, - meta, - enabled: true, - children: [], - startPublication: null, - endPublication: null, - }; - setEditingNode({ ...node, action: EnumInputAction.CREATE, original: node }); - setSelected('-1'); - break; - } - setOperationScreen(action); - }, - [ - data, - findNodeById, - nodes, - selected, - t, - expanded, - emptyEditingNode, - setEditingNode, - setExpanded, - setSelected, - setOperationScreen, - ], - ); - const renderMeta = () => { const renderInput = (meta: IMenuMeta) => { switch (meta.type) { @@ -326,74 +259,6 @@ export const OperationScreen = ({ }; switch (operationScreen) { - case EnumInputActionScreen.SELECTING_ACTION: - return ( - - - {t('menu.preview.actions.title')} - - {!selected && ( - - {t('menu.preview.errors.noItemSelected')} - - )} - - - - - ); case EnumInputActionScreen.INSERT: return (
@@ -675,7 +540,7 @@ export const OperationScreen = ({ variant="contained" color="error" sx={{ mt: '2rem' }} - onClick={() => handleActionChange(EnumInputActionScreen.SELECTING_ACTION)} + onClick={() => setEditingNode(emptyEditingNode)} > {t('buttons.discard')} @@ -963,7 +828,7 @@ export const OperationScreen = ({ variant="contained" color="error" sx={{ mt: '2rem' }} - onClick={() => handleActionChange(EnumInputActionScreen.SELECTING_ACTION)} + onClick={() => setEditingNode(emptyEditingNode)} > {t('buttons.discard')} diff --git a/src/pages/Menu/Items/Items.tsx b/src/pages/Menu/Items/Items.tsx index fd13a5f..649aef4 100644 --- a/src/pages/Menu/Items/Items.tsx +++ b/src/pages/Menu/Items/Items.tsx @@ -44,7 +44,7 @@ const emptyEditingNode: IEditingNode = { }; export enum EnumInputActionScreen { - SELECTING_ACTION, + NONE, INSERT, UPDATE, } @@ -67,7 +67,7 @@ export const ItemsPreview = () => { const [selected, setSelected] = useState('0'); const [operationScreen, setOperationScreen] = React.useState( - EnumInputActionScreen.SELECTING_ACTION, + EnumInputActionScreen.NONE, ); const nodes = useMemo(() => { @@ -248,7 +248,7 @@ export const ItemsPreview = () => { break; } setUpdatedMenu(data.updateMenu); - setOperationScreen(EnumInputActionScreen.SELECTING_ACTION); + setOperationScreen(EnumInputActionScreen.NONE); setEditingNode(emptyEditingNode); return Promise.resolve(); }, From f7b45738749ad36d3fa0cd49af223c54475ec9d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20Feij=C3=B3=20de=20Almeida=20Pereira=20Lima?= Date: Tue, 12 Sep 2023 18:44:52 -0300 Subject: [PATCH 17/23] fix: handle discard --- src/components/Menu/Items/OperationScreen.tsx | 27 +++------- src/pages/Menu/Items/Items.tsx | 49 +++++++++---------- 2 files changed, 31 insertions(+), 45 deletions(-) diff --git a/src/components/Menu/Items/OperationScreen.tsx b/src/components/Menu/Items/OperationScreen.tsx index 51f9dd0..230ca18 100644 --- a/src/components/Menu/Items/OperationScreen.tsx +++ b/src/components/Menu/Items/OperationScreen.tsx @@ -33,10 +33,6 @@ interface Props { emptyEditingNode: IEditingNode; editingNode: IEditingNode; setEditingNode: (editingNode: IEditingNode) => void; - expanded: string[]; - setExpanded: (expanded: string[]) => void; - selected: string; - setSelected: (selected: string) => void; operationScreen: EnumInputActionScreen; setOperationScreen: (operationScreen: EnumInputActionScreen) => void; findNodeById: (nodes: INode[], id: number) => INode | undefined; @@ -49,10 +45,6 @@ export const OperationScreen = ({ nodes, editingNode, emptyEditingNode, - expanded, - setExpanded, - selected, - setSelected, operationScreen, setOperationScreen, findNodeById, @@ -120,6 +112,11 @@ export const OperationScreen = ({ } }; + const handleDiscard = () => { + setEditingNode(emptyEditingNode); + setOperationScreen(EnumInputActionScreen.NONE); + }; + const renderMeta = () => { const renderInput = (meta: IMenuMeta) => { switch (meta.type) { @@ -536,12 +533,7 @@ export const OperationScreen = ({ > {t('buttons.save')} - @@ -824,12 +816,7 @@ export const OperationScreen = ({ > {t('buttons.save')} - diff --git a/src/pages/Menu/Items/Items.tsx b/src/pages/Menu/Items/Items.tsx index 649aef4..48bdcfb 100644 --- a/src/pages/Menu/Items/Items.tsx +++ b/src/pages/Menu/Items/Items.tsx @@ -557,32 +557,31 @@ export const ItemsPreview = () => { setOperationScreen={setOperationScreen} /> + {operationScreen !== EnumInputActionScreen.NONE ? ( + <> +
-
- - + + + ) : null} From 2647d319e931f5d824bde27eb186354d213c1c83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20Feij=C3=B3=20de=20Almeida=20Pereira=20Lima?= Date: Tue, 12 Sep 2023 19:33:15 -0300 Subject: [PATCH 18/23] style: tree item max width --- src/components/Menu/Items/NodeTreeView.tsx | 25 ++++++++++++---------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/src/components/Menu/Items/NodeTreeView.tsx b/src/components/Menu/Items/NodeTreeView.tsx index 95786f2..b959a42 100644 --- a/src/components/Menu/Items/NodeTreeView.tsx +++ b/src/components/Menu/Items/NodeTreeView.tsx @@ -47,15 +47,6 @@ const CustomTreeItem = ({ }: CustomTreeItemProps) => { const { id, label, children, meta } = node; - const baseSX = { - border: '1px solid #eaeaec', - margin: '3px 0px', - backgroundColor: '#fff', - borderRadius: '4px', - padding: '12px 0px 13px 25px', - my: '0.5rem', - }; - const { id: menuId } = useParams(); const navigate = useNavigate(); const { t } = useTranslation(); @@ -226,9 +217,15 @@ const CustomTreeItem = ({ } sx={{ '& > .MuiTreeItem-content': { - ...baseSX, color, fontWeight, + border: '1px solid #eaeaec', + margin: '3px 0px', + backgroundColor: '#fff', + borderRadius: '4px', + padding: '12px 0px 13px 25px', + my: '0.5rem', + maxWidth: '540px', }, }} > @@ -366,7 +363,13 @@ export const NodeTreeView = ({ > From 3b405b1028a611822c7406595d277e5ca5ca98c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20Feij=C3=B3=20de=20Almeida=20Pereira=20Lima?= Date: Tue, 12 Sep 2023 19:57:18 -0300 Subject: [PATCH 19/23] style: tree item selected --- src/components/Menu/Items/NodeTreeView.tsx | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/components/Menu/Items/NodeTreeView.tsx b/src/components/Menu/Items/NodeTreeView.tsx index b959a42..6f190f6 100644 --- a/src/components/Menu/Items/NodeTreeView.tsx +++ b/src/components/Menu/Items/NodeTreeView.tsx @@ -225,8 +225,21 @@ const CustomTreeItem = ({ borderRadius: '4px', padding: '12px 0px 13px 25px', my: '0.5rem', - maxWidth: '540px', }, + '& > .Mui-selected': { + border: '2px solid #3354FD', + backgroundColor: '#fff', + '&:hover': { + backgroundColor: 'rgba(0, 0, 0, 0.04)', + }, + }, + '& > .Mui-selected.Mui-focused': { + backgroundColor: '#fff', + '&:hover': { + backgroundColor: 'rgba(0, 0, 0, 0.04)', + }, + }, + maxWidth: '540px', }} > {children?.length > 0 && renderNodes(children)} From 5e2a65b65c304316e8c5cbfec466254ebcd73d4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20Feij=C3=B3=20de=20Almeida=20Pereira=20Lima?= Date: Tue, 12 Sep 2023 20:01:41 -0300 Subject: [PATCH 20/23] fix: onNodeClick behavior when inserting item --- src/components/Menu/Items/NodeTreeView.tsx | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/components/Menu/Items/NodeTreeView.tsx b/src/components/Menu/Items/NodeTreeView.tsx index 6f190f6..d5ce165 100644 --- a/src/components/Menu/Items/NodeTreeView.tsx +++ b/src/components/Menu/Items/NodeTreeView.tsx @@ -105,10 +105,6 @@ const CustomTreeItem = ({ const handleDelete = async (event: React.SyntheticEvent) => { event.stopPropagation(); event.preventDefault(); - if (id === 0) { - setSelected(''); - return; - } setEditingNode({ ...node, action: EnumInputAction.DELETE, @@ -143,14 +139,6 @@ const CustomTreeItem = ({ const onNodeClick = (event: React.SyntheticEvent) => { event.stopPropagation(); event.preventDefault(); - if (id === 0) { - setSelected(''); - return; - } - if (editingNode.id === -1 || id === -1) { - setSelected('-1'); - return; - } const itemMeta = { ...meta }; data?.menu.meta?.forEach(m => { const defaultValue = (meta || {})[m.id] || m.defaultValue; From 6e6ec712ac92513daa3ff9a996252ef4f5e92c2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20Feij=C3=B3=20de=20Almeida=20Pereira=20Lima?= Date: Tue, 12 Sep 2023 20:07:30 -0300 Subject: [PATCH 21/23] refactor: renaming context menu related --- src/components/Menu/Items/NodeTreeView.tsx | 24 +++++++++++----------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/components/Menu/Items/NodeTreeView.tsx b/src/components/Menu/Items/NodeTreeView.tsx index d5ce165..b802f3f 100644 --- a/src/components/Menu/Items/NodeTreeView.tsx +++ b/src/components/Menu/Items/NodeTreeView.tsx @@ -51,25 +51,25 @@ const CustomTreeItem = ({ const navigate = useNavigate(); const { t } = useTranslation(); - const [anchorEl, setAnchorEl] = React.useState(null); + const [contextMenuRef, setContextMenuRef] = React.useState(null); const [confirmedDelete, setConfirmedDelete] = React.useState(false); - const handleClick = (event: React.SyntheticEvent) => { + const handleClickContextMenu = (event: React.SyntheticEvent) => { event.stopPropagation(); event.preventDefault(); - setAnchorEl(event.currentTarget as HTMLElement); + setContextMenuRef(event.currentTarget as HTMLElement); }; - const handleClose = (event: React.SyntheticEvent) => { + const handleCloseContextMenu = (event: React.SyntheticEvent) => { event.stopPropagation(); event.preventDefault(); - setAnchorEl(null); + setContextMenuRef(null); }; const handleInsert = (event: React.SyntheticEvent) => { event.stopPropagation(); event.preventDefault(); - setAnchorEl(null); + setContextMenuRef(null); const itemMeta = { ...meta }; data?.menu.meta?.forEach(m => { const defaultValue = (meta || {})[m.id] || m.defaultValue; @@ -110,7 +110,7 @@ const CustomTreeItem = ({ action: EnumInputAction.DELETE, original: node, }); - setAnchorEl(null); + setContextMenuRef(null); // eslint-disable-next-line no-promise-executor-return await new Promise(resolve => setTimeout(resolve, 100)); // eslint-disable-next-line no-alert @@ -133,7 +133,7 @@ const CustomTreeItem = ({ event.stopPropagation(); event.preventDefault(); navigate(`/menus/${menuId}/items/${id}`); - setAnchorEl(null); + setContextMenuRef(null); }; const onNodeClick = (event: React.SyntheticEvent) => { @@ -172,14 +172,14 @@ const CustomTreeItem = ({ {label} - + Date: Tue, 12 Sep 2023 20:46:19 -0300 Subject: [PATCH 22/23] feat: smooth scroll into inserting node --- src/components/Menu/Items/NodeTreeView.tsx | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/components/Menu/Items/NodeTreeView.tsx b/src/components/Menu/Items/NodeTreeView.tsx index b802f3f..e292484 100644 --- a/src/components/Menu/Items/NodeTreeView.tsx +++ b/src/components/Menu/Items/NodeTreeView.tsx @@ -25,11 +25,11 @@ interface CustomTreeItemProps { renderNodes: (nodes: INode[]) => JSX.Element[]; setSelected: (selected: string) => void; emptyEditingNode: IEditingNode; - editingNode: IEditingNode; setEditingNode: (editingNode: IEditingNode) => void; handleUpdate: () => Promise; data: any; setOperationScreen: (operationScreen: EnumInputActionScreen) => void; + insertingNodeRef?: (node: HTMLElement) => void; } const CustomTreeItem = ({ @@ -39,11 +39,11 @@ const CustomTreeItem = ({ renderNodes, setSelected, emptyEditingNode, - editingNode, setEditingNode, handleUpdate, data, setOperationScreen, + insertingNodeRef, }: CustomTreeItemProps) => { const { id, label, children, meta } = node; @@ -165,6 +165,7 @@ const CustomTreeItem = ({ return ( { const { t } = useTranslation(); + const insertingNodeRef = React.useCallback(node => { + if (node) { + node.scrollIntoView({ behavior: 'smooth' }); + } + }, []); + const handleToggle = (event: React.SyntheticEvent, nodeIds: string[]) => { setExpanded(nodeIds); }; @@ -301,11 +308,11 @@ export const NodeTreeView = ({ renderNodes={renderNodes} setSelected={setSelected} emptyEditingNode={emptyEditingNode} - editingNode={editingNode} setEditingNode={setEditingNode} handleUpdate={handleUpdate} data={data} setOperationScreen={setOperationScreen} + insertingNodeRef={insertingNodeRef} /> ); }), @@ -317,6 +324,7 @@ export const NodeTreeView = ({ setSelected, data, setOperationScreen, + insertingNodeRef, ], ); From 9c41e654438ef78c7de752ce86ebcfe2bff66047 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20Feij=C3=B3=20de=20Almeida=20Pereira=20Lima?= Date: Tue, 12 Sep 2023 20:54:45 -0300 Subject: [PATCH 23/23] fix: keeping inserting node selected when clicking other nodes --- src/components/Menu/Items/NodeTreeView.tsx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/components/Menu/Items/NodeTreeView.tsx b/src/components/Menu/Items/NodeTreeView.tsx index e292484..e9ce5c3 100644 --- a/src/components/Menu/Items/NodeTreeView.tsx +++ b/src/components/Menu/Items/NodeTreeView.tsx @@ -25,6 +25,7 @@ interface CustomTreeItemProps { renderNodes: (nodes: INode[]) => JSX.Element[]; setSelected: (selected: string) => void; emptyEditingNode: IEditingNode; + editingNode: IEditingNode; setEditingNode: (editingNode: IEditingNode) => void; handleUpdate: () => Promise; data: any; @@ -39,6 +40,7 @@ const CustomTreeItem = ({ renderNodes, setSelected, emptyEditingNode, + editingNode, setEditingNode, handleUpdate, data, @@ -139,6 +141,10 @@ const CustomTreeItem = ({ const onNodeClick = (event: React.SyntheticEvent) => { event.stopPropagation(); event.preventDefault(); + if (editingNode.id === -1) { + setSelected('-1'); + return; + } const itemMeta = { ...meta }; data?.menu.meta?.forEach(m => { const defaultValue = (meta || {})[m.id] || m.defaultValue; @@ -308,6 +314,7 @@ export const NodeTreeView = ({ renderNodes={renderNodes} setSelected={setSelected} emptyEditingNode={emptyEditingNode} + editingNode={editingNode} setEditingNode={setEditingNode} handleUpdate={handleUpdate} data={data}