diff options
| author | Wolfboy248 <georgejvindkarlsen@gmail.com> | 2024-10-18 17:01:01 +0200 |
|---|---|---|
| committer | Wolfboy248 <georgejvindkarlsen@gmail.com> | 2024-10-18 17:01:01 +0200 |
| commit | d9974ede3b3914377beb4b07d78885c48bd74aac (patch) | |
| tree | 6ee6d0adfe62d3ec00dea21dc520dfa09eeaf005 | |
| parent | refactor: upload run logic improvement (diff) | |
| download | lphub-d9974ede3b3914377beb4b07d78885c48bd74aac.tar.gz lphub-d9974ede3b3914377beb4b07d78885c48bd74aac.tar.bz2 lphub-d9974ede3b3914377beb4b07d78885c48bd74aac.zip | |
refactor: delete run on profile
| -rw-r--r-- | frontend/src/App.tsx | 7 | ||||
| -rw-r--r-- | frontend/src/api/Api.tsx | 6 | ||||
| -rw-r--r-- | frontend/src/api/Maps.tsx | 9 | ||||
| -rw-r--r-- | frontend/src/components/Login.tsx | 3 | ||||
| -rw-r--r-- | frontend/src/components/UploadRunDialog.tsx | 3 | ||||
| -rw-r--r-- | frontend/src/images/Images.tsx | 4 | ||||
| -rw-r--r-- | frontend/src/pages/Profile.tsx | 19 | ||||
| -rw-r--r-- | frontend/src/types/Map.tsx | 5 |
8 files changed, 48 insertions, 8 deletions
diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index c34cbcf..a0725f3 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx | |||
| @@ -11,6 +11,7 @@ import Maps from './pages/Maps'; | |||
| 11 | import User from './pages/User'; | 11 | import User from './pages/User'; |
| 12 | import Homepage from './pages/Homepage'; | 12 | import Homepage from './pages/Homepage'; |
| 13 | import UploadRunDialog from './components/UploadRunDialog'; | 13 | import UploadRunDialog from './components/UploadRunDialog'; |
| 14 | import MapDeleteConfirmDialog from './components/MapDeleteConfirmDialog'; | ||
| 14 | import Rules from './pages/Rules'; | 15 | import Rules from './pages/Rules'; |
| 15 | import About from './pages/About'; | 16 | import About from './pages/About'; |
| 16 | import { Game } from './types/Game'; | 17 | import { Game } from './types/Game'; |
| @@ -18,6 +19,7 @@ import { API } from './api/Api'; | |||
| 18 | import Maplist from './pages/Maplist'; | 19 | import Maplist from './pages/Maplist'; |
| 19 | import Rankings from './pages/Rankings'; | 20 | import Rankings from './pages/Rankings'; |
| 20 | import { get_user_id_from_token, get_user_mod_from_token } from './utils/Jwt'; | 21 | import { get_user_id_from_token, get_user_mod_from_token } from './utils/Jwt'; |
| 22 | import { MapDeleteEndpoint } from './types/Map'; | ||
| 21 | 23 | ||
| 22 | const App: React.FC = () => { | 24 | const App: React.FC = () => { |
| 23 | const [token, setToken] = React.useState<string | undefined>(undefined); | 25 | const [token, setToken] = React.useState<string | undefined>(undefined); |
| @@ -29,6 +31,9 @@ const App: React.FC = () => { | |||
| 29 | const [uploadRunDialog, setUploadRunDialog] = React.useState<boolean>(false); | 31 | const [uploadRunDialog, setUploadRunDialog] = React.useState<boolean>(false); |
| 30 | const [uploadRunDialogMapID, setUploadRunDialogMapID] = React.useState<number | undefined>(undefined); | 32 | const [uploadRunDialogMapID, setUploadRunDialogMapID] = React.useState<number | undefined>(undefined); |
| 31 | 33 | ||
| 34 | const [confirmDialogOpen, setConfirmDialogOpen] = React.useState<boolean>(false); | ||
| 35 | const [currDeleteMapInfo, setCurrDeleteMapInfo] = React.useState<MapDeleteEndpoint>(); | ||
| 36 | |||
| 32 | const _fetch_token = async () => { | 37 | const _fetch_token = async () => { |
| 33 | const token = await API.get_token(); | 38 | const token = await API.get_token(); |
| 34 | setToken(token); | 39 | setToken(token); |
| @@ -79,7 +84,7 @@ const App: React.FC = () => { | |||
| 79 | <Sidebar setToken={setToken} profile={profile} setProfile={setProfile} onUploadRun={() => setUploadRunDialog(true)} /> | 84 | <Sidebar setToken={setToken} profile={profile} setProfile={setProfile} onUploadRun={() => setUploadRunDialog(true)} /> |
| 80 | <Routes> | 85 | <Routes> |
| 81 | <Route path="/" element={<Homepage />} /> | 86 | <Route path="/" element={<Homepage />} /> |
| 82 | <Route path="/profile" element={<Profile profile={profile} token={token} gameData={games} />} /> | 87 | <Route path="/profile" element={<Profile profile={profile} token={token} gameData={games} onDeleteRecord={() => setConfirmDialogOpen(true)} />} /> |
| 83 | <Route path="/users/*" element={<User profile={profile} token={token} gameData={games} />} /> | 88 | <Route path="/users/*" element={<User profile={profile} token={token} gameData={games} />} /> |
| 84 | <Route path="/games" element={<Games games={games} />} /> | 89 | <Route path="/games" element={<Games games={games} />} /> |
| 85 | <Route path='/games/:id' element={<Maplist />}></Route> | 90 | <Route path='/games/:id' element={<Maplist />}></Route> |
diff --git a/frontend/src/api/Api.tsx b/frontend/src/api/Api.tsx index 0f0c4d3..d03d0ec 100644 --- a/frontend/src/api/Api.tsx +++ b/frontend/src/api/Api.tsx | |||
| @@ -3,7 +3,7 @@ import { delete_token, get_token } from './Auth'; | |||
| 3 | import { get_user, get_profile, post_profile } from './User'; | 3 | import { get_user, get_profile, post_profile } from './User'; |
| 4 | import { get_games, get_chapters, get_games_chapters, get_game_maps, get_search } from './Games'; | 4 | import { get_games, get_chapters, get_games_chapters, get_game_maps, get_search } from './Games'; |
| 5 | import { get_official_rankings, get_unofficial_rankings } from './Rankings'; | 5 | import { get_official_rankings, get_unofficial_rankings } from './Rankings'; |
| 6 | import { get_map_summary, get_map_leaderboard, get_map_discussions, get_map_discussion, post_map_discussion, post_map_discussion_comment, delete_map_discussion, post_record } from './Maps'; | 6 | import { get_map_summary, get_map_leaderboard, get_map_discussions, get_map_discussion, post_map_discussion, post_map_discussion_comment, delete_map_discussion, post_record, delete_map_record } from './Maps'; |
| 7 | import { delete_map_summary, post_map_summary, put_map_image, put_map_summary } from './Mod'; | 7 | import { delete_map_summary, post_map_summary, put_map_image, put_map_summary } from './Mod'; |
| 8 | import { UploadRunContent } from '../types/Content'; | 8 | import { UploadRunContent } from '../types/Content'; |
| 9 | 9 | ||
| @@ -38,6 +38,8 @@ export const API = { | |||
| 38 | post_record: (token: string, run: UploadRunContent) => post_record(token, run), | 38 | post_record: (token: string, run: UploadRunContent) => post_record(token, run), |
| 39 | 39 | ||
| 40 | delete_map_discussion: (token: string, map_id: string, discussion_id: number) => delete_map_discussion(token, map_id, discussion_id), | 40 | delete_map_discussion: (token: string, map_id: string, discussion_id: number) => delete_map_discussion(token, map_id, discussion_id), |
| 41 | |||
| 42 | delete_map_record: (token: string, map_id: number, record_id: number) => delete_map_record(token, map_id, record_id), | ||
| 41 | // Mod | 43 | // Mod |
| 42 | post_map_summary: (token: string, map_id: string, content: ModMenuContent) => post_map_summary(token, map_id, content), | 44 | post_map_summary: (token: string, map_id: string, content: ModMenuContent) => post_map_summary(token, map_id, content), |
| 43 | 45 | ||
| @@ -47,7 +49,7 @@ export const API = { | |||
| 47 | delete_map_summary: (token: string, map_id: string, route_id: number) => delete_map_summary(token, map_id, route_id), | 49 | delete_map_summary: (token: string, map_id: string, route_id: number) => delete_map_summary(token, map_id, route_id), |
| 48 | }; | 50 | }; |
| 49 | 51 | ||
| 50 | const BASE_API_URL: string = "/api/v1/" | 52 | const BASE_API_URL: string = "https://lp.ardapektezol.com/api/v1/" |
| 51 | 53 | ||
| 52 | export function url(path: string): string { | 54 | export function url(path: string): string { |
| 53 | return BASE_API_URL + path; | 55 | return BASE_API_URL + path; |
diff --git a/frontend/src/api/Maps.tsx b/frontend/src/api/Maps.tsx index 6bdc3e6..8295cf2 100644 --- a/frontend/src/api/Maps.tsx +++ b/frontend/src/api/Maps.tsx | |||
| @@ -97,4 +97,13 @@ export const post_record = async (token: string, run: UploadRunContent): Promise | |||
| 97 | }); | 97 | }); |
| 98 | return response.data.message; | 98 | return response.data.message; |
| 99 | } | 99 | } |
| 100 | } | ||
| 101 | |||
| 102 | export const delete_map_record = async (token: string, map_id: number, record_id: number): Promise<boolean> => { | ||
| 103 | const response = await axios.delete(url(`maps/${map_id}/record/${record_id}`), { | ||
| 104 | headers: { | ||
| 105 | "Authorization": token, | ||
| 106 | } | ||
| 107 | }); | ||
| 108 | return response.data.success; | ||
| 100 | }; | 109 | }; |
diff --git a/frontend/src/components/Login.tsx b/frontend/src/components/Login.tsx index a8c5503..f27a63e 100644 --- a/frontend/src/components/Login.tsx +++ b/frontend/src/components/Login.tsx | |||
| @@ -17,7 +17,8 @@ const Login: React.FC<LoginProps> = ({ setToken, profile, setProfile }) => { | |||
| 17 | const navigate = useNavigate(); | 17 | const navigate = useNavigate(); |
| 18 | 18 | ||
| 19 | const _login = () => { | 19 | const _login = () => { |
| 20 | window.location.href = "/api/v1/login"; | 20 | setToken("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MzExNjU1NTgsIm1vZCI6dHJ1ZSwic3ViIjoiNzY1NjExOTgxMzE2Mjk5ODkifQ.KoENjj6Z41-QQu1VKvgAiACsjLK7IoVWlJgrGdr6s24"); |
| 21 | // window.location.href = "/api/v1/login"; | ||
| 21 | }; | 22 | }; |
| 22 | 23 | ||
| 23 | const _logout = () => { | 24 | const _logout = () => { |
diff --git a/frontend/src/components/UploadRunDialog.tsx b/frontend/src/components/UploadRunDialog.tsx index 08d4108..d42ffe7 100644 --- a/frontend/src/components/UploadRunDialog.tsx +++ b/frontend/src/components/UploadRunDialog.tsx | |||
| @@ -172,6 +172,9 @@ const UploadRunDialog: React.FC<UploadRunDialogProps> = ({ token, open, onClose, | |||
| 172 | </> | 172 | </> |
| 173 | ) | 173 | ) |
| 174 | } | 174 | } |
| 175 | <div className='search-container'> | ||
| 176 | |||
| 177 | </div> | ||
| 175 | <div className='upload-run-buttons-container'> | 178 | <div className='upload-run-buttons-container'> |
| 176 | <button onClick={_upload_run}>Submit</button> | 179 | <button onClick={_upload_run}>Submit</button> |
| 177 | <button onClick={() => onClose()}>Cancel</button> | 180 | <button onClick={() => onClose()}>Cancel</button> |
diff --git a/frontend/src/images/Images.tsx b/frontend/src/images/Images.tsx index 89b99c0..198431b 100644 --- a/frontend/src/images/Images.tsx +++ b/frontend/src/images/Images.tsx | |||
| @@ -20,6 +20,7 @@ import img17 from './png/17.png'; | |||
| 20 | import img18 from './png/18.png'; | 20 | import img18 from './png/18.png'; |
| 21 | import img19 from './png/19.png'; | 21 | import img19 from './png/19.png'; |
| 22 | import img20 from './png/20.png'; | 22 | import img20 from './png/20.png'; |
| 23 | import img21 from "./png/21.png"; | ||
| 23 | 24 | ||
| 24 | export const LogoIcon = logo; | 25 | export const LogoIcon = logo; |
| 25 | export const LoginIcon = login; | 26 | export const LoginIcon = login; |
| @@ -43,4 +44,5 @@ export const YouTubeIcon = img16; | |||
| 43 | export const SteamIcon = img17; | 44 | export const SteamIcon = img17; |
| 44 | export const HistoryIcon = img18; | 45 | export const HistoryIcon = img18; |
| 45 | export const SortIcon = img19; | 46 | export const SortIcon = img19; |
| 46 | export const UploadIcon = img20; \ No newline at end of file | 47 | export const UploadIcon = img20; |
| 48 | export const DeleteIcon = img21; \ No newline at end of file | ||
diff --git a/frontend/src/pages/Profile.tsx b/frontend/src/pages/Profile.tsx index e20d930..5e39308 100644 --- a/frontend/src/pages/Profile.tsx +++ b/frontend/src/pages/Profile.tsx | |||
| @@ -1,21 +1,23 @@ | |||
| 1 | import React from 'react'; | 1 | import React from 'react'; |
| 2 | import { Link, useLocation, useNavigate } from 'react-router-dom'; | 2 | import { Link, useLocation, useNavigate } from 'react-router-dom'; |
| 3 | 3 | ||
| 4 | import { SteamIcon, TwitchIcon, YouTubeIcon, PortalIcon, FlagIcon, StatisticsIcon, SortIcon, ThreedotIcon, DownloadIcon, HistoryIcon } from '../images/Images'; | 4 | import { SteamIcon, TwitchIcon, YouTubeIcon, PortalIcon, FlagIcon, StatisticsIcon, SortIcon, ThreedotIcon, DownloadIcon, HistoryIcon, DeleteIcon } from '../images/Images'; |
| 5 | import { UserProfile } from '../types/Profile'; | 5 | import { UserProfile } from '../types/Profile'; |
| 6 | import { Game, GameChapters } from '../types/Game'; | 6 | import { Game, GameChapters } from '../types/Game'; |
| 7 | import { Map } from '../types/Map'; | 7 | import { Map } from '../types/Map'; |
| 8 | import { ticks_to_time } from '../utils/Time'; | 8 | import { ticks_to_time } from '../utils/Time'; |
| 9 | import "../css/Profile.css"; | 9 | import "../css/Profile.css"; |
| 10 | import { API } from '../api/Api'; | 10 | import { API } from '../api/Api'; |
| 11 | import MapDeleteConfirmDialog from '../components/MapDeleteConfirmDialog'; | ||
| 11 | 12 | ||
| 12 | interface ProfileProps { | 13 | interface ProfileProps { |
| 13 | profile?: UserProfile; | 14 | profile?: UserProfile; |
| 14 | token?: string; | 15 | token?: string; |
| 15 | gameData: Game[]; | 16 | gameData: Game[]; |
| 17 | onDeleteRecord: () => void; | ||
| 16 | } | 18 | } |
| 17 | 19 | ||
| 18 | const Profile: React.FC<ProfileProps> = ({ profile, token, gameData }) => { | 20 | const Profile: React.FC<ProfileProps> = ({ profile, token, gameData, onDeleteRecord }) => { |
| 19 | 21 | ||
| 20 | const [navState, setNavState] = React.useState(0); | 22 | const [navState, setNavState] = React.useState(0); |
| 21 | const [pageNumber, setPageNumber] = React.useState(1); | 23 | const [pageNumber, setPageNumber] = React.useState(1); |
| @@ -58,6 +60,16 @@ const Profile: React.FC<ProfileProps> = ({ profile, token, gameData }) => { | |||
| 58 | } | 60 | } |
| 59 | }; | 61 | }; |
| 60 | 62 | ||
| 63 | const _delete_submission = async (map_id: number, record_id: number) => { | ||
| 64 | onDeleteRecord(); | ||
| 65 | const api_success = await API.delete_map_record(token!, map_id, record_id); | ||
| 66 | if (api_success) { | ||
| 67 | window.alert("Successfully deleted record"); | ||
| 68 | } else { | ||
| 69 | window.alert("Error: Could not delete record"); | ||
| 70 | } | ||
| 71 | }; | ||
| 72 | |||
| 61 | React.useEffect(() => { | 73 | React.useEffect(() => { |
| 62 | if (!profile) { | 74 | if (!profile) { |
| 63 | navigate("/"); | 75 | navigate("/"); |
| @@ -228,7 +240,8 @@ const Profile: React.FC<ProfileProps> = ({ profile, token, gameData }) => { | |||
| 228 | <span>{e.date.split("T")[0]}</span> | 240 | <span>{e.date.split("T")[0]}</span> |
| 229 | <span style={{ flexDirection: "row-reverse" }}> | 241 | <span style={{ flexDirection: "row-reverse" }}> |
| 230 | 242 | ||
| 231 | <button onClick={() => { window.alert(`Demo ID: ${e.demo_id}`) }}><img src={ThreedotIcon} alt="demo_id" /></button> | 243 | <button style={{marginRight: "10px"}} onClick={() => { window.alert(`Demo ID: ${e.demo_id}`) }}><img src={ThreedotIcon} alt="demo_id" /></button> |
| 244 | <button onClick={() => { _delete_submission(r.map_id, e.record_id) }}><img src={DeleteIcon}></img></button> | ||
| 232 | <button onClick={() => window.location.href = `/api/v1/demos?uuid=${e.demo_id}`}><img src={DownloadIcon} alt="download" /></button> | 245 | <button onClick={() => window.location.href = `/api/v1/demos?uuid=${e.demo_id}`}><img src={DownloadIcon} alt="download" /></button> |
| 233 | {i === 0 && r.scores.length > 1 ? <button onClick={() => { | 246 | {i === 0 && r.scores.length > 1 ? <button onClick={() => { |
| 234 | (document.querySelectorAll(".profileboard-record")[index % 20] as HTMLInputElement).style.height === "44px" || | 247 | (document.querySelectorAll(".profileboard-record")[index % 20] as HTMLInputElement).style.height === "44px" || |
diff --git a/frontend/src/types/Map.tsx b/frontend/src/types/Map.tsx index 4a6b60e..4669e8b 100644 --- a/frontend/src/types/Map.tsx +++ b/frontend/src/types/Map.tsx | |||
| @@ -101,3 +101,8 @@ interface MapSummaryDetailsRouteHistory { | |||
| 101 | date: string; | 101 | date: string; |
| 102 | }; | 102 | }; |
| 103 | 103 | ||
| 104 | export interface MapDeleteEndpoint { | ||
| 105 | map_id: number; | ||
| 106 | record_id: number; | ||
| 107 | } | ||
| 108 | |||