AECC database project.

feat(product): Finished products.

+773 -278
+10
components/include.js
··· 1 + class includeHTML extends HTMLElement { 2 + connectedCallback() { 3 + const src = this.getAttribute("src"); 4 + fetch(src).then(response => response.text()).then(data => this.outerHTML = data); 5 + } 6 + } 7 + 8 + export function registerIncludeComponent() { 9 + customElements.define("x-include", includeHTML); 10 + }
+426
index.css
··· 1 + @import url("./reset.css"); 2 + 3 + :root { 4 + --font-family: monospace; 5 + --line-height: 1.15rem; 6 + --border-thickness: 2px; 7 + 8 + --font-weight-normal: 500; 9 + --font-weight-medium: 600; 10 + --font-weight-bold: 800; 11 + 12 + --text-color: #ddd; 13 + --background-color: #080808; 14 + --color-yellow: #e3c78a; 15 + --color-blue: #80a0ff; 16 + 17 + font-family: var(--font-family); 18 + font-optical-sizing: auto; 19 + font-weight: var(--font-weight-normal); 20 + font-style: normal; 21 + font-variant-numeric: tabular-nums lining-nums; 22 + font-size: 16px; 23 + } 24 + 25 + * { 26 + box-sizing: border-box; 27 + } 28 + 29 + 30 + * + * { 31 + margin-top: var(--line-height); 32 + } 33 + 34 + html { 35 + display: flex; 36 + width: 100%; 37 + margin: 0; 38 + padding: 0; 39 + flex-direction: column; 40 + align-items: center; 41 + } 42 + 43 + body { 44 + position: relative; 45 + width: 100%; 46 + margin: 0; 47 + padding: var(--line-height) 2ch; 48 + max-width: calc(min(90ch, round(down, 100%, 1ch))); 49 + line-height: var(--line-height); 50 + overflow-x: hidden; 51 + background-color: var(--background-color); 52 + color: var(--text-color); 53 + } 54 + 55 + @media screen and (max-width: 480px) { 56 + :root { 57 + font-size: 16px; 58 + } 59 + body { 60 + padding: var(--line-height) 1ch; 61 + } 62 + } 63 + 64 + img { 65 + max-width: 61.8%; 66 + height: auto; 67 + margin-left: auto; 68 + margin-right: auto; 69 + } 70 + 71 + h1, h2, h3, h4, h5, h6 { 72 + font-weight: var(--font-weight-bold); 73 + margin: calc(var(--line-height) * 2) 0 var(--line-height); 74 + line-height: var(--line-height); 75 + } 76 + 77 + h1 { 78 + font-size: 2rem; 79 + line-height: calc(2 * var(--line-height)); 80 + margin-bottom: calc(var(--line-height) * 2); 81 + text-transform: uppercase; 82 + } 83 + 84 + h2 { 85 + font-size: 1rem; 86 + text-transform: uppercase; 87 + } 88 + 89 + hr { 90 + position: relative; 91 + display: block; 92 + height: var(--line-height); 93 + margin: calc(var(--line-height) * 1.5) 0; 94 + border: none; 95 + color: var(--text-color); 96 + } 97 + 98 + hr:after { 99 + display: block; 100 + content: ""; 101 + position: absolute; 102 + top: calc(var(--line-height) / 2 - var(--border-thickness)); 103 + left: 0; 104 + width: 100%; 105 + border-top: calc(var(--border-thickness) * 3) double var(--text-color); 106 + height: 0; 107 + } 108 + 109 + a { 110 + text-decoration-thickness: var(--border-thickness); 111 + } 112 + 113 + a:link, a:visited { 114 + color: var(--text-color); 115 + } 116 + 117 + p { 118 + font-size: 1em; 119 + margin-bottom: var(--line-height); 120 + } 121 + 122 + strong { 123 + font-weight: var(--font-weight-bold); 124 + } 125 + 126 + em { 127 + font-style: italic; 128 + } 129 + 130 + sub { 131 + position: relative; 132 + display: inline-block; 133 + margin: 0; 134 + vertical-align: sub; 135 + line-height: 0; 136 + width: calc(1ch / 0.75); 137 + font-size: .75rem; 138 + } 139 + 140 + table { 141 + position: relative; 142 + border-collapse: collapse; 143 + top: calc(var(--line-height) / 2); 144 + width: calc(round(down, 100%, 1ch)); 145 + margin: 0 0 calc(var(--line-height) * 2); 146 + } 147 + 148 + th, td { 149 + border: var(--border-thickness) solid var(--text-color); 150 + padding: 151 + calc((var(--line-height) / 2)) 152 + calc(1ch - var(--border-thickness) / 2) 153 + calc((var(--line-height) / 2) - (var(--border-thickness))) 154 + ; 155 + line-height: var(--line-height); 156 + vertical-align: top; 157 + text-align: left; 158 + } 159 + 160 + table tbody tr:first-child > * { 161 + padding-top: calc((var(--line-height) / 2) - var(--border-thickness)); 162 + } 163 + 164 + th { 165 + font-weight: 700; 166 + } 167 + 168 + .width-min { 169 + width: 0%; 170 + } 171 + 172 + .width-auto { 173 + width: 100%; 174 + } 175 + 176 + .header { 177 + margin-bottom: calc(var(--line-height) * 2); 178 + } 179 + 180 + .header h1 { 181 + margin: 0; 182 + } 183 + 184 + .header tr td:last-child { 185 + text-align: right; 186 + } 187 + 188 + p { 189 + word-break: break-word; 190 + word-wrap: break-word; 191 + hyphens: auto; 192 + } 193 + 194 + img, video { 195 + display: block; 196 + object-fit: contain; 197 + overflow: hidden; 198 + } 199 + 200 + img { 201 + font-style: italic; 202 + color: var(--text-color-alt); 203 + } 204 + 205 + details { 206 + border: var(--border-thickness) solid var(--text-color); 207 + padding: calc(var(--line-height) - var(--border-thickness)) 2em; 208 + margin-bottom: calc(var(--line-height) + 1em); 209 + } 210 + 211 + summary { 212 + font-weight: var(--font-weight-medium); 213 + cursor: pointer; 214 + } 215 + 216 + details[open] summary { 217 + margin-bottom: var(--line-height); 218 + } 219 + 220 + details ::marker { 221 + display: inline-block; 222 + content: '▶'; 223 + margin: 0; 224 + } 225 + 226 + details[open] ::marker { 227 + content: '▼'; 228 + } 229 + 230 + details :last-child { 231 + margin-bottom: 0; 232 + } 233 + 234 + pre { 235 + white-space: pre; 236 + overflow-x: auto; 237 + margin: var(--line-height) 0; 238 + overflow-y: hidden; 239 + } 240 + 241 + figure pre { 242 + margin: 0; 243 + } 244 + 245 + 246 + pre, code { 247 + font-family: "IBM Plex Mono", monospace; 248 + font-variant-numeric: tabular-nums lining-nums; 249 + } 250 + 251 + code { 252 + font-weight: var(--font-weight-medium); 253 + } 254 + 255 + figure { 256 + margin: calc(var(--line-height) * 2) 3ch; 257 + overflow-x: auto; 258 + overflow-y: hidden; 259 + } 260 + 261 + figcaption { 262 + display: block; 263 + font-style: italic; 264 + margin-top: var(--line-height); 265 + } 266 + 267 + ul, ol { 268 + padding: 0; 269 + margin: 0 0 var(--line-height); 270 + } 271 + 272 + ul { 273 + list-style-type: square; 274 + padding: 0 0 0 2ch; 275 + } 276 + 277 + ol { 278 + list-style-type: none; 279 + counter-reset: item; 280 + padding: 0; 281 + } 282 + 283 + ol ul, 284 + ol ol, 285 + ul ol, 286 + ul ul { 287 + padding: 0 0 0 3ch; 288 + margin: 0; 289 + } 290 + 291 + ol li:before { 292 + content: counters(item, ".") ". "; 293 + counter-increment: item; 294 + font-weight: var(--font-weight-medium); 295 + } 296 + 297 + li { 298 + margin: 0; 299 + padding: 0; 300 + } 301 + 302 + li::marker { 303 + line-height: 0; 304 + } 305 + 306 + ::-webkit-scrollbar { 307 + height: var(--line-height); 308 + } 309 + 310 + input, button, textarea, select { 311 + border: var(--border-thickness) solid var(--text-color); 312 + padding: 313 + calc(var(--line-height) / 2 - var(--border-thickness)) 314 + calc(1ch - var(--border-thickness)); 315 + margin: 0; 316 + font: inherit; 317 + font-weight: inherit; 318 + height: calc(var(--line-height) * 2); 319 + width: auto; 320 + overflow: visible; 321 + background: var(--background-color); 322 + color: var(--text-color); 323 + line-height: normal; 324 + transition: 400ms; 325 + -webkit-font-smoothing: inherit; 326 + -moz-osx-font-smoothing: inherit; 327 + -webkit-appearance: none; 328 + } 329 + 330 + input:hover, button:hover, textarea:hover, select:hover { 331 + border-color: var(--color-blue); 332 + transition: 400ms; 333 + } 334 + 335 + input[type=checkbox] { 336 + display: inline-grid; 337 + place-content: center; 338 + vertical-align: top; 339 + width: 2ch; 340 + height: var(--line-height); 341 + cursor: pointer; 342 + } 343 + 344 + input[type=checkbox]:checked:before { 345 + content: ""; 346 + width: 1ch; 347 + height: calc(var(--line-height) / 2); 348 + background: var(--text-color); 349 + } 350 + 351 + button:focus, input:focus { 352 + --border-thickness: 3px; 353 + outline: none; 354 + transition: 100ms; 355 + } 356 + 357 + input { 358 + width: calc(round(down, 100%, 1ch)); 359 + } 360 + 361 + ::placeholder { 362 + color: var(--text-color-alt); 363 + opacity: 1; 364 + } 365 + 366 + ::-ms-input-placeholder { 367 + color: var(--text-color-alt); 368 + } 369 + 370 + button::-moz-focus-inner { 371 + padding: 0; 372 + border: 0; 373 + } 374 + 375 + button { 376 + text-transform: uppercase; 377 + font-weight: var(--font-weight-medium); 378 + cursor: pointer; 379 + } 380 + 381 + button:hover { 382 + background: var(--background-color-alt); 383 + } 384 + 385 + button:active { 386 + transform: translate(2px, 2px); 387 + } 388 + 389 + label { 390 + display: block; 391 + width: calc(round(down, 100%, 1ch)); 392 + height: auto; 393 + line-height: var(--line-height); 394 + font-weight: var(--font-weight-medium); 395 + margin: 0; 396 + } 397 + 398 + label input { 399 + width: 100%; 400 + } 401 + 402 + br { 403 + text-indent: 0; 404 + margin: 0; 405 + } 406 + 407 + #page-size { 408 + position: fixed; 409 + bottom: 5px; 410 + } 411 + 412 + .input-box { 413 + display: flex; 414 + align-items: center; 415 + border: 2px solid; 416 + } 417 + 418 + .input-box input { 419 + border: none; 420 + outline: none; 421 + } 422 + 423 + .input-box:focus-within, input:focus-within { 424 + border-color: #4f7df3; 425 + transition: 2ms; 426 + }
+44 -196
index.html
··· 1 - <!DOCTYPE html> 2 - <html> 1 + <!doctype html> 2 + <html lang="en"> 3 3 <head> 4 + <title>AECC DataBase</title> 5 + <meta charset="UTF-8" /> 6 + <meta name="viewport" content="width=device-width" /> 7 + 8 + <!-- Favicon settings --> 4 9 <link rel="icon" type="image/png" href="/~diego.estrada1/CCOM/4027/db/assets/favicon-96x96.png" sizes="96x96" /> 5 10 <link rel="icon" type="image/svg+xml" href="/~diego.estrada1/CCOM/4027/db/assets/favicon.svg" /> 6 11 <link rel="shortcut icon" href="/~diego.estrada1/CCOM/4027/db/assets/favicon.ico" /> 7 12 <link rel="apple-touch-icon" sizes="180x180" href="/~diego.estrada1/CCOM/4027/db/assets/apple-touch-icon.png" /> 8 13 <meta name="apple-mobile-web-app-title" content="AECC DB" /> 9 14 <link rel="manifest" href="/~diego.estrada1/CCOM/4027/db/assets/site.webmanifest" /> 10 - <meta charset="UTF-8"> 11 15 12 16 <script src="lib/header.js"></script> 13 - <script src="lib/components.js"></script> 14 - <script>const URL = "https://ada.uprrp.edu/~diego.estrada1/CCOM/4027/db";</script> 17 + <script src="lib/product.js"></script> 18 + <script src="lib/transaction.js"></script> 19 + <link rel="stylesheet" href="index.css"> 15 20 </head> 16 21 <body> 17 - <h1> 18 - Welcome to AECC DB! 19 - </h1> 22 + <noscript> 23 + <strong>Please enable JavaScript.</strong> 24 + This site relies on basic JavaScript functionality and your personal data is not collected. 25 + </noscript> 26 + <header> 27 + <h1> 28 + Welcome to AECC DB! 29 + </h1> 30 + </header> 31 + <hr /> 32 + <main> 33 + <div id="buttons" style="width:120ch;"> 34 + What would you like to work with? 35 + <br /> 36 + <br /> 37 + <button onclick="hide('buttons'); activity();">Activities</button> 38 + <button onclick="hide('buttons'); activity_transaction();">Activity Transactions</button> 39 + <button onclick="hide('buttons'); board_member();">Board Members</button> 40 + <button onclick="hide('buttons'); member();">Members</button> 41 + <button onclick="next('productFunctionality');">Products</button> 42 + <button onclick="next('transactionFunctionality');">Transactions</button> 43 + </div> 20 44 21 - What would you like to work with? 22 - <br /> 45 + <x-include src="product.html"></x-include> 23 46 24 - <div id="buttons" style="width:120ch;"> 25 - <button onclick="hide('buttons'); activity();">Activities</button> 26 - <button onclick="hide('buttons'); activity_transaction();">Activity Transactions</button> 27 - <button onclick="hide('buttons'); board_member();">Board Members</button> 28 - <button onclick="hide('buttons'); member();">Members</button> 29 - <button onclick="next('productFunctionality');">Products</button> 30 - <button onclick="hide('buttons'); transaction();">Transactions</button> 31 - </div> 47 + <x-include src="transaction.html"></x-include> 48 + 49 + <div id="product"> 50 + </div> 32 51 33 - <x-include src="product.html"></x-include> 52 + <div id="member"> 53 + </div> 34 54 55 + <div id="activity"> 56 + </div> 57 + 58 + </main> 35 59 <footer> 36 60 <div style="height:50px"></div> 37 61 <button onclick="restart()">Restart?</button> 38 62 </footer> 63 + <script type="module" src="index.js"></script> 39 64 </body> 40 - <x-include src="/~diego.estrada1/resetcss.html"></x-include> 41 - <x-include src="/~diego.estrada1/css.html"></x-include> 42 - <script> 43 - function fillProductEditor() { 44 - try { 45 - fetch(URL + "/api/v1/read/?t=product").then(response => response.json()).then(json => { 46 - productEditorOptions.innerHTML = ""; 47 - for (var i = 0; i < json.length; i++) { 48 - const obj = json[i]; 49 - const id = obj[0]; 50 - const cents = obj[1]; 51 - const description = obj[2]; 52 - const cost = (cents / 100.00).toFixed(2); 53 - productEditorOptions.innerHTML += ` 54 - <option value="${id}">${description} ($${cost})</option> 55 - `; 56 - } 57 - }); 58 - } catch (error) { 59 - console.error(error.message); 60 - } 61 - } 62 - 63 - function fillProductTable() { 64 - try { 65 - fetch(URL + "/api/v1/read/?t=product").then(response => response.json()).then(json => { 66 - resultsTable.innerHTML = ` 67 - <tr> 68 - <th>ID</th> 69 - <th style="width:10ch;">$USD</th> 70 - <th style="width:80ch;">Description</th> 71 - </tr> 72 - `; 73 - for (var i = 0; i < json.length; i++) { 74 - const obj = json[i]; 75 - const id = obj[0]; 76 - const cents = obj[1]; 77 - const description = obj[2]; 78 - const cost = (cents / 100.00).toFixed(2); 79 - resultsTable.innerHTML += ` 80 - <tr> 81 - <td>${id}</td> 82 - <td>$${cost}</td> 83 - <td>${description}</td> 84 - </tr> 85 - `; 86 - } 87 - }); 88 - } catch (error) { 89 - console.error(error.message); 90 - } 91 - }; 92 - 93 - function loadEditorSubmission() { 94 - const id = productEditorOptions.value; 95 - try { 96 - fetch(URL + "/api/v1/read?t=product&id=" + id).then(response => response.json()).then(json => { 97 - const obj = json[0]; 98 - const id = obj[0]; 99 - const cents = obj[1]; 100 - const description = obj[2]; 101 - const dollars = (cents / 100.0).toFixed(2); 102 - productEditorDollars.text = dollars; 103 - productEditorDescription.text = '"' + description + '"'; 104 - productEditorSubmissionId.value = id; 105 - productEditorSubmissionDollars.value = dollars; 106 - productEditorSubmissionDescription.value = description; 107 - productEditorMenu.hidden = false; 108 - }) 109 - } catch (error) { 110 - console.error(error.message); 111 - } 112 - } 113 - 114 - function productEditorFormHandler(event) { 115 - event.preventDefault(); 116 - const id = event.target[3].value; 117 - const oldDollars = productEditorDollars.text; 118 - const oldDescription = productEditorDescription.text; 119 - const dollars = event.target[0].value; 120 - const cents = dollars * 100; 121 - const description = event.target[1].value 122 - try { 123 - if (confirm(`Changing product: 124 - ID: ${id} 125 - Cost ($USD): $${oldDollars} -> ${dollars} 126 - Description: ${oldDescription} -> ${description} 127 - `)) { 128 - const payload = JSON.stringify({ 129 - t: "product", 130 - p_id: id, 131 - cents: cents, 132 - description: description 133 - }) 134 - fetch(URL + "/api/v1/update/", { 135 - method: "POST", 136 - headers: { 137 - "Content-Type": "application/json" 138 - }, 139 - body: payload 140 - }).then(response => response.json()).then(json => { 141 - alert(`Succesfully updated product ${id}.`); 142 - restart(); 143 - }); 144 - } 145 - } catch (error) { 146 - console.error(error.message); 147 - } 148 - } 149 - 150 - function loadDeleterSubmission() { 151 - const id = productDeleterOptions.value; 152 - try { 153 - fetch(URL + "/api/v1/read?t=product&id=" + id).then(response => response.json()).then(json => { 154 - const obj = json[0]; 155 - const id = obj[0]; 156 - const cents = obj[1]; 157 - const description = obj[2]; 158 - const dollars = (cents / 100.0).toFixed(2); 159 - productDeleterDollars.innerHTML = dollars; 160 - productDeleterDescription.innerHTML = '"' + description + '"'; 161 - productDeleterMenu.hidden = false; 162 - }) 163 - } catch (error) { 164 - console.error(error.message); 165 - } 166 - } 167 - 168 - function deleteProduct() { 169 - const id = productDeleterOptions.value; 170 - const dollars = productDeleterDollars.innerHTML; 171 - const description = productDeleterDescription.innerHTML; 172 - try { 173 - const payload = JSON.stringify({ 174 - t: "product", 175 - p_id: id 176 - }); 177 - if (confirm(`Are you sure you want to delete product ${id}: 178 - Cost ($USD): $${dollars} 179 - Description: ${description}`)) { 180 - fetch(URL + "/api/v1/delete/", { 181 - method: "POST", 182 - headers: { 183 - "Content-Type": "application/json" 184 - }, 185 - body: payload 186 - }).then(response => { 187 - alert("Succesfully deleted product.") 188 - productDeleterMenu.hidden = true; 189 - restart(); 190 - }); 191 - } 192 - } catch(error) { 193 - console.error(error); 194 - } 195 - } 196 - 197 - function fillProductDeleter() { 198 - try { 199 - fetch(URL + "/api/v1/read/?t=product").then(response => response.json()).then(json => { 200 - productDeleterOptions.innerHTML = ""; 201 - for (var i = 0; i < json.length; i++) { 202 - const obj = json[i]; 203 - const id = obj[0]; 204 - const cents = obj[1]; 205 - const description = obj[2]; 206 - const cost = (cents / 100.00).toFixed(2); 207 - productDeleterOptions.innerHTML += ` 208 - <option value="${id}">${description} ($${cost})</option> 209 - `; 210 - } 211 - }); 212 - } catch (error) { 213 - console.error(error.message); 214 - } 215 - } 216 - </script> 217 65 </html>
+23
index.js
··· 1 + import { registerIncludeComponent } from "./components/include.js"; 2 + 3 + function app() { 4 + registerIncludeComponent(); 5 + } 6 + 7 + document.addEventListener("DOMContentLoaded", app); 8 + 9 + var last = 'buttons'; 10 + const show = i => document.getElementById(i).hidden = false; 11 + const hide = i => document.getElementById(i).hidden = true; 12 + 13 + function next(i) { 14 + hide(last); 15 + last = i; 16 + show(i); 17 + } 18 + 19 + function restart() { 20 + show('buttons'); 21 + hide(last); 22 + last = 'buttons'; 23 + }
+2 -16
lib/header.js
··· 1 - function ti(tag, inside, content) { 2 - return "<" + tag + " " + inside + ">" + content + "</" + tag + ">"; 3 - } 4 - function t(tag, content) { 5 - return "<" + tag + ">" + content + "</" + tag + ">"; 6 - } 7 - function input_any(label, label_str, input_type) { 8 - return ti("label", 'for="' + label + '"', label_str) 9 - + "<br />" 10 - + ti("input", 'input_type="' + input_type + '" id="' + label + '" name="' + label + '"', '') 11 - + "<br />"; 12 - } 13 - function input_submit() { 14 - return '<input type="submit" value="Submit" />'; 15 - } 1 + const URL = "https://ada.uprrp.edu/~diego.estrada1/CCOM/4027/db"; 16 2 17 3 var last = 'buttons'; 18 4 const show = i => document.getElementById(i).hidden = false; ··· 48 34 } 49 35 } 50 36 } 51 - } 37 + };
+200 -1
lib/product.js
··· 1 - const URL = "https://ada.uprrp.edu/~diego.estrada1/CCOM/4027/db"; 1 + function fillProductTable() { 2 + try { 3 + fetch(URL + "/api/v1/read/?t=product").then(response => response.json()).then(json => { 4 + resultsTable.innerHTML = ` 5 + <tr> 6 + <th>ID</th> 7 + <th style="width:10ch;">$USD</th> 8 + <th style="width:80ch;">Description</th> 9 + </tr> 10 + `; 11 + for (var i = 0; i < json.length; i++) { 12 + const obj = json[i]; 13 + const id = obj[0]; 14 + const cents = obj[1]; 15 + const description = obj[2]; 16 + const cost = (cents / 100.00).toFixed(2); 17 + resultsTable.innerHTML += ` 18 + <tr> 19 + <td>${id}</td> 20 + <td>$${cost}</td> 21 + <td>${description}</td> 22 + </tr> 23 + `; 24 + } 25 + }); 26 + } catch (error) { 27 + console.error(error.message); 28 + } 29 + } 30 + 31 + function fillProductDeleter() { 32 + try { 33 + fetch(URL + "/api/v1/read/?t=product").then(response => response.json()).then(json => { 34 + productDeleterOptions.innerHTML = ""; 35 + for (var i = 0; i < json.length; i++) { 36 + const obj = json[i]; 37 + const id = obj[0]; 38 + const cents = obj[1]; 39 + const description = obj[2]; 40 + const cost = (cents / 100.00).toFixed(2); 41 + productDeleterOptions.innerHTML += ` 42 + <option value="${id}">${description} ($${cost})</option> 43 + `; 44 + } 45 + }); 46 + } catch (error) { 47 + console.error(error.message); 48 + } 49 + } 50 + 51 + function fillProductEditor() { 52 + try { 53 + fetch(URL + "/api/v1/read/?t=product").then(response => response.json()).then(json => { 54 + productEditorOptions.innerHTML = ""; 55 + for (var i = 0; i < json.length; i++) { 56 + const obj = json[i]; 57 + const id = obj[0]; 58 + const cents = obj[1]; 59 + const description = obj[2]; 60 + const cost = (cents / 100.00).toFixed(2); 61 + productEditorOptions.innerHTML += ` 62 + <option value="${id}">${description} ($${cost})</option> 63 + `; 64 + } 65 + }); 66 + } catch (error) { 67 + console.error(error.message); 68 + } 69 + } 70 + 71 + function productCreatorFormHandler(event) { 72 + event.preventDefault(); 73 + 74 + const dollars = event.srcElement[1].value; 75 + const description = event.srcElement[2].value; 76 + const cents = dollars * 100; 77 + 78 + if (confirm('Do you want to create a product called: "' + description + '" costing $' + dollars + " (¢" + cents + " cents)?")) { 79 + try { 80 + fetch(URL + "/api/v1/create/", { 81 + method: "POST", 82 + body: JSON.stringify({ 83 + t: "product", 84 + cents: cents, 85 + description: description 86 + }) 87 + }).then(response => { 88 + console.log(response); 89 + alert("Succesfully created product"); 90 + restart(); 91 + }); 92 + } catch (error) { 93 + alert("Error inserting"); 94 + console.error(error.message); 95 + } 96 + } 97 + } 98 + 99 + function loadEditorSubmission() { 100 + const id = productEditorOptions.value; 101 + try { 102 + fetch(URL + "/api/v1/read?t=product&id=" + id).then(response => response.json()).then(json => { 103 + const obj = json[0]; 104 + const id = obj[0]; 105 + const cents = obj[1]; 106 + const description = obj[2]; 107 + const dollars = (cents / 100.0).toFixed(2); 108 + productEditorDollars.text = dollars; 109 + productEditorDescription.text = '"' + description + '"'; 110 + productEditorSubmissionId.value = id; 111 + productEditorSubmissionDollars.value = dollars; 112 + productEditorSubmissionDescription.value = description; 113 + productEditorMenu.hidden = false; 114 + }) 115 + } catch (error) { 116 + console.error(error.message); 117 + } 118 + } 2 119 120 + function productEditorFormHandler(event) { 121 + event.preventDefault(); 122 + const oldDollars = productEditorDollars.text; 123 + const oldDescription = productEditorDescription.text; 124 + const dollars = event.target[1].value; 125 + const description = event.target[2].value 126 + const id = event.target[4].value; 127 + const cents = dollars * 100; 128 + try { 129 + if (confirm(`Changing product: 130 + ID: ${id} 131 + Cost ($USD): $${oldDollars} -> $${dollars} 132 + Description: ${oldDescription} -> "${description}" 133 + `)) { 134 + const payload = JSON.stringify({ 135 + t: "product", 136 + p_id: id, 137 + cents: cents, 138 + description: description 139 + }) 140 + fetch(URL + "/api/v1/update/", { 141 + method: "POST", 142 + headers: { 143 + "Content-Type": "application/json" 144 + }, 145 + body: payload 146 + }).then(response => response.json()).then(json => { 147 + alert(`Succesfully updated product ${id}.`); 148 + restart(); 149 + }); 150 + } 151 + } catch (error) { 152 + console.error(error.message); 153 + } 154 + } 155 + 156 + function loadDeleterSubmission() { 157 + const id = productDeleterOptions.value; 158 + try { 159 + fetch(URL + "/api/v1/read?t=product&id=" + id).then(response => response.json()).then(json => { 160 + const obj = json[0]; 161 + const id = obj[0]; 162 + const cents = obj[1]; 163 + const description = obj[2]; 164 + const dollars = (cents / 100.0).toFixed(2); 165 + productDeleterDollars.innerHTML = dollars; 166 + productDeleterDescription.innerHTML = '"' + description + '"'; 167 + productDeleterMenu.hidden = false; 168 + }) 169 + } catch (error) { 170 + console.error(error.message); 171 + } 172 + } 173 + 174 + function deleteProduct() { 175 + const id = productDeleterOptions.value; 176 + const dollars = productDeleterDollars.innerHTML; 177 + const description = productDeleterDescription.innerHTML; 178 + try { 179 + const payload = JSON.stringify({ 180 + t: "product", 181 + p_id: id 182 + }); 183 + if (confirm(`Are you sure you want to delete product ${id}: 184 + Cost ($USD): $${dollars} 185 + Description: ${description}`)) { 186 + fetch(URL + "/api/v1/delete/", { 187 + method: "POST", 188 + headers: { 189 + "Content-Type": "application/json" 190 + }, 191 + body: payload 192 + }).then(response => { 193 + alert("Succesfully deleted product.") 194 + productDeleterMenu.hidden = true; 195 + restart(); 196 + }); 197 + } 198 + } catch(error) { 199 + console.error(error); 200 + } 201 + }
+41 -65
product.html
··· 4 4 What would you like to do? 5 5 <br /> 6 6 <br /> 7 - <button onclick="next('productCreator')">Create a product</button> 8 - <button onclick="next('productFinder'); fillProductTable();">Find a product</button> 9 - <button onclick="next('productEditor'); fillProductEditor();">Edit a product</button> 10 - <button onclick="next('productDeleter'); fillProductDeleter();">Delete a product</button> 7 + <nav> 8 + <button onclick="next('productCreator')">Create a product</button> 9 + <button onclick="next('productFinder'); fillProductTable();">Find a product</button> 10 + <button onclick="next('productEditor'); fillProductEditor();">Edit a product</button> 11 + <button onclick="next('productDeleter'); fillProductDeleter();">Delete a product</button> 12 + </nav> 11 13 </div> 12 14 13 15 <div id="productCreator" hidden> 14 16 So you want to create a product... 15 17 <br /> 16 - <form id="productCreatorForm"> 17 - <label for="cents">Cost:</label> 18 - <br /> 19 - <div class="input-box"> 20 - <span>&nbsp;$</span> 21 - <input type="number" name="cents" min="0.01" step="0.01" max="2500" /> 22 - </div> 23 - <br /> 24 - 25 - <label for="description">Description:</label> 26 - <input type="text" minlength="1" name="description" maxlength="80" /> 27 - <br /> 28 - <br /> 29 - <input type="submit" value="Submit" /> 30 - <input type="hidden" name="t" value="product" /> 18 + <form id="productCreatorForm" onsubmit="productCreatorFormHandler(event);"> 19 + <fieldset> 20 + <legend>Product:</legend> 21 + <label for="cents">Cost:</label> 22 + <div class="input-box"> 23 + <span>&nbsp;$</span> 24 + <input type="number" name="cents" min="0.01" step="0.01" max="2500" /> 25 + </div> 26 + <br /> 27 + <label for="description">Description:</label> 28 + <br /> 29 + <input type="text" minlength="1" name="description" maxlength="80" /> 30 + <br /> 31 + <br /> 32 + <input type="submit" value="Submit" /> 33 + </fieldset> 31 34 </form> 32 35 </div> 33 36 34 37 <div id="productFinder" hidden> 35 38 <h1>Here is the list of products:</h1> 36 39 <label for="tableFilter">Filter the results:</label> 37 - <input type="search" id="tableFilter" onkeyup="table_filter()" placeholder="filter..."> 40 + <search> 41 + <input type="search" id="tableFilter" onkeyup="table_filter()" placeholder="filter..."> 42 + </search> 38 43 <table id="resultsTable"></table> 39 44 </div> 40 45 ··· 50 55 Description: <span id="productEditorDescription"></span> 51 56 <br /> 52 57 <form id="productEditorForm" onsubmit="productEditorFormHandler(event);"> 53 - <label for="cents">Cost:</label> 54 - <br /> 55 - <div class="input-box"> 56 - <span>&nbsp;$</span> 57 - <input type="number" id="productEditorSubmissionDollars" name="cents" min="0.01" step="0.01" max="2500" /> 58 - </div> 59 - <br /> 60 - 61 - <label for="description">Description:</label> 62 - <input type="text" id="productEditorSubmissionDescription" minlength="1" name="description" maxlength="80" /> 63 - <br /> 64 - <br /> 65 - <input type="submit" value="Submit" /> 66 - <input type="hidden" id="productEditorSubmissionId" name="id" value="" /> 58 + <fieldset> 59 + <legend>Edit product:</legend> 60 + <label for="cents">Cost:</label> 61 + <div class="input-box"> 62 + <span>&nbsp;$</span> 63 + <input type="number" id="productEditorSubmissionDollars" name="cents" min="0.01" step="0.01" max="2500" /> 64 + </div> 65 + <br /> 66 + <label for="description">Description:</label> 67 + <input type="text" id="productEditorSubmissionDescription" minlength="1" name="description" maxlength="80" /> 68 + <br /> 69 + <br /> 70 + <input type="submit" value="Submit" /> 71 + <input type="hidden" id="productEditorSubmissionId" value="" /> 72 + </fieldset> 67 73 </form> 68 74 </div> 69 75 </div> ··· 81 87 Description: <span id="productDeleterDescription"></span> 82 88 <br /> 83 89 <br /> 84 - <button onclick="deleteProduct();">Delete?</button> 90 + <button style="color:#ff5454;" onclick="deleteProduct();">Delete?</button> 85 91 </div> 86 92 </div> 87 - 88 - <script> 89 - productCreatorForm.addEventListener("submit", event => { 90 - event.preventDefault(); 91 - 92 - const description = event.srcElement[1].value; 93 - const dollars = event.srcElement[0].value; 94 - const cents = dollars * 100; 95 - 96 - if (confirm('Do you want to create a product called: "' + description + '" costing $' + dollars + " (¢" + cents + " cents)?")) { 97 - try { 98 - fetch(URL + "/api/v1/create/", { 99 - method: "POST", 100 - body: JSON.stringify({ 101 - t: "product", 102 - cents: cents, 103 - description: description 104 - }) 105 - }).then(response => { 106 - console.log(response); 107 - alert("Succesfully created product"); 108 - restart(); 109 - }); 110 - } catch (error) { 111 - alert("Error inserting"); 112 - console.error(error.message); 113 - } 114 - } 115 - }); 116 - </script>
+27
reset.css
··· 1 + /* generic minimal CSS reset 2 + * inspiration: https://www.digitalocean.com/community/tutorials/css-minimal-css-reset 3 + */ 4 + 5 + :root { 6 + box-sizing: border-box; 7 + line-height: 1.4; 8 + /* https://kilianvalkhof.com/2022/css-html/your-css-reset-needs-text-size-adjust-probably/ */ 9 + -moz-text-size-adjust: none; 10 + -webkit-text-size-adjust: none; 11 + text-size-adjust: none; 12 + } 13 + 14 + *, *::before, *::after { 15 + box-sizing: inherit; 16 + } 17 + 18 + body, h1, h2, h3, h4, h5, h6, p { 19 + margin: 0; 20 + padding: 0; 21 + font-weight: normal; 22 + } 23 + 24 + img { 25 + max-width:100%; 26 + height:auto; 27 + }