From 8c8edfc9c8bb1a0fa3d1e02aab576044857f1c95 Mon Sep 17 00:00:00 2001 From: Wolfboy248 <105884620+Wolfboy248@users.noreply.github.com> Date: Sun, 20 Oct 2024 00:27:38 +0200 Subject: refactor: custom modals --- frontend/src/App.tsx | 1 - frontend/src/components/ConfirmDialog.tsx | 31 +++++++++ frontend/src/components/MapDeleteConfirmDialog.tsx | 28 -------- frontend/src/components/MessageDialog.tsx | 29 ++++++++ frontend/src/components/UseConfirm.tsx | 36 ++++++++++ frontend/src/components/UseMessage.tsx | 27 ++++++++ frontend/src/css/Dialog.css | 78 ++++++++++++++++++++++ frontend/src/css/MapDeleteConfirmDialog.css | 6 -- frontend/src/pages/Profile.tsx | 19 ++++-- 9 files changed, 215 insertions(+), 40 deletions(-) create mode 100644 frontend/src/components/ConfirmDialog.tsx delete mode 100644 frontend/src/components/MapDeleteConfirmDialog.tsx create mode 100644 frontend/src/components/MessageDialog.tsx create mode 100644 frontend/src/components/UseConfirm.tsx create mode 100644 frontend/src/components/UseMessage.tsx create mode 100644 frontend/src/css/Dialog.css delete mode 100644 frontend/src/css/MapDeleteConfirmDialog.css diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index a0725f3..41e426c 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -11,7 +11,6 @@ import Maps from './pages/Maps'; import User from './pages/User'; import Homepage from './pages/Homepage'; import UploadRunDialog from './components/UploadRunDialog'; -import MapDeleteConfirmDialog from './components/MapDeleteConfirmDialog'; import Rules from './pages/Rules'; import About from './pages/About'; import { Game } from './types/Game'; diff --git a/frontend/src/components/ConfirmDialog.tsx b/frontend/src/components/ConfirmDialog.tsx new file mode 100644 index 0000000..c4299fd --- /dev/null +++ b/frontend/src/components/ConfirmDialog.tsx @@ -0,0 +1,31 @@ +import React from 'react'; + +import "../css/Dialog.css" + +interface ConfirmDialogProps { + title: string; + subtitle: string; + onConfirm: () => void; + onCancel: () => void; +}; + +const ConfirmDialog: React.FC = ({ title, subtitle, onConfirm, onCancel }) => { + return ( +
+
+
+ {title} +
+
+ {subtitle} +
+
+ + +
+
+
+ ) +}; + +export default ConfirmDialog; diff --git a/frontend/src/components/MapDeleteConfirmDialog.tsx b/frontend/src/components/MapDeleteConfirmDialog.tsx deleted file mode 100644 index 1518a14..0000000 --- a/frontend/src/components/MapDeleteConfirmDialog.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import React from 'react'; - -import "../css/MapDeleteConfirmDialog.css" - -interface MapDeleteConfirmDialogProps { - open: boolean; - onClose: () => void; - map_id: number; - record_id: number; -}; - -const MapDeleteConfirmDialog: React.FC = ({ open, onClose, map_id, record_id }) => { - if (open) { - return ( -
-
- -
-
- ) - } - - return ( - <> - ) -}; - -export default MapDeleteConfirmDialog; diff --git a/frontend/src/components/MessageDialog.tsx b/frontend/src/components/MessageDialog.tsx new file mode 100644 index 0000000..0a8db42 --- /dev/null +++ b/frontend/src/components/MessageDialog.tsx @@ -0,0 +1,29 @@ +import React from 'react'; + +import "../css/Dialog.css" + +interface MessageDialogProps { + title: string; + subtitle: string; + onClose: () => void; +}; + +const MessageDialog: React.FC = ({ title, subtitle, onClose }) => { + return ( +
+
+
+ {title} +
+
+ {subtitle} +
+
+ +
+
+
+ ) +} + +export default MessageDialog; diff --git a/frontend/src/components/UseConfirm.tsx b/frontend/src/components/UseConfirm.tsx new file mode 100644 index 0000000..2fe0e12 --- /dev/null +++ b/frontend/src/components/UseConfirm.tsx @@ -0,0 +1,36 @@ +import React, { useState } from 'react'; +import ConfirmDialog from './ConfirmDialog'; + +const useConfirm = ( title: string, subtitle: string ) => { + const [isOpen, setIsOpen] = useState(false); + const [resolvePromise, setResolvePromise] = useState<((value: boolean) => void) | null>(null); + + const confirm = () => { + setIsOpen(true); + return new Promise((resolve) => { + setResolvePromise(() => resolve); + }); + }; + + const handleConfirm = () => { + setIsOpen(false); + if (resolvePromise) { + resolvePromise(true); + } + } + + const handleCancel = () => { + setIsOpen(false); + if (resolvePromise) { + resolvePromise(false); + } + } + + const ConfirmDialogComponent = isOpen && ( + + ); + + return { confirm, ConfirmDialogComponent }; +} + +export default useConfirm; diff --git a/frontend/src/components/UseMessage.tsx b/frontend/src/components/UseMessage.tsx new file mode 100644 index 0000000..fbd3c82 --- /dev/null +++ b/frontend/src/components/UseMessage.tsx @@ -0,0 +1,27 @@ +import React, { useState } from 'react'; +import MessageDialog from "./MessageDialog"; + +const useMessage = () => { + const [isOpen, setIsOpen] = useState(false); + + const [title, setTitle] = useState(""); + const [subtitle, setSubtitle] = useState(""); + + const message = (title: string, subtitle: string) => { + setIsOpen(true); + setTitle(title); + setSubtitle(subtitle); + }; + + const handleClose = () => { + setIsOpen(false); + }; + + const MessageDialogComponent = isOpen && ( + + ); + + return { message, MessageDialogComponent }; +} + +export default useMessage; diff --git a/frontend/src/css/Dialog.css b/frontend/src/css/Dialog.css new file mode 100644 index 0000000..c25a755 --- /dev/null +++ b/frontend/src/css/Dialog.css @@ -0,0 +1,78 @@ +.dimmer { + position: absolute; + width: calc(100%); + height: 100%; + background-color: rgba(0, 0, 0, 0.5); +} + +.dialog { + position: absolute; + z-index: 10000; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + background-color: #2b2e46; + border-radius: 24px; + overflow: hidden; + min-width: 350px; + border: 0.1em solid #272831; + animation: dialog_in 0.2s cubic-bezier(0.075, 0.82, 0.165, 1.1); +} + +.dialog-element { + padding: 8px; +} + +.dialog-header, .dialog-btns-container { + background-color: #202232; +} + +.dialog-btns-container { + display: flex; + justify-content: right; + border-top: 2px solid #3f4151; + gap: 8px; +} + +.dialog-header { + border-bottom: 2px solid #3f4151; +} + +.dialog-description { + font-size: 18px; + padding: 14px 10px; + white-space: pre-wrap; +} + +.dialog-btns-container button { + border-radius: 24px; + padding: 5px 10px; +} + +.dialog-btns-container button:nth-child(2):hover { + background-color: rgb(105, 36, 36); +} + +.dialog-btns-container button:nth-child(2) { + background-color: rgb(147, 45, 45); +} + +.dialog-btns-container button:nth-child(1):hover { + background-color: rgb(38, 42, 62); +} + +@keyframes dialog_in { + 0% { + transform: translate(-50%, calc(-50% + 100px)) scaleX(0%); + opacity: 0; + } + + 20% { + opacity: 0; + } + + 100% { + transform: translate(-50%, calc(-50%)); + opacity: 1; + } +} \ No newline at end of file diff --git a/frontend/src/css/MapDeleteConfirmDialog.css b/frontend/src/css/MapDeleteConfirmDialog.css deleted file mode 100644 index 1f7df1a..0000000 --- a/frontend/src/css/MapDeleteConfirmDialog.css +++ /dev/null @@ -1,6 +0,0 @@ -.dimmer { - position: absolute; - width: calc(100%); - height: 100%; - background-color: rgba(0, 0, 0, 0.5); -} \ No newline at end of file diff --git a/frontend/src/pages/Profile.tsx b/frontend/src/pages/Profile.tsx index 09033a5..bf79a65 100644 --- a/frontend/src/pages/Profile.tsx +++ b/frontend/src/pages/Profile.tsx @@ -8,7 +8,8 @@ import { Map } from '../types/Map'; import { ticks_to_time } from '../utils/Time'; import "../css/Profile.css"; import { API } from '../api/Api'; -import MapDeleteConfirmDialog from '../components/MapDeleteConfirmDialog'; +import useConfirm from '../components/UseConfirm'; +import useMessage from '../components/UseMessage'; interface ProfileProps { profile?: UserProfile; @@ -18,7 +19,8 @@ interface ProfileProps { } const Profile: React.FC = ({ profile, token, gameData, onDeleteRecord }) => { - + const { confirm, ConfirmDialogComponent } = useConfirm("Delete record?", "This action cannot be undone"); + const { message, MessageDialogComponent } = useMessage(); const [navState, setNavState] = React.useState(0); const [pageNumber, setPageNumber] = React.useState(1); const [pageMax, setPageMax] = React.useState(0); @@ -61,12 +63,17 @@ const Profile: React.FC = ({ profile, token, gameData, onDeleteRec }; const _delete_submission = async (map_id: number, record_id: number) => { - onDeleteRecord(); + const userConfirmed = await confirm(); + + if (!userConfirmed) { + return; + } + const api_success = await API.delete_map_record(token!, map_id, record_id); if (api_success) { - window.alert("Successfully deleted record"); + message("Success", "Successfully deleted record"); } else { - window.alert("Error: Could not delete record"); + message("Error", "Could not delete record"); } }; @@ -96,6 +103,8 @@ const Profile: React.FC = ({ profile, token, gameData, onDeleteRec return (
+ {ConfirmDialogComponent} + {MessageDialogComponent}
{profile.profile -- cgit v1.2.3