diff options
Diffstat (limited to 'frontend/src/components/ModMenu.tsx')
| -rw-r--r-- | frontend/src/components/ModMenu.tsx | 466 |
1 files changed, 313 insertions, 153 deletions
diff --git a/frontend/src/components/ModMenu.tsx b/frontend/src/components/ModMenu.tsx index 925b8a8..f765cd8 100644 --- a/frontend/src/components/ModMenu.tsx +++ b/frontend/src/components/ModMenu.tsx | |||
| @@ -1,12 +1,12 @@ | |||
| 1 | import React from 'react'; | 1 | import React from "react"; |
| 2 | import ReactMarkdown from 'react-markdown'; | 2 | import ReactMarkdown from "react-markdown"; |
| 3 | import { useNavigate } from 'react-router-dom'; | 3 | import { useNavigate } from "react-router-dom"; |
| 4 | 4 | ||
| 5 | import { MapSummary } from '@customTypes/Map'; | 5 | import { MapSummary } from "@customTypes/Map"; |
| 6 | import { ModMenuContent } from '@customTypes/Content'; | 6 | import { ModMenuContent } from "@customTypes/Content"; |
| 7 | import { API } from '@api/Api'; | 7 | import { API } from "@api/Api"; |
| 8 | import "@css/ModMenu.css" | 8 | import "@css/ModMenu.css"; |
| 9 | import useConfirm from '@hooks/UseConfirm'; | 9 | import useConfirm from "@hooks/UseConfirm"; |
| 10 | 10 | ||
| 11 | interface ModMenuProps { | 11 | interface ModMenuProps { |
| 12 | token?: string; | 12 | token?: string; |
| @@ -15,8 +15,12 @@ interface ModMenuProps { | |||
| 15 | mapID: string; | 15 | mapID: string; |
| 16 | } | 16 | } |
| 17 | 17 | ||
| 18 | const ModMenu: React.FC<ModMenuProps> = ({ token, data, selectedRun, mapID }) => { | 18 | const ModMenu: React.FC<ModMenuProps> = ({ |
| 19 | 19 | token, | |
| 20 | data, | ||
| 21 | selectedRun, | ||
| 22 | mapID, | ||
| 23 | }) => { | ||
| 20 | const { confirm, ConfirmDialogComponent } = useConfirm(); | 24 | const { confirm, ConfirmDialogComponent } = useConfirm(); |
| 21 | 25 | ||
| 22 | const [menu, setMenu] = React.useState<number>(0); | 26 | const [menu, setMenu] = React.useState<number>(0); |
| @@ -40,7 +44,7 @@ const ModMenu: React.FC<ModMenuProps> = ({ token, data, selectedRun, mapID }) => | |||
| 40 | function compressImage(file: File): Promise<string> { | 44 | function compressImage(file: File): Promise<string> { |
| 41 | const reader = new FileReader(); | 45 | const reader = new FileReader(); |
| 42 | reader.readAsDataURL(file); | 46 | reader.readAsDataURL(file); |
| 43 | return new Promise(resolve => { | 47 | return new Promise((resolve) => { |
| 44 | reader.onload = () => { | 48 | reader.onload = () => { |
| 45 | const img = new Image(); | 49 | const img = new Image(); |
| 46 | if (typeof reader.result === "string") { | 50 | if (typeof reader.result === "string") { |
| @@ -55,74 +59,99 @@ const ModMenu: React.FC<ModMenuProps> = ({ token, data, selectedRun, mapID }) => | |||
| 55 | width *= 320 / height; | 59 | width *= 320 / height; |
| 56 | height = 320; | 60 | height = 320; |
| 57 | } | 61 | } |
| 58 | const canvas = document.createElement('canvas'); | 62 | const canvas = document.createElement("canvas"); |
| 59 | canvas.width = width; | 63 | canvas.width = width; |
| 60 | canvas.height = height; | 64 | canvas.height = height; |
| 61 | canvas.getContext('2d')!.drawImage(img, 0, 0, width, height); | 65 | canvas.getContext("2d")!.drawImage(img, 0, 0, width, height); |
| 62 | resolve(canvas.toDataURL(file.type, 0.6)); | 66 | resolve(canvas.toDataURL(file.type, 0.6)); |
| 63 | }; | 67 | }; |
| 64 | } | 68 | } |
| 65 | }; | 69 | }; |
| 66 | }); | 70 | }); |
| 67 | }; | 71 | } |
| 68 | 72 | ||
| 69 | const _edit_map_summary_image = async () => { | 73 | const _edit_map_summary_image = async () => { |
| 70 | if (await confirm("Edit Map Summary Image", "Are you sure you want to submit this to the database?")) { | 74 | if ( |
| 75 | await confirm( | ||
| 76 | "Edit Map Summary Image", | ||
| 77 | "Are you sure you want to submit this to the database?" | ||
| 78 | ) | ||
| 79 | ) { | ||
| 71 | if (token) { | 80 | if (token) { |
| 72 | const success = await API.put_map_image(token, mapID, image); | 81 | const success = await API.put_map_image(token, mapID, image); |
| 73 | if (success) { | 82 | if (success) { |
| 74 | navigate(0); | 83 | navigate(0); |
| 75 | } else { | 84 | } else { |
| 76 | alert("Error. Check logs.") | 85 | alert("Error. Check logs."); |
| 77 | } | 86 | } |
| 78 | } | 87 | } |
| 79 | } | 88 | } |
| 80 | }; | 89 | }; |
| 81 | 90 | ||
| 82 | const _edit_map_summary_route = async () => { | 91 | const _edit_map_summary_route = async () => { |
| 83 | if (await confirm("Edit Map Summary Route", "Are you sure you want to submit this to the database?")) { | 92 | if ( |
| 93 | await confirm( | ||
| 94 | "Edit Map Summary Route", | ||
| 95 | "Are you sure you want to submit this to the database?" | ||
| 96 | ) | ||
| 97 | ) { | ||
| 84 | if (token) { | 98 | if (token) { |
| 85 | routeContent.date += "T00:00:00Z"; | 99 | routeContent.date += "T00:00:00Z"; |
| 86 | const success = await API.put_map_summary(token, mapID, routeContent); | 100 | const success = await API.put_map_summary(token, mapID, routeContent); |
| 87 | if (success) { | 101 | if (success) { |
| 88 | navigate(0); | 102 | navigate(0); |
| 89 | } else { | 103 | } else { |
| 90 | alert("Error. Check logs.") | 104 | alert("Error. Check logs."); |
| 91 | } | 105 | } |
| 92 | } | 106 | } |
| 93 | } | 107 | } |
| 94 | }; | 108 | }; |
| 95 | 109 | ||
| 96 | const _create_map_summary_route = async () => { | 110 | const _create_map_summary_route = async () => { |
| 97 | if (await confirm("Create Map Summary Route", "Are you sure you want to submit this to the database?")) { | 111 | if ( |
| 112 | await confirm( | ||
| 113 | "Create Map Summary Route", | ||
| 114 | "Are you sure you want to submit this to the database?" | ||
| 115 | ) | ||
| 116 | ) { | ||
| 98 | if (token) { | 117 | if (token) { |
| 99 | routeContent.date += "T00:00:00Z"; | 118 | routeContent.date += "T00:00:00Z"; |
| 100 | const success = await API.post_map_summary(token, mapID, routeContent); | 119 | const success = await API.post_map_summary(token, mapID, routeContent); |
| 101 | if (success) { | 120 | if (success) { |
| 102 | navigate(0); | 121 | navigate(0); |
| 103 | } else { | 122 | } else { |
| 104 | alert("Error. Check logs.") | 123 | alert("Error. Check logs."); |
| 105 | } | 124 | } |
| 106 | } | 125 | } |
| 107 | } | 126 | } |
| 108 | }; | 127 | }; |
| 109 | 128 | ||
| 110 | const _delete_map_summary_route = async () => { | 129 | const _delete_map_summary_route = async () => { |
| 111 | if (await confirm("Delete Map Summary Route", `Are you sure you want to submit this to the database?\n | 130 | if ( |
| 112 | ${data.summary.routes[selectedRun].category.name}\n${data.summary.routes[selectedRun].history.score_count} portals\n${data.summary.routes[selectedRun].history.runner_name}`)) { | 131 | await confirm( |
| 132 | "Delete Map Summary Route", | ||
| 133 | `Are you sure you want to submit this to the database?\n | ||
| 134 | ${data.summary.routes[selectedRun].category.name}\n${data.summary.routes[selectedRun].history.score_count} portals\n${data.summary.routes[selectedRun].history.runner_name}` | ||
| 135 | ) | ||
| 136 | ) { | ||
| 113 | if (token) { | 137 | if (token) { |
| 114 | const success = await API.delete_map_summary(token, mapID, data.summary.routes[selectedRun].route_id); | 138 | const success = await API.delete_map_summary( |
| 139 | token, | ||
| 140 | mapID, | ||
| 141 | data.summary.routes[selectedRun].route_id | ||
| 142 | ); | ||
| 115 | if (success) { | 143 | if (success) { |
| 116 | navigate(0); | 144 | navigate(0); |
| 117 | } else { | 145 | } else { |
| 118 | alert("Error. Check logs.") | 146 | alert("Error. Check logs."); |
| 119 | } | 147 | } |
| 120 | } | 148 | } |
| 121 | } | 149 | } |
| 122 | }; | 150 | }; |
| 123 | 151 | ||
| 124 | React.useEffect(() => { | 152 | React.useEffect(() => { |
| 125 | if (menu === 3) { // add route | 153 | if (menu === 3) { |
| 154 | // add route | ||
| 126 | setRouteContent({ | 155 | setRouteContent({ |
| 127 | id: 0, | 156 | id: 0, |
| 128 | name: "", | 157 | name: "", |
| @@ -134,7 +163,8 @@ const ModMenu: React.FC<ModMenuProps> = ({ token, data, selectedRun, mapID }) => | |||
| 134 | }); | 163 | }); |
| 135 | setMd("No description available."); | 164 | setMd("No description available."); |
| 136 | } | 165 | } |
| 137 | if (menu === 2) { // edit route | 166 | if (menu === 2) { |
| 167 | // edit route | ||
| 138 | setRouteContent({ | 168 | setRouteContent({ |
| 139 | id: data.summary.routes[selectedRun].route_id, | 169 | id: data.summary.routes[selectedRun].route_id, |
| 140 | name: data.summary.routes[selectedRun].history.runner_name, | 170 | name: data.summary.routes[selectedRun].history.runner_name, |
| @@ -146,207 +176,337 @@ const ModMenu: React.FC<ModMenuProps> = ({ token, data, selectedRun, mapID }) => | |||
| 146 | }); | 176 | }); |
| 147 | setMd(data.summary.routes[selectedRun].description); | 177 | setMd(data.summary.routes[selectedRun].description); |
| 148 | } | 178 | } |
| 149 | }, [menu]); | 179 | }, [menu, data.summary.routes, selectedRun]); |
| 150 | 180 | ||
| 151 | React.useEffect(() => { | 181 | React.useEffect(() => { |
| 152 | const modview = document.querySelector("div#modview") as HTMLElement | 182 | const modview = document.querySelector("div#modview") as HTMLElement; |
| 153 | if (modview) { | 183 | if (modview) { |
| 154 | showButton ? modview.style.transform = "translateY(-68%)" | 184 | showButton |
| 155 | : modview.style.transform = "translateY(0%)" | 185 | ? (modview.style.transform = "translateY(-68%)") |
| 186 | : (modview.style.transform = "translateY(0%)"); | ||
| 156 | } | 187 | } |
| 157 | 188 | ||
| 158 | const modview_block = document.querySelector("#modview_block") as HTMLElement | 189 | const modview_block = document.querySelector( |
| 190 | "#modview_block" | ||
| 191 | ) as HTMLElement; | ||
| 159 | if (modview_block) { | 192 | if (modview_block) { |
| 160 | showButton ? modview_block.style.display = "none" : modview_block.style.display = "block" | 193 | showButton |
| 194 | ? (modview_block.style.display = "none") | ||
| 195 | : (modview_block.style.display = "block"); | ||
| 161 | } | 196 | } |
| 162 | }, [showButton]) | 197 | }, [showButton]); |
| 163 | 198 | ||
| 164 | return ( | 199 | return ( |
| 165 | <> | 200 | <> |
| 166 | {ConfirmDialogComponent} | 201 | {ConfirmDialogComponent} |
| 167 | <div id="modview_block" /> | 202 | <div id="modview_block" /> |
| 168 | <div id='modview'> | 203 | <div id="modview"> |
| 169 | <div> | 204 | <div> |
| 170 | <button onClick={() => setMenu(1)}>Edit Image</button> | 205 | <button onClick={() => setMenu(1)}>Edit Image</button> |
| 171 | <button onClick={() => setMenu(2)}>Edit Selected Route</button> | 206 | <button onClick={() => setMenu(2)}>Edit Selected Route</button> |
| 172 | <button onClick={() => setMenu(3)}>Add New Route</button> | 207 | <button onClick={() => setMenu(3)}>Add New Route</button> |
| 173 | <button onClick={() => _delete_map_summary_route()}>Delete Selected Route</button> | 208 | <button onClick={() => _delete_map_summary_route()}> |
| 209 | Delete Selected Route | ||
| 210 | </button> | ||
| 174 | </div> | 211 | </div> |
| 175 | <div> | 212 | <div> |
| 176 | {showButton ? ( | 213 | {showButton ? ( |
| 177 | <button onClick={() => setShowButton(false)}>Show</button> | 214 | <button onClick={() => setShowButton(false)}>Show</button> |
| 178 | ) : ( | 215 | ) : ( |
| 179 | <button onClick={() => { setShowButton(true); setMenu(0); }}>Hide</button> | 216 | <button |
| 217 | onClick={() => { | ||
| 218 | setShowButton(true); | ||
| 219 | setMenu(0); | ||
| 220 | }} | ||
| 221 | > | ||
| 222 | Hide | ||
| 223 | </button> | ||
| 180 | )} | 224 | )} |
| 181 | </div> | 225 | </div> |
| 182 | </div><div id='modview-menu'> | 226 | </div> |
| 183 | {// Edit Image | 227 | <div id="modview-menu"> |
| 228 | { | ||
| 229 | // Edit Image | ||
| 184 | menu === 1 && ( | 230 | menu === 1 && ( |
| 185 | <div id='modview-menu-image'> | 231 | <div id="modview-menu-image"> |
| 186 | <div> | 232 | <div> |
| 187 | <span>Current Image:</span> | 233 | <span>Current Image:</span> |
| 188 | <img src={data.map.image} alt="missing" /> | 234 | <img src={data.map.image} alt="missing" /> |
| 189 | </div> | 235 | </div> |
| 190 | 236 | ||
| 191 | <div> | 237 | <div> |
| 192 | <span>New Image: | 238 | <span> |
| 193 | <input type="file" accept='image/*' onChange={e => { | 239 | New Image: |
| 194 | if (e.target.files) { | 240 | <input |
| 195 | compressImage(e.target.files[0]) | 241 | type="file" |
| 196 | .then(d => setImage(d)); | 242 | accept="image/*" |
| 197 | } | 243 | onChange={(e) => { |
| 198 | }} /></span> | 244 | if (e.target.files) { |
| 199 | {image ? (<button onClick={() => _edit_map_summary_image()}>upload</button>) : <span></span>} | 245 | compressImage(e.target.files[0]).then((d) => |
| 200 | <img src={image} alt="" id='modview-menu-image-file' /> | 246 | setImage(d) |
| 201 | 247 | ); | |
| 248 | } | ||
| 249 | }} | ||
| 250 | /> | ||
| 251 | </span> | ||
| 252 | {image ? ( | ||
| 253 | <button onClick={() => _edit_map_summary_image()}> | ||
| 254 | upload | ||
| 255 | </button> | ||
| 256 | ) : ( | ||
| 257 | <span></span> | ||
| 258 | )} | ||
| 259 | <img src={image} alt="" id="modview-menu-image-file" /> | ||
| 202 | </div> | 260 | </div> |
| 203 | </div> | 261 | </div> |
| 204 | )} | 262 | ) |
| 263 | } | ||
| 205 | 264 | ||
| 206 | {// Edit Route | 265 | { |
| 266 | // Edit Route | ||
| 207 | menu === 2 && ( | 267 | menu === 2 && ( |
| 208 | <div id='modview-menu-edit'> | 268 | <div id="modview-menu-edit"> |
| 209 | <div id='modview-route-id'> | 269 | <div id="modview-route-id"> |
| 210 | <span>Route ID:</span> | 270 | <span>Route ID:</span> |
| 211 | <input type="number" value={routeContent.id} disabled /> | 271 | <input type="number" value={routeContent.id} disabled /> |
| 212 | </div> | 272 | </div> |
| 213 | <div id='modview-route-name'> | 273 | <div id="modview-route-name"> |
| 214 | <span>Runner Name:</span> | 274 | <span>Runner Name:</span> |
| 215 | <input type="text" value={routeContent.name} onChange={(e) => { | 275 | <input |
| 216 | setRouteContent({ | 276 | type="text" |
| 217 | ...routeContent, | 277 | value={routeContent.name} |
| 218 | name: e.target.value, | 278 | onChange={(e) => { |
| 219 | }); | 279 | setRouteContent({ |
| 220 | }} /> | 280 | ...routeContent, |
| 281 | name: e.target.value, | ||
| 282 | }); | ||
| 283 | }} | ||
| 284 | /> | ||
| 221 | </div> | 285 | </div> |
| 222 | <div id='modview-route-score'> | 286 | <div id="modview-route-score"> |
| 223 | <span>Score:</span> | 287 | <span>Score:</span> |
| 224 | <input type="number" value={routeContent.score} onChange={(e) => { | 288 | <input |
| 225 | setRouteContent({ | 289 | type="number" |
| 226 | ...routeContent, | 290 | value={routeContent.score} |
| 227 | score: parseInt(e.target.value), | 291 | onChange={(e) => { |
| 228 | }); | 292 | setRouteContent({ |
| 229 | }} /> | 293 | ...routeContent, |
| 294 | score: parseInt(e.target.value), | ||
| 295 | }); | ||
| 296 | }} | ||
| 297 | /> | ||
| 230 | </div> | 298 | </div> |
| 231 | <div id='modview-route-date'> | 299 | <div id="modview-route-date"> |
| 232 | <span>Date:</span> | 300 | <span>Date:</span> |
| 233 | <input type="date" value={routeContent.date} onChange={(e) => { | 301 | <input |
| 234 | setRouteContent({ | 302 | type="date" |
| 235 | ...routeContent, | 303 | value={routeContent.date} |
| 236 | date: e.target.value, | 304 | onChange={(e) => { |
| 237 | }); | 305 | setRouteContent({ |
| 238 | }} /> | 306 | ...routeContent, |
| 307 | date: e.target.value, | ||
| 308 | }); | ||
| 309 | }} | ||
| 310 | /> | ||
| 239 | </div> | 311 | </div> |
| 240 | <div id='modview-route-showcase'> | 312 | <div id="modview-route-showcase"> |
| 241 | <span>Showcase Video:</span> | 313 | <span>Showcase Video:</span> |
| 242 | <input type="text" value={routeContent.showcase} onChange={(e) => { | 314 | <input |
| 243 | setRouteContent({ | 315 | type="text" |
| 244 | ...routeContent, | 316 | value={routeContent.showcase} |
| 245 | showcase: e.target.value, | 317 | onChange={(e) => { |
| 246 | }); | 318 | setRouteContent({ |
| 247 | }} /> | 319 | ...routeContent, |
| 320 | showcase: e.target.value, | ||
| 321 | }); | ||
| 322 | }} | ||
| 323 | /> | ||
| 248 | </div> | 324 | </div> |
| 249 | <div id='modview-route-description' style={{ height: "180px", gridColumn: "1 / span 5" }}> | 325 | <div |
| 326 | id="modview-route-description" | ||
| 327 | style={{ height: "180px", gridColumn: "1 / span 5" }} | ||
| 328 | > | ||
| 250 | <span>Description:</span> | 329 | <span>Description:</span> |
| 251 | <textarea value={routeContent.description} onChange={(e) => { | 330 | <textarea |
| 252 | setRouteContent({ | 331 | value={routeContent.description} |
| 253 | ...routeContent, | 332 | onChange={(e) => { |
| 254 | description: e.target.value, | 333 | setRouteContent({ |
| 255 | }); | 334 | ...routeContent, |
| 256 | setMd(routeContent.description); | 335 | description: e.target.value, |
| 257 | }} /> | 336 | }); |
| 337 | setMd(routeContent.description); | ||
| 338 | }} | ||
| 339 | /> | ||
| 258 | </div> | 340 | </div> |
| 259 | <button style={{ gridColumn: "2 / span 3", height: "40px" }} onClick={_edit_map_summary_route}>Apply</button> | 341 | <button |
| 342 | style={{ gridColumn: "2 / span 3", height: "40px" }} | ||
| 343 | onClick={_edit_map_summary_route} | ||
| 344 | > | ||
| 345 | Apply | ||
| 346 | </button> | ||
| 260 | 347 | ||
| 261 | <div id='modview-md'> | 348 | <div id="modview-md"> |
| 262 | <span>Markdown Preview</span> | 349 | <span>Markdown Preview</span> |
| 263 | <span><a href="https://commonmark.org/help/" rel="noreferrer" target='_blank'>Documentation</a></span> | 350 | <span> |
| 264 | <span><a href="https://remarkjs.github.io/react-markdown/" rel="noreferrer" target='_blank'>Demo</a></span> | 351 | <a |
| 352 | href="https://commonmark.org/help/" | ||
| 353 | rel="noreferrer" | ||
| 354 | target="_blank" | ||
| 355 | > | ||
| 356 | Documentation | ||
| 357 | </a> | ||
| 358 | </span> | ||
| 359 | <span> | ||
| 360 | <a | ||
| 361 | href="https://remarkjs.github.io/react-markdown/" | ||
| 362 | rel="noreferrer" | ||
| 363 | target="_blank" | ||
| 364 | > | ||
| 365 | Demo | ||
| 366 | </a> | ||
| 367 | </span> | ||
| 265 | <p> | 368 | <p> |
| 266 | <ReactMarkdown>{md} | 369 | <ReactMarkdown>{md}</ReactMarkdown> |
| 267 | </ReactMarkdown> | ||
| 268 | </p> | 370 | </p> |
| 269 | </div> | 371 | </div> |
| 270 | </div> | 372 | </div> |
| 271 | )} | 373 | ) |
| 374 | } | ||
| 272 | 375 | ||
| 273 | {// Add Route | 376 | { |
| 377 | // Add Route | ||
| 274 | menu === 3 && ( | 378 | menu === 3 && ( |
| 275 | <div id='modview-menu-add'> | 379 | <div id="modview-menu-add"> |
| 276 | <div id='modview-route-category'> | 380 | <div id="modview-route-category"> |
| 277 | <span>Category:</span> | 381 | <span>Category:</span> |
| 278 | <select onChange={(e) => { | 382 | <select |
| 279 | setRouteContent({ | 383 | onChange={(e) => { |
| 280 | ...routeContent, | 384 | setRouteContent({ |
| 281 | category_id: parseInt(e.target.value), | 385 | ...routeContent, |
| 282 | }); | 386 | category_id: parseInt(e.target.value), |
| 283 | }}> | 387 | }); |
| 284 | <option value="1" key="1">CM</option> | 388 | }} |
| 285 | <option value="2" key="2">No SLA</option> | 389 | > |
| 286 | {data.map.game_name === "Portal 2 - Cooperative" ? "" : ( | 390 | <option value="1" key="1"> |
| 287 | <option value="3" key="3">Inbounds SLA</option>)} | 391 | CM |
| 288 | <option value="4" key="4">Any%</option> | 392 | </option> |
| 393 | <option value="2" key="2"> | ||
| 394 | No SLA | ||
| 395 | </option> | ||
| 396 | {data.map.game_name === "Portal 2 - Cooperative" ? ( | ||
| 397 | "" | ||
| 398 | ) : ( | ||
| 399 | <option value="3" key="3"> | ||
| 400 | Inbounds SLA | ||
| 401 | </option> | ||
| 402 | )} | ||
| 403 | <option value="4" key="4"> | ||
| 404 | Any% | ||
| 405 | </option> | ||
| 289 | </select> | 406 | </select> |
| 290 | </div> | 407 | </div> |
| 291 | <div id='modview-route-name'> | 408 | <div id="modview-route-name"> |
| 292 | <span>Runner Name:</span> | 409 | <span>Runner Name:</span> |
| 293 | <input type="text" value={routeContent.name} onChange={(e) => { | 410 | <input |
| 294 | setRouteContent({ | 411 | type="text" |
| 295 | ...routeContent, | 412 | value={routeContent.name} |
| 296 | name: e.target.value, | 413 | onChange={(e) => { |
| 297 | }); | 414 | setRouteContent({ |
| 298 | }} /> | 415 | ...routeContent, |
| 416 | name: e.target.value, | ||
| 417 | }); | ||
| 418 | }} | ||
| 419 | /> | ||
| 299 | </div> | 420 | </div> |
| 300 | <div id='modview-route-score'> | 421 | <div id="modview-route-score"> |
| 301 | <span>Score:</span> | 422 | <span>Score:</span> |
| 302 | <input type="number" value={routeContent.score} onChange={(e) => { | 423 | <input |
| 303 | setRouteContent({ | 424 | type="number" |
| 304 | ...routeContent, | 425 | value={routeContent.score} |
| 305 | score: parseInt(e.target.value), | 426 | onChange={(e) => { |
| 306 | }); | 427 | setRouteContent({ |
| 307 | }} /> | 428 | ...routeContent, |
| 429 | score: parseInt(e.target.value), | ||
| 430 | }); | ||
| 431 | }} | ||
| 432 | /> | ||
| 308 | </div> | 433 | </div> |
| 309 | <div id='modview-route-date'> | 434 | <div id="modview-route-date"> |
| 310 | <span>Date:</span> | 435 | <span>Date:</span> |
| 311 | <input type="date" value={routeContent.date} onChange={(e) => { | 436 | <input |
| 312 | setRouteContent({ | 437 | type="date" |
| 313 | ...routeContent, | 438 | value={routeContent.date} |
| 314 | date: e.target.value, | 439 | onChange={(e) => { |
| 315 | }); | 440 | setRouteContent({ |
| 316 | }} /> | 441 | ...routeContent, |
| 442 | date: e.target.value, | ||
| 443 | }); | ||
| 444 | }} | ||
| 445 | /> | ||
| 317 | </div> | 446 | </div> |
| 318 | <div id='modview-route-showcase'> | 447 | <div id="modview-route-showcase"> |
| 319 | <span>Showcase Video:</span> | 448 | <span>Showcase Video:</span> |
| 320 | <input type="text" value={routeContent.showcase} onChange={(e) => { | 449 | <input |
| 321 | setRouteContent({ | 450 | type="text" |
| 322 | ...routeContent, | 451 | value={routeContent.showcase} |
| 323 | showcase: e.target.value, | 452 | onChange={(e) => { |
| 324 | }); | 453 | setRouteContent({ |
| 325 | }} /> | 454 | ...routeContent, |
| 455 | showcase: e.target.value, | ||
| 456 | }); | ||
| 457 | }} | ||
| 458 | /> | ||
| 326 | </div> | 459 | </div> |
| 327 | <div id='modview-route-description' style={{ height: "180px", gridColumn: "1 / span 5" }}> | 460 | <div |
| 461 | id="modview-route-description" | ||
| 462 | style={{ height: "180px", gridColumn: "1 / span 5" }} | ||
| 463 | > | ||
| 328 | <span>Description:</span> | 464 | <span>Description:</span> |
| 329 | <textarea value={routeContent.description} onChange={(e) => { | 465 | <textarea |
| 330 | setRouteContent({ | 466 | value={routeContent.description} |
| 331 | ...routeContent, | 467 | onChange={(e) => { |
| 332 | description: e.target.value, | 468 | setRouteContent({ |
| 333 | }); | 469 | ...routeContent, |
| 334 | setMd(routeContent.description); | 470 | description: e.target.value, |
| 335 | }} /> | 471 | }); |
| 472 | setMd(routeContent.description); | ||
| 473 | }} | ||
| 474 | /> | ||
| 336 | </div> | 475 | </div> |
| 337 | <button style={{ gridColumn: "2 / span 3", height: "40px" }} onClick={_create_map_summary_route}>Apply</button> | 476 | <button |
| 477 | style={{ gridColumn: "2 / span 3", height: "40px" }} | ||
| 478 | onClick={_create_map_summary_route} | ||
| 479 | > | ||
| 480 | Apply | ||
| 481 | </button> | ||
| 338 | 482 | ||
| 339 | <div id='modview-md'> | 483 | <div id="modview-md"> |
| 340 | <span>Markdown preview</span> | 484 | <span>Markdown preview</span> |
| 341 | <span><a href="https://commonmark.org/help/" rel="noreferrer" target='_blank'>documentation</a></span> | 485 | <span> |
| 342 | <span><a href="https://remarkjs.github.io/react-markdown/" rel="noreferrer" target='_blank'>demo</a></span> | 486 | <a |
| 487 | href="https://commonmark.org/help/" | ||
| 488 | rel="noreferrer" | ||
| 489 | target="_blank" | ||
| 490 | > | ||
| 491 | documentation | ||
| 492 | </a> | ||
| 493 | </span> | ||
| 494 | <span> | ||
| 495 | <a | ||
| 496 | href="https://remarkjs.github.io/react-markdown/" | ||
| 497 | rel="noreferrer" | ||
| 498 | target="_blank" | ||
| 499 | > | ||
| 500 | demo | ||
| 501 | </a> | ||
| 502 | </span> | ||
| 343 | <p> | 503 | <p> |
| 344 | <ReactMarkdown>{md} | 504 | <ReactMarkdown>{md}</ReactMarkdown> |
| 345 | </ReactMarkdown> | ||
| 346 | </p> | 505 | </p> |
| 347 | </div> | 506 | </div> |
| 348 | </div> | 507 | </div> |
| 349 | )} | 508 | ) |
| 509 | } | ||
| 350 | </div> | 510 | </div> |
| 351 | </> | 511 | </> |
| 352 | ); | 512 | ); |