diff --git a/client/src/App.tsx b/client/src/App.tsx index d50ba94..f46f1d8 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -14,7 +14,7 @@ import { SolutionsPage } from "./pages/SolutionsPage"; import { SkeletonCustom } from "./components/ui/skeleton/skeleton"; import BoardItemSocket from "./pages/boards/board-socket/board-item-socket.tsx"; import { t } from "i18next"; -// import SocketProvider from "./hooks/socket/socket-provider.tsx"; +import UpgradeModalContainer from "./components/ui/modals/UpgradeModalContainer.tsx"; const LoginPage = lazy(() => import("./pages/login/login").then((m) => ({ default: m.LoginPage })), @@ -40,202 +40,208 @@ const AuthRoute = ({ children, requireAuth, redirectTo }: AuthRouteProps) => { function App() { return ( - - {/* Public routes */} - }> - } /> - + <> + + {/* Public routes */} + }> + } /> + + + + + } + > + + + + } + /> + + + + } + /> + + + + + } + > + + + + } + /> + } > - + - - } - /> - - - - } - /> - + } + /> + } > - + - - } - /> - - - - } - > - - - } - /> - - - - } - > - - - } - /> - } /> - } /> - } /> - } /> - } /> - + } + /> + } /> + } /> + } /> + } /> + } /> + - {/* Backoffice routes */} - - - - } - > - - - - } - > - - - } - /> + {/* Backoffice routes */} - - - } - > - - - - + + + } - /> - - - - - } - > + > + + + + } + > + + + } + /> + + + + } + > + + + + + } + /> + - - - } - > - - + + + } - /> - - + > + + + + } + > + + + } + /> + + + + ); } diff --git a/client/src/components/DropdownMenu.tsx b/client/src/components/DropdownMenu.tsx index 26b38eb..b2ade9c 100644 --- a/client/src/components/DropdownMenu.tsx +++ b/client/src/components/DropdownMenu.tsx @@ -24,10 +24,13 @@ const DropdownMenu: React.FC = ({ const buttonRef = useRef(null); const { t } = useTranslation(); - const toggleMenu = useCallback((e: React.MouseEvent) => { - e.stopPropagation(); - setOpen((prev) => !prev); - }, []); + const toggleMenu = useCallback( + (e: React.MouseEvent) => { + e.stopPropagation(); + setOpen((prev) => !prev); + }, + [setOpen], + ); const menuClasses = position === "right" ? "right-0" : "left-0"; diff --git a/client/src/components/ui/modals/UpgradeModal.tsx b/client/src/components/ui/modals/UpgradeModal.tsx new file mode 100644 index 0000000..391e694 --- /dev/null +++ b/client/src/components/ui/modals/UpgradeModal.tsx @@ -0,0 +1,77 @@ +import { IconCancel } from "../../icons/IconCancel"; +import { Button } from "../Button"; +import { useTranslation } from "react-i18next"; +import { useDismiss } from "../../../hooks/useDismissClickAndEsc"; +import { useEffect } from "react"; + +interface UpgradeModalProps { + onClose: () => void; + message: string; +} + +const UpgradeModal = ({ onClose, message }: UpgradeModalProps) => { + const { t } = useTranslation(); + const { open, ref, setOpen } = useDismiss(true); + + useEffect(() => { + if (!open) { + onClose(); + } + }, [open, onClose]); + + if (!open) return null; + + const handleCloseClick = () => { + setOpen(false); + }; + + return ( +
+
+
+ +
+
+ + + + +

+ {t("upgradeModal.title")} +

+ +

{message}

+ +
+ +
+
+
+
+ ); +}; + +export default UpgradeModal; diff --git a/client/src/components/ui/modals/UpgradeModalContainer.tsx b/client/src/components/ui/modals/UpgradeModalContainer.tsx new file mode 100644 index 0000000..8c6015b --- /dev/null +++ b/client/src/components/ui/modals/UpgradeModalContainer.tsx @@ -0,0 +1,21 @@ +import { useAppSelector } from "../../../store"; +import UpgradeModal from "./UpgradeModal"; +import { useUiResetError } from "../../../store/boards/hooks"; +import type { LimitErrorData } from "../../../pages/boards/types"; + +export default function UpgradeModalContainer() { + const limitErrorData = useAppSelector( + (state) => state.ui.error as LimitErrorData | null, + ); + const resetError = useUiResetError(); + + if (!limitErrorData || (limitErrorData && !limitErrorData.errorCode)) { + return null; + } + + const handleClose = () => resetError(); + + return ( + + ); +} diff --git a/client/src/components/ui/modals/new-board.tsx b/client/src/components/ui/modals/new-board.tsx index cdcbcdd..b557b8e 100644 --- a/client/src/components/ui/modals/new-board.tsx +++ b/client/src/components/ui/modals/new-board.tsx @@ -10,6 +10,7 @@ import { useAppDispatch } from "../../../store"; import { SpinnerLoadingText } from "../Spinner"; import toast from "react-hot-toast"; import { CustomToast } from "../../CustomToast"; +import { useUiResetError } from "../../../store/boards/hooks"; interface NewBoardProps { onClose: () => void; @@ -21,8 +22,12 @@ const NewBoard = ({ onClose }: NewBoardProps) => { const [isSubmitting, setIsSubmitting] = useState(false); const dispatch = useAppDispatch(); const fileRef = useRef(null); + const resetError = useUiResetError(); - const isDisabled = !titleInput && isSubmitting; + const handleClose = () => { + resetError(); + onClose(); + }; const handleTitleChange = (event: ChangeEvent) => { setTitleInput(event.target.value); @@ -31,45 +36,39 @@ const NewBoard = ({ onClose }: NewBoardProps) => { const handleSubmit = async (event: FormEvent) => { event.preventDefault(); - try { - const boardData = new FormData(); - boardData.append("title", titleInput); - const file = fileRef.current?.files?.[0]; - - if (isSubmitting) return; - setIsSubmitting(true); - - if (file) { - const maxSizeMB = 5; - - if (file.size > maxSizeMB * 1024 * 1024) { - toast.custom((t) => ( - - )); + if (isSubmitting) return; + setIsSubmitting(true); + resetError(); - return; - } + const boardData = new FormData(); + boardData.append("title", titleInput); - boardData.append("image", file); + const file = fileRef.current?.files?.[0]; + if (file) { + const maxSizeMB = 5; + if (file.size > maxSizeMB * 1024 * 1024) { + toast.custom((t) => ( + + )); + setIsSubmitting(false); + return; } - - await dispatch(addBoard(boardData)); - onClose(); // Cierra el modal solo si la creación fue exitosa - } catch (error) { - console.error(translation("newBoard.error"), error); - } finally { - setIsSubmitting(false); + boardData.append("image", file); } + + await dispatch(addBoard(boardData)); + setIsSubmitting(false); + onClose(); }; return (
- +

{translation("newBoard.form.header")}

@@ -85,6 +84,7 @@ const NewBoard = ({ onClose }: NewBoardProps) => { onChange={handleTitleChange} />
+
{ ref={fileRef} />
+