Refine UI/UX for custom css / footer text. Add switch for show/hide powered by
This commit is contained in:
		
							parent
							
								
									1bc01d1077
								
							
						
					
					
						commit
						8eb83394f7
					
				
					 9 changed files with 87 additions and 28 deletions
				
			
		
							
								
								
									
										6
									
								
								db/patch-status-page-footer-css.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								db/patch-status-page-footer-css.sql
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,6 @@ | ||||||
|  | -- You should not modify if this have pushed to Github, unless it does serious wrong with the db. | ||||||
|  | BEGIN TRANSACTION; | ||||||
|  | ALTER TABLE status_page ADD footer_text TEXT; | ||||||
|  | ALTER TABLE status_page ADD custom_css TEXT; | ||||||
|  | ALTER TABLE status_page ADD show_powered_by BOOLEAN NOT NULL DEFAULT 1; | ||||||
|  | COMMIT; | ||||||
|  | @ -91,6 +91,7 @@ | ||||||
|         "password-hash": "~1.2.2", |         "password-hash": "~1.2.2", | ||||||
|         "postcss-rtlcss": "~3.4.1", |         "postcss-rtlcss": "~3.4.1", | ||||||
|         "postcss-scss": "~4.0.3", |         "postcss-scss": "~4.0.3", | ||||||
|  |         "prismjs": "^1.27.0", | ||||||
|         "prom-client": "~13.2.0", |         "prom-client": "~13.2.0", | ||||||
|         "prometheus-api-metrics": "~3.2.1", |         "prometheus-api-metrics": "~3.2.1", | ||||||
|         "qrcode": "~1.5.0", |         "qrcode": "~1.5.0", | ||||||
|  | @ -110,6 +111,7 @@ | ||||||
|         "vue-i18n": "~9.1.9", |         "vue-i18n": "~9.1.9", | ||||||
|         "vue-image-crop-upload": "~3.0.3", |         "vue-image-crop-upload": "~3.0.3", | ||||||
|         "vue-multiselect": "~3.0.0-alpha.2", |         "vue-multiselect": "~3.0.0-alpha.2", | ||||||
|  |         "vue-prism-editor": "^2.0.0-alpha.2", | ||||||
|         "vue-qrcode": "~1.0.0", |         "vue-qrcode": "~1.0.0", | ||||||
|         "vue-router": "~4.0.14", |         "vue-router": "~4.0.14", | ||||||
|         "vue-toastification": "~2.0.0-rc.5", |         "vue-toastification": "~2.0.0-rc.5", | ||||||
|  |  | ||||||
|  | @ -56,6 +56,7 @@ class Database { | ||||||
|         "patch-status-page.sql": true, |         "patch-status-page.sql": true, | ||||||
|         "patch-proxy.sql": true, |         "patch-proxy.sql": true, | ||||||
|         "patch-monitor-expiry-notification.sql": true, |         "patch-monitor-expiry-notification.sql": true, | ||||||
|  |         "patch-status-page-footer-css.sql": true, | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|  |  | ||||||
|  | @ -92,6 +92,9 @@ class StatusPage extends BeanModel { | ||||||
|             published: !!this.published, |             published: !!this.published, | ||||||
|             showTags: !!this.show_tags, |             showTags: !!this.show_tags, | ||||||
|             domainNameList: this.getDomainNameList(), |             domainNameList: this.getDomainNameList(), | ||||||
|  |             customCSS: this.custom_css, | ||||||
|  |             footerText: this.footer_text, | ||||||
|  |             showPoweredBy: !!this.show_powered_by, | ||||||
|         }; |         }; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -104,6 +107,9 @@ class StatusPage extends BeanModel { | ||||||
|             theme: this.theme, |             theme: this.theme, | ||||||
|             published: !!this.published, |             published: !!this.published, | ||||||
|             showTags: !!this.show_tags, |             showTags: !!this.show_tags, | ||||||
|  |             customCSS: this.custom_css, | ||||||
|  |             footerText: this.footer_text, | ||||||
|  |             showPoweredBy: !!this.show_powered_by, | ||||||
|         }; |         }; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -155,6 +155,9 @@ module.exports.statusPageSocketHandler = (socket) => { | ||||||
|             //statusPage.search_engine_index = ;
 |             //statusPage.search_engine_index = ;
 | ||||||
|             statusPage.show_tags = config.showTags; |             statusPage.show_tags = config.showTags; | ||||||
|             //statusPage.password = null;
 |             //statusPage.password = null;
 | ||||||
|  |             statusPage.footer_text = config.footerText; | ||||||
|  |             statusPage.custom_css = config.customCSS; | ||||||
|  |             statusPage.show_powered_by = config.showPoweredBy; | ||||||
|             statusPage.modified_date = R.isoDateTime(); |             statusPage.modified_date = R.isoDateTime(); | ||||||
| 
 | 
 | ||||||
|             await R.store(statusPage); |             await R.store(statusPage); | ||||||
|  |  | ||||||
|  | @ -469,6 +469,10 @@ textarea.form-control { | ||||||
|     color: $primary; |     color: $primary; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | .prism-editor__textarea { | ||||||
|  |     outline: none !important; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // Localization | // Localization | ||||||
| 
 | 
 | ||||||
| @import "localization.scss"; | @import "localization.scss"; | ||||||
|  |  | ||||||
|  | @ -443,6 +443,6 @@ export default { | ||||||
|     "Fingerprint:": "Fingerabdruck:", |     "Fingerprint:": "Fingerabdruck:", | ||||||
|     "No status pages": "Keine Status-Seiten", |     "No status pages": "Keine Status-Seiten", | ||||||
|     Customize: "Anpassen", |     Customize: "Anpassen", | ||||||
|     "Custom Footer": "Eigener Footer (Leerlassen für Standard)", |     "Custom Footer": "Eigener Footer", | ||||||
|     "Custom CSS": "Eigenes CSS", |     "Custom CSS": "Eigenes CSS", | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | @ -355,7 +355,7 @@ export default { | ||||||
|     serwersmsSenderName: "SMS Sender Name (registered via customer portal)", |     serwersmsSenderName: "SMS Sender Name (registered via customer portal)", | ||||||
|     stackfield: "Stackfield", |     stackfield: "Stackfield", | ||||||
|     Customize: "Customize", |     Customize: "Customize", | ||||||
|     "Custom Footer": "Custom Footer (empty string for default)", |     "Custom Footer": "Custom Footer", | ||||||
|     "Custom CSS": "Custom CSS", |     "Custom CSS": "Custom CSS", | ||||||
|     smtpDkimSettings: "DKIM Settings", |     smtpDkimSettings: "DKIM Settings", | ||||||
|     smtpDkimDesc: "Please refer to the Nodemailer DKIM {0} for usage.", |     smtpDkimDesc: "Please refer to the Nodemailer DKIM {0} for usage.", | ||||||
|  |  | ||||||
|  | @ -16,11 +16,18 @@ | ||||||
|                     <input id="title" v-model="config.title" type="text" class="form-control"> |                     <input id="title" v-model="config.title" type="text" class="form-control"> | ||||||
|                 </div> |                 </div> | ||||||
| 
 | 
 | ||||||
|  |                 <!-- Description --> | ||||||
|                 <div class="my-3"> |                 <div class="my-3"> | ||||||
|                     <label for="description" class="form-label">{{ $t("Description") }}</label> |                     <label for="description" class="form-label">{{ $t("Description") }}</label> | ||||||
|                     <textarea id="description" v-model="config.description" class="form-control"></textarea> |                     <textarea id="description" v-model="config.description" class="form-control"></textarea> | ||||||
|                 </div> |                 </div> | ||||||
| 
 | 
 | ||||||
|  |                 <!-- Footer Text --> | ||||||
|  |                 <div class="my-3"> | ||||||
|  |                     <label for="footer-text" class="form-label">{{ $t("Footer Text") }}</label> | ||||||
|  |                     <textarea id="footer-text" v-model="config.footerText" class="form-control"></textarea> | ||||||
|  |                 </div> | ||||||
|  | 
 | ||||||
|                 <div class="my-3 form-check form-switch"> |                 <div class="my-3 form-check form-switch"> | ||||||
|                     <input id="switch-theme" v-model="config.theme" class="form-check-input" type="checkbox" true-value="dark" false-value="light"> |                     <input id="switch-theme" v-model="config.theme" class="form-check-input" type="checkbox" true-value="dark" false-value="light"> | ||||||
|                     <label class="form-check-label" for="switch-theme">{{ $t("Switch to Dark Theme") }}</label> |                     <label class="form-check-label" for="switch-theme">{{ $t("Switch to Dark Theme") }}</label> | ||||||
|  | @ -31,6 +38,12 @@ | ||||||
|                     <label class="form-check-label" for="showTags">{{ $t("Show Tags") }}</label> |                     <label class="form-check-label" for="showTags">{{ $t("Show Tags") }}</label> | ||||||
|                 </div> |                 </div> | ||||||
| 
 | 
 | ||||||
|  |                 <!-- Show Powered By --> | ||||||
|  |                 <div class="my-3 form-check form-switch"> | ||||||
|  |                     <input id="show-powered-by" v-model="config.showPoweredBy" class="form-check-input" type="checkbox"> | ||||||
|  |                     <label class="form-check-label" for="show-powered-by">{{ $t("Show Powered By") }}</label> | ||||||
|  |                 </div> | ||||||
|  | 
 | ||||||
|                 <div v-if="false" class="my-3"> |                 <div v-if="false" class="my-3"> | ||||||
|                     <label for="password" class="form-label">{{ $t("Password") }} <sup>Coming Soon</sup></label> |                     <label for="password" class="form-label">{{ $t("Password") }} <sup>Coming Soon</sup></label> | ||||||
|                     <input id="password" v-model="config.password" disabled type="password" autocomplete="new-password" class="form-control"> |                     <input id="password" v-model="config.password" disabled type="password" autocomplete="new-password" class="form-control"> | ||||||
|  | @ -51,6 +64,12 @@ | ||||||
|                     </ul> |                     </ul> | ||||||
|                 </div> |                 </div> | ||||||
| 
 | 
 | ||||||
|  |                 <!-- Custom CSS --> | ||||||
|  |                 <div class="my-3"> | ||||||
|  |                     <div class="mb-1">{{ $t("Custom CSS") }}</div> | ||||||
|  |                     <prism-editor v-model="config.customCSS" class="css-editor" :highlight="highlighter" line-numbers></prism-editor> | ||||||
|  |                 </div> | ||||||
|  | 
 | ||||||
|                 <div class="danger-zone"> |                 <div class="danger-zone"> | ||||||
|                     <button class="btn btn-danger me-2" @click="deleteDialog"> |                     <button class="btn btn-danger me-2" @click="deleteDialog"> | ||||||
|                         <font-awesome-icon icon="trash" /> |                         <font-awesome-icon icon="trash" /> | ||||||
|  | @ -119,11 +138,6 @@ | ||||||
|                         <font-awesome-icon icon="bullhorn" /> |                         <font-awesome-icon icon="bullhorn" /> | ||||||
|                         {{ $t("Create Incident") }} |                         {{ $t("Create Incident") }} | ||||||
|                     </button> |                     </button> | ||||||
| 
 |  | ||||||
|                     <button class="btn btn-primary me-2" @click="customizeStatusPage"> |  | ||||||
|                         <font-awesome-icon icon="cog" /> |  | ||||||
|                         {{ $t("Customize") }} |  | ||||||
|                     </button> |  | ||||||
|                 </div> |                 </div> | ||||||
|             </div> |             </div> | ||||||
| 
 | 
 | ||||||
|  | @ -180,15 +194,6 @@ | ||||||
|                 </div> |                 </div> | ||||||
|             </div> |             </div> | ||||||
| 
 | 
 | ||||||
|             <!-- Customize --> |  | ||||||
|             <div v-if="editMode && enableEditCustomizeMode" class="mb-4 p-4 alert shadow-box customize" role="alert"> |  | ||||||
|                 <strong v-if="enableEditCustomizeMode">{{ $t("Custom CSS") }}:</strong> |  | ||||||
|                 <Editable v-model="config.customCSS" tag="div" :contenteditable="enableEditCustomizeMode" class="content p-2" /> |  | ||||||
|                 <br /> |  | ||||||
|                 <strong v-if="enableEditCustomizeMode">{{ $t("Custom Footer") }}:</strong> |  | ||||||
|                 <Editable v-model="config.poweredBy" tag="h4" :contenteditable="enableEditCustomizeMode" :noNL="true" class="alert-heading p-2" /> |  | ||||||
|             </div> |  | ||||||
| 
 |  | ||||||
|             <!-- Overall Status --> |             <!-- Overall Status --> | ||||||
|             <div class="shadow-box list  p-4 overall-status mb-4"> |             <div class="shadow-box list  p-4 overall-status mb-4"> | ||||||
|                 <div v-if="Object.keys($root.publicMonitorList).length === 0 && loadedData"> |                 <div v-if="Object.keys($root.publicMonitorList).length === 0 && loadedData"> | ||||||
|  | @ -253,8 +258,14 @@ | ||||||
|             </div> |             </div> | ||||||
| 
 | 
 | ||||||
|             <footer class="mt-5 mb-4"> |             <footer class="mt-5 mb-4"> | ||||||
|                 <p v-if="config.poweredBy" v-html="config.poweredBy"></p> |                 <div class="custom-footer-text text-start"> | ||||||
|                 <p v-else>{{ $t("Powered by") }} <a target="_blank" href="https://github.com/louislam/uptime-kuma">{{ $t("Uptime Kuma" ) }}</a></p> |                     <strong v-if="enableEditMode">{{ $t("Custom Footer") }}:</strong> | ||||||
|  |                 </div> | ||||||
|  |                 <Editable v-model="config.footerText" tag="div" :contenteditable="enableEditMode" :noNL="false" class="alert-heading p-2" /> | ||||||
|  | 
 | ||||||
|  |                 <p v-if="config.showPoweredBy"> | ||||||
|  |                     {{ $t("Powered by") }} <a target="_blank" href="https://github.com/louislam/uptime-kuma">{{ $t("Uptime Kuma" ) }}</a> | ||||||
|  |                 </p> | ||||||
|             </footer> |             </footer> | ||||||
|         </div> |         </div> | ||||||
| 
 | 
 | ||||||
|  | @ -278,6 +289,14 @@ import dayjs from "dayjs"; | ||||||
| import Favico from "favico.js"; | import Favico from "favico.js"; | ||||||
| import { getResBaseURL } from "../util-frontend"; | import { getResBaseURL } from "../util-frontend"; | ||||||
| import Confirm from "../components/Confirm.vue"; | import Confirm from "../components/Confirm.vue"; | ||||||
|  | // import Prism Editor | ||||||
|  | import { PrismEditor } from "vue-prism-editor"; | ||||||
|  | import "vue-prism-editor/dist/prismeditor.min.css"; // import the styles somewhere | ||||||
|  | 
 | ||||||
|  | // import highlighting library (you can use any library you want just return html string) | ||||||
|  | import { highlight, languages } from "prismjs/components/prism-core"; | ||||||
|  | import "prismjs/components/prism-css"; | ||||||
|  | import "prismjs/themes/prism-tomorrow.css"; // import syntax highlighting styles | ||||||
| 
 | 
 | ||||||
| const toast = useToast(); | const toast = useToast(); | ||||||
| 
 | 
 | ||||||
|  | @ -296,6 +315,7 @@ export default { | ||||||
|         PublicGroupList, |         PublicGroupList, | ||||||
|         ImageCropUpload, |         ImageCropUpload, | ||||||
|         Confirm, |         Confirm, | ||||||
|  |         PrismEditor, | ||||||
|     }, |     }, | ||||||
| 
 | 
 | ||||||
|     // Leave Page for vue route change |     // Leave Page for vue route change | ||||||
|  | @ -324,7 +344,6 @@ export default { | ||||||
|             slug: null, |             slug: null, | ||||||
|             enableEditMode: false, |             enableEditMode: false, | ||||||
|             enableEditIncidentMode: false, |             enableEditIncidentMode: false, | ||||||
|             enableEditCustomizeMode: false, |  | ||||||
|             hasToken: false, |             hasToken: false, | ||||||
|             config: {}, |             config: {}, | ||||||
|             selectedMonitor: null, |             selectedMonitor: null, | ||||||
|  | @ -439,6 +458,13 @@ export default { | ||||||
|                 this.$root.getSocket().emit("getStatusPage", this.slug, (res) => { |                 this.$root.getSocket().emit("getStatusPage", this.slug, (res) => { | ||||||
|                     if (res.ok) { |                     if (res.ok) { | ||||||
|                         this.config = res.config; |                         this.config = res.config; | ||||||
|  | 
 | ||||||
|  |                         if (!this.config.customCSS) { | ||||||
|  |                             this.config.customCSS = "body {\n" + | ||||||
|  |                                 "  \n" + | ||||||
|  |                                 "}\n"; | ||||||
|  |                         } | ||||||
|  | 
 | ||||||
|                     } else { |                     } else { | ||||||
|                         toast.error(res.msg); |                         toast.error(res.msg); | ||||||
|                     } |                     } | ||||||
|  | @ -541,6 +567,10 @@ export default { | ||||||
|     }, |     }, | ||||||
|     methods: { |     methods: { | ||||||
| 
 | 
 | ||||||
|  |         highlighter(code) { | ||||||
|  |             return highlight(code, languages.css); | ||||||
|  |         }, | ||||||
|  | 
 | ||||||
|         updateHeartbeatList() { |         updateHeartbeatList() { | ||||||
|             // If editMode, it will use the data from websocket. |             // If editMode, it will use the data from websocket. | ||||||
|             if (! this.editMode) { |             if (! this.editMode) { | ||||||
|  | @ -726,14 +756,6 @@ export default { | ||||||
|             this.config.domainNameList.splice(index, 1); |             this.config.domainNameList.splice(index, 1); | ||||||
|         }, |         }, | ||||||
| 
 | 
 | ||||||
|         /** customize status page */ |  | ||||||
|         customizeStatusPage() { |  | ||||||
|             if (this.editMode) { |  | ||||||
|                 // toggle modal |  | ||||||
|                 this.enableEditCustomizeMode = !this.enableEditCustomizeMode; |  | ||||||
|             } |  | ||||||
|         }, |  | ||||||
| 
 |  | ||||||
|     } |     } | ||||||
| }; | }; | ||||||
| </script> | </script> | ||||||
|  | @ -863,7 +885,7 @@ footer { | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| .incident, .customize { | .incident { | ||||||
|     .content { |     .content { | ||||||
|         &[contenteditable="true"] { |         &[contenteditable="true"] { | ||||||
|             min-height: 60px; |             min-height: 60px; | ||||||
|  | @ -922,4 +944,19 @@ footer { | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /* required class */ | ||||||
|  | .css-editor { | ||||||
|  |     /* we dont use `language-` classes anymore so thats why we need to add background and text color manually */ | ||||||
|  | 
 | ||||||
|  |     border-radius: 1rem; | ||||||
|  |     padding: 10px 5px; | ||||||
|  |     border: 1px solid #ced4da; | ||||||
|  | 
 | ||||||
|  |     .dark & { | ||||||
|  |         background: $dark-bg; | ||||||
|  |         border: 1px solid $dark-border-color; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
| </style> | </style> | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue