Form Login » History » Version 2
Lê Sĩ Quý, 08/28/2025 12:05 PM
| 1 | 1 | Lê Sĩ Quý | {{TOC}} |
|---|---|---|---|
| 2 | |||
| 3 | # Login |
||
| 4 | |||
| 5 | Northwind app sử dụng cơ chế authentcation đơn giản cung cấp bởi AKA-Form. Hệ thống thiết lập giá trị cứng cho email/password. Giá trị password được hash bằng hàm md5. |
||
| 6 | |||
| 7 | ## JSON Schema |
||
| 8 | |||
| 9 | Gồm 2 trường email và password |
||
| 10 | |||
| 11 | ``` javascript |
||
| 12 | { |
||
| 13 | "display": "form", |
||
| 14 | "components": [ |
||
| 15 | { |
||
| 16 | "label": "Email", |
||
| 17 | "applyMaskOn": "change", |
||
| 18 | "tableView": true, |
||
| 19 | "validate": { |
||
| 20 | "required": true |
||
| 21 | }, |
||
| 22 | "validateWhenHidden": false, |
||
| 23 | "key": "email", |
||
| 24 | "type": "email", |
||
| 25 | "input": true |
||
| 26 | }, |
||
| 27 | { |
||
| 28 | "label": "Password", |
||
| 29 | "applyMaskOn": "change", |
||
| 30 | "tableView": false, |
||
| 31 | "validate": { |
||
| 32 | "required": true |
||
| 33 | }, |
||
| 34 | "validateWhenHidden": false, |
||
| 35 | "key": "password", |
||
| 36 | "type": "password", |
||
| 37 | "input": true, |
||
| 38 | "protected": true |
||
| 39 | }, |
||
| 40 | { |
||
| 41 | "label": "Login", |
||
| 42 | "showValidations": false, |
||
| 43 | "disableOnInvalid": true, |
||
| 44 | "tableView": false, |
||
| 45 | "key": "submit", |
||
| 46 | "type": "button", |
||
| 47 | "input": true, |
||
| 48 | "saveOnEnter": false |
||
| 49 | } |
||
| 50 | ] |
||
| 51 | } |
||
| 52 | ``` |
||
| 53 | 2 | Lê Sĩ Quý | |
| 54 | ## HTML Template |
||
| 55 | |||
| 56 | ``` html |
||
| 57 | <!DOCTYPE html> |
||
| 58 | <html lang="en"> |
||
| 59 | |||
| 60 | <head> |
||
| 61 | <meta charset="UTF-8"> |
||
| 62 | <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
||
| 63 | <title>Northwind - Đăng nhập</title> |
||
| 64 | <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons/font/bootstrap-icons.css"> |
||
| 65 | <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap/dist/css/bootstrap.min.css"> |
||
| 66 | <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/notyf@3/notyf.min.css"> |
||
| 67 | <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script> |
||
| 68 | <script src="https://cdn.form.io/js/formio.embed.js"></script> |
||
| 69 | <script src="https://cdn.jsdelivr.net/npm/localstorage-slim"></script> |
||
| 70 | <script src="https://cdn.jsdelivr.net/npm/notyf@3/notyf.min.js"></script> |
||
| 71 | <style> |
||
| 72 | body { |
||
| 73 | font-family: sans-serif; |
||
| 74 | background-color: #f4f4f4; |
||
| 75 | display: flex; |
||
| 76 | justify-content: center; |
||
| 77 | align-items: center; |
||
| 78 | min-height: 100vh; |
||
| 79 | margin: 0; |
||
| 80 | } |
||
| 81 | |||
| 82 | button[type="submit"] { |
||
| 83 | background-color: #007bff; |
||
| 84 | color: white; |
||
| 85 | padding: 10px 15px; |
||
| 86 | border: none; |
||
| 87 | border-radius: 4px; |
||
| 88 | cursor: pointer; |
||
| 89 | font-size: 1em; |
||
| 90 | width: 100%; |
||
| 91 | } |
||
| 92 | |||
| 93 | .login-container { |
||
| 94 | background-color: #fff; |
||
| 95 | padding: 30px; |
||
| 96 | border-radius: 8px; |
||
| 97 | box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); |
||
| 98 | width: 400px; |
||
| 99 | max-width: 90%; |
||
| 100 | text-align: center; |
||
| 101 | } |
||
| 102 | |||
| 103 | .logo-container { |
||
| 104 | margin-bottom: 20px; |
||
| 105 | } |
||
| 106 | |||
| 107 | .logo { |
||
| 108 | max-width: 100px; |
||
| 109 | height: auto; |
||
| 110 | } |
||
| 111 | |||
| 112 | .app-name { |
||
| 113 | font-size: 1.5em; |
||
| 114 | color: #333; |
||
| 115 | margin-top: 10px; |
||
| 116 | text-transform: uppercase; |
||
| 117 | } |
||
| 118 | |||
| 119 | .form-group { |
||
| 120 | margin-bottom: 20px; |
||
| 121 | text-align: left; |
||
| 122 | } |
||
| 123 | |||
| 124 | |||
| 125 | .copyright { |
||
| 126 | margin-top: 20px; |
||
| 127 | font-size: 0.9em; |
||
| 128 | color: #777; |
||
| 129 | } |
||
| 130 | |||
| 131 | /* Responsive adjustments */ |
||
| 132 | @media (max-width: 600px) { |
||
| 133 | .login-container { |
||
| 134 | padding: 20px; |
||
| 135 | } |
||
| 136 | |||
| 137 | .logo { |
||
| 138 | max-width: 80px; |
||
| 139 | } |
||
| 140 | |||
| 141 | .app-name { |
||
| 142 | font-size: 1.2em; |
||
| 143 | } |
||
| 144 | |||
| 145 | .copyright { |
||
| 146 | font-size: 0.8em; |
||
| 147 | } |
||
| 148 | } |
||
| 149 | </style> |
||
| 150 | </head> |
||
| 151 | |||
| 152 | <body> |
||
| 153 | <div class="login-container"> |
||
| 154 | <div class="logo-container"> |
||
| 155 | <img src="https://northwind.gal/wp-content/uploads/2021/06/northwind-footer-logo.png" alt="Logo" class="logo"> |
||
| 156 | <h1 class="app-name">Orders Management</h1> |
||
| 157 | </div> |
||
| 158 | <div class="form-group" id="formio"></div> |
||
| 159 | |||
| 160 | <p class="copyright">© 2025 Northwind, LLC. All rights reserved.</p> |
||
| 161 | </div> |
||
| 162 | <script> |
||
| 163 | var curForm; |
||
| 164 | var submission = {}; |
||
| 165 | |||
| 166 | $(function () { |
||
| 167 | Formio.createForm(document.getElementById("formio"), <?!= template ?>) |
||
| 168 | .then(function (form) { |
||
| 169 | // Prevent the submission from going to the form.io server. |
||
| 170 | form.noAlerts = true; |
||
| 171 | form.nosubmit = true; |
||
| 172 | curForm = form; |
||
| 173 | |||
| 174 | // Triggered when they click the submit button. |
||
| 175 | form.on("submit", function (obj) { |
||
| 176 | submission = obj; |
||
| 177 | google.script.run |
||
| 178 | .withSuccessHandler(onSuccess) |
||
| 179 | .withFailureHandler(onFailure) |
||
| 180 | .login(submission.data); |
||
| 181 | }); |
||
| 182 | }); |
||
| 183 | }); |
||
| 184 | |||
| 185 | function onFailure(err) { |
||
| 186 | curForm.emit("submitError", err); |
||
| 187 | var notyf = new Notyf(); |
||
| 188 | notyf.error(err.message); |
||
| 189 | } |
||
| 190 | |||
| 191 | function onSuccess(sid) { |
||
| 192 | if (sid) { |
||
| 193 | curForm.emit("submitDone", submission); |
||
| 194 | let url = "<?!= base ?>?url=<?!= callback ?>"; |
||
| 195 | window.top.location.href = url; |
||
| 196 | ls.set("sid", sid); |
||
| 197 | ls.set("email", submission.data.email); |
||
| 198 | } else { |
||
| 199 | let message = "Login Failed. Your user ID or password is incorrect."; |
||
| 200 | curForm.emit("submitError", new Error(message)); |
||
| 201 | var notyf = new Notyf(); |
||
| 202 | notyf.error(message); |
||
| 203 | } |
||
| 204 | } |
||
| 205 | </script> |
||
| 206 | </body> |
||
| 207 | |||
| 208 | </html> |
||
| 209 | ``` |