aboutsummaryrefslogtreecommitdiff
path: root/frontend
diff options
context:
space:
mode:
Diffstat (limited to 'frontend')
-rw-r--r--frontend/src/App.tsx1
-rw-r--r--frontend/src/components/ConfirmDialog.tsx31
-rw-r--r--frontend/src/components/MapDeleteConfirmDialog.tsx28
-rw-r--r--frontend/src/components/MessageDialog.tsx29
-rw-r--r--frontend/src/components/UseConfirm.tsx36
-rw-r--r--frontend/src/components/UseMessage.tsx27
-rw-r--r--frontend/src/css/Dialog.css78
-rw-r--r--frontend/src/css/MapDeleteConfirmDialog.css6
-rw-r--r--frontend/src/pages/Profile.tsx19
9 files changed, 215 insertions, 40 deletions
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';
11import User from './pages/User'; 11import User from './pages/User';
12import Homepage from './pages/Homepage'; 12import Homepage from './pages/Homepage';
13import UploadRunDialog from './components/UploadRunDialog'; 13import UploadRunDialog from './components/UploadRunDialog';
14import MapDeleteConfirmDialog from './components/MapDeleteConfirmDialog';
15import Rules from './pages/Rules'; 14import Rules from './pages/Rules';
16import About from './pages/About'; 15import About from './pages/About';
17import { Game } from './types/Game'; 16import { 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 @@
1import React from 'react';
2
3import "../css/Dialog.css"
4
5interface ConfirmDialogProps {
6 title: string;
7 subtitle: string;
8 onConfirm: () => void;
9 onCancel: () => void;
10};
11
12const ConfirmDialog: React.FC<ConfirmDialogProps> = ({ title, subtitle, onConfirm, onCancel }) => {
13 return (
14 <div className='dimmer'>
15 <div className='dialog'>
16 <div className='dialog-element dialog-header'>
17 <span>{title}</span>
18 </div>
19 <div className='dialog-element dialog-description'>
20 <span>{subtitle}</span>
21 </div>
22 <div className='dialog-element dialog-btns-container'>
23 <button onClick={onCancel}>Cancel</button>
24 <button onClick={onConfirm}>Confirm</button>
25 </div>
26 </div>
27 </div>
28 )
29};
30
31export 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 @@
1import React from 'react';
2
3import "../css/MapDeleteConfirmDialog.css"
4
5interface MapDeleteConfirmDialogProps {
6 open: boolean;
7 onClose: () => void;
8 map_id: number;
9 record_id: number;
10};
11
12const MapDeleteConfirmDialog: React.FC<MapDeleteConfirmDialogProps> = ({ open, onClose, map_id, record_id }) => {
13 if (open) {
14 return (
15 <div className='dimmer'>
16 <div>
17
18 </div>
19 </div>
20 )
21 }
22
23 return (
24 <></>
25 )
26};
27
28export 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 @@
1import React from 'react';
2
3import "../css/Dialog.css"
4
5interface MessageDialogProps {
6 title: string;
7 subtitle: string;
8 onClose: () => void;
9};
10
11const MessageDialog: React.FC<MessageDialogProps> = ({ title, subtitle, onClose }) => {
12 return (
13 <div className='dimmer'>
14 <div className='dialog'>
15 <div className='dialog-element dialog-header'>
16 <span>{title}</span>
17 </div>
18 <div className='dialog-element dialog-description'>
19 <span>{subtitle}</span>
20 </div>
21 <div className='dialog-element dialog-btns-container'>
22 <button onClick={onClose}>Ok</button>
23 </div>
24 </div>
25 </div>
26 )
27}
28
29export 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 @@
1import React, { useState } from 'react';
2import ConfirmDialog from './ConfirmDialog';
3
4const useConfirm = ( title: string, subtitle: string ) => {
5 const [isOpen, setIsOpen] = useState(false);
6 const [resolvePromise, setResolvePromise] = useState<((value: boolean) => void) | null>(null);
7
8 const confirm = () => {
9 setIsOpen(true);
10 return new Promise((resolve) => {
11 setResolvePromise(() => resolve);
12 });
13 };
14
15 const handleConfirm = () => {
16 setIsOpen(false);
17 if (resolvePromise) {
18 resolvePromise(true);
19 }
20 }
21
22 const handleCancel = () => {
23 setIsOpen(false);
24 if (resolvePromise) {
25 resolvePromise(false);
26 }
27 }
28
29 const ConfirmDialogComponent = isOpen && (
30 <ConfirmDialog title={title} subtitle={subtitle} onConfirm={handleConfirm} onCancel={handleCancel}></ConfirmDialog>
31 );
32
33 return { confirm, ConfirmDialogComponent };
34}
35
36export 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 @@
1import React, { useState } from 'react';
2import MessageDialog from "./MessageDialog";
3
4const useMessage = () => {
5 const [isOpen, setIsOpen] = useState(false);
6
7 const [title, setTitle] = useState<string>("");
8 const [subtitle, setSubtitle] = useState<string>("");
9
10 const message = (title: string, subtitle: string) => {
11 setIsOpen(true);
12 setTitle(title);
13 setSubtitle(subtitle);
14 };
15
16 const handleClose = () => {
17 setIsOpen(false);
18 };
19
20 const MessageDialogComponent = isOpen && (
21 <MessageDialog title={title} subtitle={subtitle} onClose={handleClose}></MessageDialog>
22 );
23
24 return { message, MessageDialogComponent };
25}
26
27export 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 @@
1.dimmer {
2 position: absolute;
3 width: calc(100%);
4 height: 100%;
5 background-color: rgba(0, 0, 0, 0.5);
6}
7
8.dialog {
9 position: absolute;
10 z-index: 10000;
11 top: 50%;
12 left: 50%;
13 transform: translate(-50%, -50%);
14 background-color: #2b2e46;
15 border-radius: 24px;
16 overflow: hidden;
17 min-width: 350px;
18 border: 0.1em solid #272831;
19 animation: dialog_in 0.2s cubic-bezier(0.075, 0.82, 0.165, 1.1);
20}
21
22.dialog-element {
23 padding: 8px;
24}
25
26.dialog-header, .dialog-btns-container {
27 background-color: #202232;
28}
29
30.dialog-btns-container {
31 display: flex;
32 justify-content: right;
33 border-top: 2px solid #3f4151;
34 gap: 8px;
35}
36
37.dialog-header {
38 border-bottom: 2px solid #3f4151;
39}
40
41.dialog-description {
42 font-size: 18px;
43 padding: 14px 10px;
44 white-space: pre-wrap;
45}
46
47.dialog-btns-container button {
48 border-radius: 24px;
49 padding: 5px 10px;
50}
51
52.dialog-btns-container button:nth-child(2):hover {
53 background-color: rgb(105, 36, 36);
54}
55
56.dialog-btns-container button:nth-child(2) {
57 background-color: rgb(147, 45, 45);
58}
59
60.dialog-btns-container button:nth-child(1):hover {
61 background-color: rgb(38, 42, 62);
62}
63
64@keyframes dialog_in {
65 0% {
66 transform: translate(-50%, calc(-50% + 100px)) scaleX(0%);
67 opacity: 0;
68 }
69
70 20% {
71 opacity: 0;
72 }
73
74 100% {
75 transform: translate(-50%, calc(-50%));
76 opacity: 1;
77 }
78} \ 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 @@
1.dimmer {
2 position: absolute;
3 width: calc(100%);
4 height: 100%;
5 background-color: rgba(0, 0, 0, 0.5);
6} \ 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';
8import { ticks_to_time } from '../utils/Time'; 8import { ticks_to_time } from '../utils/Time';
9import "../css/Profile.css"; 9import "../css/Profile.css";
10import { API } from '../api/Api'; 10import { API } from '../api/Api';
11import MapDeleteConfirmDialog from '../components/MapDeleteConfirmDialog'; 11import useConfirm from '../components/UseConfirm';
12import useMessage from '../components/UseMessage';
12 13
13interface ProfileProps { 14interface ProfileProps {
14 profile?: UserProfile; 15 profile?: UserProfile;
@@ -18,7 +19,8 @@ interface ProfileProps {
18} 19}
19 20
20const Profile: React.FC<ProfileProps> = ({ profile, token, gameData, onDeleteRecord }) => { 21const Profile: React.FC<ProfileProps> = ({ profile, token, gameData, onDeleteRecord }) => {
21 22 const { confirm, ConfirmDialogComponent } = useConfirm("Delete record?", "This action cannot be undone");
23 const { message, MessageDialogComponent } = useMessage();
22 const [navState, setNavState] = React.useState(0); 24 const [navState, setNavState] = React.useState(0);
23 const [pageNumber, setPageNumber] = React.useState(1); 25 const [pageNumber, setPageNumber] = React.useState(1);
24 const [pageMax, setPageMax] = React.useState(0); 26 const [pageMax, setPageMax] = React.useState(0);
@@ -61,12 +63,17 @@ const Profile: React.FC<ProfileProps> = ({ profile, token, gameData, onDeleteRec
61 }; 63 };
62 64
63 const _delete_submission = async (map_id: number, record_id: number) => { 65 const _delete_submission = async (map_id: number, record_id: number) => {
64 onDeleteRecord(); 66 const userConfirmed = await confirm();
67
68 if (!userConfirmed) {
69 return;
70 }
71
65 const api_success = await API.delete_map_record(token!, map_id, record_id); 72 const api_success = await API.delete_map_record(token!, map_id, record_id);
66 if (api_success) { 73 if (api_success) {
67 window.alert("Successfully deleted record"); 74 message("Success", "Successfully deleted record");
68 } else { 75 } else {
69 window.alert("Error: Could not delete record"); 76 message("Error", "Could not delete record");
70 } 77 }
71 }; 78 };
72 79
@@ -96,6 +103,8 @@ const Profile: React.FC<ProfileProps> = ({ profile, token, gameData, onDeleteRec
96 103
97 return ( 104 return (
98 <main> 105 <main>
106 {ConfirmDialogComponent}
107 {MessageDialogComponent}
99 <section id='section1' className='profile'> 108 <section id='section1' className='profile'>
100 109
101 {profile.profile 110 {profile.profile