aboutsummaryrefslogtreecommitdiff
path: root/frontend/src/components/ModMenu.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'frontend/src/components/ModMenu.tsx')
-rw-r--r--frontend/src/components/ModMenu.tsx324
1 files changed, 324 insertions, 0 deletions
diff --git a/frontend/src/components/ModMenu.tsx b/frontend/src/components/ModMenu.tsx
new file mode 100644
index 0000000..1fe4239
--- /dev/null
+++ b/frontend/src/components/ModMenu.tsx
@@ -0,0 +1,324 @@
1import React from 'react';
2import ReactMarkdown from 'react-markdown';
3
4import { MapSummary } from '../types/Map';
5import { ModMenuContent } from '../types/Content';
6import { API } from '../api/Api';
7import "../css/ModMenu.css"
8
9interface ModMenuProps {
10 data: MapSummary;
11 selectedRun: number;
12 mapID: string;
13}
14
15const ModMenu: React.FC<ModMenuProps> = ({ data, selectedRun, mapID }) => {
16
17 const [menu, setMenu] = React.useState<number>(0);
18 const [showButton, setShowButton] = React.useState(1)
19
20 const [routeContent, setRouteContent] = React.useState<ModMenuContent>({
21 id: 0,
22 name: "",
23 score: 0,
24 date: "",
25 showcase: "",
26 description: "No description available.",
27 category_id: 1,
28 });
29
30 const [image, setImage] = React.useState<string>("");
31 const [md, setMd] = React.useState<string>("");
32
33 function compressImage(file: File): Promise<string> {
34 const reader = new FileReader();
35 reader.readAsDataURL(file);
36 return new Promise(resolve => {
37 reader.onload = () => {
38 const img = new Image();
39 if (typeof reader.result === "string") {
40 img.src = reader.result;
41 img.onload = () => {
42 let { width, height } = img;
43 if (width > 550) {
44 height *= 550 / width;
45 width = 550;
46 }
47 if (height > 320) {
48 width *= 320 / height;
49 height = 320;
50 }
51 const canvas = document.createElement('canvas');
52 canvas.width = width;
53 canvas.height = height;
54 canvas.getContext('2d')!.drawImage(img, 0, 0, width, height);
55 resolve(canvas.toDataURL(file.type, 0.6));
56 };
57 }
58 };
59 });
60 };
61
62 const _edit_map_summary_image = async () => {
63 if (window.confirm("Are you sure you want to submit this to the database?")) {
64 await API.put_map_image(mapID, image);
65 }
66 };
67
68 const _edit_map_summary_route = async () => {
69 if (window.confirm("Are you sure you want to submit this to the database?")) {
70 await API.put_map_summary(mapID, routeContent);
71 }
72 };
73
74 const _create_map_summary_route = async () => {
75 if (window.confirm("Are you sure you want to submit this to the database?")) {
76 await API.post_map_summary(mapID, routeContent);
77 }
78 };
79
80 const _delete_map_summary_route = async () => {
81 if (window.confirm(`Are you sure you want to delete this run from the database?
82 ${data.summary.routes[selectedRun].category.name} ${data.summary.routes[selectedRun].history.score_count} portals ${data.summary.routes[selectedRun].history.runner_name}`)) {
83 await API.delete_map_summary(mapID, data.summary.routes[selectedRun].route_id);
84 }
85 };
86
87 React.useEffect(() => {
88 if (menu === 3) { // add route
89 setRouteContent({
90 id: 0,
91 name: "",
92 score: 0,
93 date: "",
94 showcase: "",
95 description: "No description available.",
96 category_id: 1,
97 });
98 setMd("No description available.");
99 }
100 if (menu === 2) { // edit route
101 setRouteContent({
102 id: data.summary.routes[selectedRun].route_id,
103 name: data.summary.routes[selectedRun].history.runner_name,
104 score: data.summary.routes[selectedRun].history.score_count,
105 date: data.summary.routes[selectedRun].history.date.split("T")[0],
106 showcase: data.summary.routes[selectedRun].showcase,
107 description: data.summary.routes[selectedRun].description,
108 category_id: data.summary.routes[selectedRun].category.id,
109 });
110 setMd(data.summary.routes[selectedRun].description);
111 }
112 }, [menu]);
113
114 React.useEffect(() => {
115 const modview = document.querySelector("div#modview") as HTMLElement
116 if (modview) {
117 showButton ? modview.style.transform = "translateY(-68%)"
118 : modview.style.transform = "translateY(0%)"
119 }
120
121 const modview_block = document.querySelector("#modview_block") as HTMLElement
122 if (modview_block) {
123 showButton === 1 ? modview_block.style.display = "none" : modview_block.style.display = "block"// eslint-disable-next-line
124 }
125 }, [showButton])
126
127 return (
128 <div id="modview_bdlock">
129
130 <div id='modview'>
131 <div>
132 <button onClick={() => setMenu(1)}>Edit Image</button>
133 <button onClick={() => setMenu(2)}>Edit Selected Route</button>
134 <button onClick={() => setMenu(3)}>Add New Route</button>
135 <button onClick={() => _delete_map_summary_route()}>Delete Selected Route</button>
136 </div>
137 <div>
138 {showButton ? (
139 <button onClick={() => setShowButton(0)}>Show</button>
140 ) : (
141 <button onClick={() => { setShowButton(1); setMenu(0) }}>Hide</button>
142 )}
143 </div>
144 </div>
145
146 <div id='modview-menu'>
147 { // Edit Image
148 menu === 1 && (
149 <div id='modview-menu-image'>
150 <div>
151 <span>Current Image:</span>
152 <img src={data.map.image} alt="missing" />
153 </div>
154
155 <div>
156 <span>New Image:
157 <input type="file" accept='image/*' onChange={e => {
158 if (e.target.files) {
159 compressImage(e.target.files[0])
160 .then(d => setImage(d))
161 }
162 }
163 } /></span>
164 {image ? (<button onClick={() => _edit_map_summary_image()}>upload</button>) : <span></span>}
165 <img src={image} alt="" id='modview-menu-image-file' />
166
167 </div>
168 </div>
169 )
170 }
171
172 { // Edit Route
173 menu === 2 && (
174 <div id='modview-menu-edit'>
175 <div id='modview-route-id'>
176 <span>Route ID:</span>
177 <input type="number" value={routeContent.id} disabled />
178 </div>
179 <div id='modview-route-name'>
180 <span>Runner Name:</span>
181 <input type="text" value={routeContent.name} onChange={(e) => {
182 setRouteContent({
183 ...routeContent,
184 name: e.target.value,
185 });
186 }} />
187 </div>
188 <div id='modview-route-score'>
189 <span>Score:</span>
190 <input type="number" value={routeContent.score} onChange={(e) => {
191 setRouteContent({
192 ...routeContent,
193 score: parseInt(e.target.value),
194 });
195 }} />
196 </div>
197 <div id='modview-route-date'>
198 <span>Date:</span>
199 <input type="date" value={routeContent.date} onChange={(e) => {
200 setRouteContent({
201 ...routeContent,
202 date: e.target.value,
203 });
204 }} />
205 </div>
206 <div id='modview-route-showcase'>
207 <span>Showcase Video:</span>
208 <input type="text" value={routeContent.showcase} onChange={(e) => {
209 setRouteContent({
210 ...routeContent,
211 showcase: e.target.value,
212 });
213 }} />
214 </div>
215 <div id='modview-route-description' style={{ height: "180px", gridColumn: "1 / span 5" }}>
216 <span>Description:</span>
217 <textarea value={routeContent.description} onChange={(e) => {
218 setRouteContent({
219 ...routeContent,
220 description: e.target.value,
221 });
222 setMd(routeContent.description);
223 }} />
224 </div>
225 <button style={{ gridColumn: "2 / span 3", height: "40px" }} onClick={_edit_map_summary_route}>Apply</button>
226
227 <div id='modview-md'>
228 <span>Markdown Preview</span>
229 <span><a href="https://commonmark.org/help/" rel="noreferrer" target='_blank'>Documentation</a></span>
230 <span><a href="https://remarkjs.github.io/react-markdown/" rel="noreferrer" target='_blank'>Demo</a></span>
231 <p>
232 <ReactMarkdown>{md}
233 </ReactMarkdown>
234 </p>
235 </div>
236 </div>
237 )
238 }
239
240 { // Add Route
241 menu === 3 && (
242 <div id='modview-menu-add'>
243 <div id='modview-route-category'>
244 <span>Category:</span>
245 <select onChange={(e) => {
246 setRouteContent({
247 ...routeContent,
248 category_id: parseInt(e.target.value),
249 });
250 }}>
251 <option value="1" key="1">CM</option>
252 <option value="2" key="2">No SLA</option>
253 {data.map.game_name === "Portal 2 - Cooperative" ? "" : (
254 <option value="3" key="3">Inbounds SLA</option>)}
255 <option value="4" key="4">Any%</option>
256 </select>
257 </div>
258 <div id='modview-route-name'>
259 <span>Runner Name:</span>
260 <input type="text" value={routeContent.name} onChange={(e) => {
261 setRouteContent({
262 ...routeContent,
263 name: e.target.value,
264 });
265 }} />
266 </div>
267 <div id='modview-route-score'>
268 <span>Score:</span>
269 <input type="number" value={routeContent.score} onChange={(e) => {
270 setRouteContent({
271 ...routeContent,
272 score: parseInt(e.target.value),
273 });
274 }} />
275 </div>
276 <div id='modview-route-date'>
277 <span>Date:</span>
278 <input type="date" value={routeContent.date} onChange={(e) => {
279 setRouteContent({
280 ...routeContent,
281 date: e.target.value,
282 });
283 }} />
284 </div>
285 <div id='modview-route-showcase'>
286 <span>Showcase Video:</span>
287 <input type="text" value={routeContent.showcase} onChange={(e) => {
288 setRouteContent({
289 ...routeContent,
290 showcase: e.target.value,
291 });
292 }} />
293 </div>
294 <div id='modview-route-description' style={{ height: "180px", gridColumn: "1 / span 5" }}>
295 <span>Description:</span>
296 <textarea value={routeContent.description} onChange={(e) => {
297 setRouteContent({
298 ...routeContent,
299 description: e.target.value,
300 });
301 setMd(routeContent.description);
302 }} />
303 </div>
304 <button style={{ gridColumn: "2 / span 3", height: "40px" }} onClick={_create_map_summary_route}>Apply</button>
305
306 <div id='modview-md'>
307 <span>Markdown preview</span>
308 <span><a href="https://commonmark.org/help/" rel="noreferrer" target='_blank'>documentation</a></span>
309 <span><a href="https://remarkjs.github.io/react-markdown/" rel="noreferrer" target='_blank'>demo</a></span>
310 <p>
311 <ReactMarkdown>{md}
312 </ReactMarkdown>
313 </p>
314 </div>
315 </div>
316 )
317 }
318 </div>
319
320 </div>
321 );
322};
323
324export default ModMenu;