Просмотр исходного кода

Improve user notifications UI (#3709)

* Improve HTML and consistency of user notifications

* Bump Node dependencies

* Show loading spinner while loading notifications
Alec Rust 2 лет назад
Родитель
Сommit
7c12c9c5ce

+ 2 - 2
bin/v-add-sys-dependencies

@@ -49,7 +49,7 @@ fi
 if [ ! -f "$COMPOSER_BIN" ]; then
 	$BIN/v-add-user-composer "$user"
 	if [ $? -ne 0 ]; then
-		$BIN/v-add-user-notification admin 'Composer installation failed!' '<b>Hestia will not work without Composer.</b><br><br>Please try running the installer manually from a shell session:<br>v-add-sys-dependencies<br><br>If this continues, <a href="https://github.com/hestiacp/hestiacp/issues" target="_blank">open an issue on GitHub</a>.'
+		$BIN/v-add-user-notification admin 'Composer installation failed!' '<p class="u-text-bold">Hestia will not work without Composer.</p><p>Please try running the installer manually from a shell session:<br><code>v-add-sys-dependencies</code></p><p>If this continues, <a href="https://github.com/hestiacp/hestiacp/issues" target="_blank">open an issue on GitHub</a>.</p>'
 		exit 1
 	fi
 fi
@@ -78,7 +78,7 @@ if [ $? -ne 0 ]; then
 	echo "ERROR: PHPMailer installation failed!"
 	echo "Please report this to our development team:"
 	echo "https://github.com/hestiacp/hestiacp/issues"
-	$BIN/v-add-user-notification admin 'Hestia PHP dependencies installation failed!' 'Please <a href="https://github.com/hestiacp/hestiacp/issues" target="_blank">open an issue on GitHub</a> to report this to our development team.'
+	$BIN/v-add-user-notification admin 'Hestia PHP dependencies installation failed!' '<p>Please <a href="https://github.com/hestiacp/hestiacp/issues" target="_blank">open an issue on GitHub</a> to report this to our development team.</p>'
 	# Installation failed, clean up files
 	rm --recursive --force ${PM_INSTALL_DIR}/vendor
 	$BIN/v-change-sys-config-value 'USE_SERVER_SMTP' 'n'

+ 2 - 2
bin/v-add-sys-filemanager

@@ -51,7 +51,7 @@ fi
 if [ ! -f "$COMPOSER_BIN" ]; then
 	$BIN/v-add-user-composer "$user"
 	if [ $? -ne 0 ]; then
-		$BIN/v-add-user-notification admin 'Composer installation failed!' '<b>The File Manager will not work without Composer.</b><br><br>Please try running the installer manually from a shell session:<br>v-add-sys-filemanager<br><br>If this continues, <a href="https://github.com/hestiacp/hestiacp/issues" target="_blank">open an issue on GitHub</a>.'
+		$BIN/v-add-user-notification admin 'Composer installation failed!' '<p class="u-text-bold">The File Manager will not work without Composer.</p><p>Please try running the installer manually from a shell session:<br><code>v-add-sys-filemanager</code></p><p>If this continues, <a href="https://github.com/hestiacp/hestiacp/issues" target="_blank">open an issue on GitHub</a>.</p>'
 		exit 1
 	fi
 fi
@@ -91,7 +91,7 @@ if [ $? -ne 0 ]; then
 	echo "ERROR: File Manager installation failed!"
 	echo "Please report this to our development team:"
 	echo "https://github.com/hestiacp/hestiacp/issues"
-	$BIN/v-add-user-notification admin 'File Manager installation failed!' 'Please <a href="https://github.com/hestiacp/hestiacp/issues" target="_blank">open an issue on GitHub</a> to report this to our development team.'
+	$BIN/v-add-user-notification admin 'File Manager installation failed!' '<p>Please <a href="https://github.com/hestiacp/hestiacp/issues" target="_blank">open an issue on GitHub</a> to report this to our development team.</p>'
 	# Installation failed, clean up files
 	rm --recursive --force ${FM_INSTALL_DIR}
 	$BIN/v-change-sys-config-value 'FILE_MANAGER' 'false'

+ 1 - 1
bin/v-backup-user

@@ -789,7 +789,7 @@ if [ -e "$BACKUP/$user.log" ] && [ "$notify" = "yes" ]; then
 	subj="$user → backup has been completed"
 	email=$(get_user_value '$CONTACT')
 	cat $BACKUP/$user.log | $SENDMAIL -s "$subj" "$email" "$notify"
-	$BIN/v-add-user-notification "$user" "Backup created successfully" "<b>Archive:</b> $user.$backup_new_date.tar"
+	$BIN/v-add-user-notification "$user" "Backup created successfully" "<p><span class='u-text-bold'>Archive:</span> <code>$user.$backup_new_date.tar</code></p>"
 fi
 
 # Logging

+ 2 - 2
bin/v-download-backup

@@ -83,7 +83,7 @@ if [ ! -e "$BACKUP/$backup" ]; then
 	fi
 	if [ -z "$downloaded" ]; then
 		subj="Download of $backup failed for $user"
-		$BIN/v-add-user-notification $user "$subj" "<b>Unable to retrieve backup file from remote server.</b><br><b>Error:</b> $backup file doesn't exist in '${BACKUP}' directory."
+		$BIN/v-add-user-notification $user "$subj" "<p class'u-text-bold'>Unable to retrieve backup file from remote server.</p><p><span class='u-text-bold'>Error:</span> $backup file doesn't exist in <code>'${BACKUP}'</code> directory.</p>"
 		sed -i "/v-download-backup $user /d" $HESTIA/data/queue/backup.pipe
 		check_result "$E_NOTEXIST" "backup file $backup doesn't exist in '${BACKUP}' folder"
 	else
@@ -105,7 +105,7 @@ if [ -e "$BACKUP/$backup" ]; then
 	subj="Download of $backup completed for $user"
 	email=$(get_user_value '$CONTACT')
 	echo "Backup file $backup was retrieved from the remote server and will be available to download for 12 hours." | $SENDMAIL -s "$subj" "$email" "$notify"
-	$BIN/v-add-user-notification "$user" "$subj" "Backup file <b>$backup</b> was retrieved from the remote server and will be available to download for <b>12 hours</b>."
+	$BIN/v-add-user-notification "$user" "$subj" "<p>Backup file <code>$backup</code> was retrieved from the remote server and will be available to download for 12 hours.</p>"
 fi
 
 # Cleaning restore queue

+ 1 - 1
bin/v-restore-user

@@ -916,7 +916,7 @@ subj="$user → restore has been completed"
 cat $tmpdir/restore.log | $SENDMAIL -s "$subj" $email $notify
 
 # Send notification to panel
-$BIN/v-add-user-notification "$user" "Backup restored successfully" "<b>Archive:</b> $backup"
+$BIN/v-add-user-notification "$user" "Backup restored successfully" "<p><span class='u-text-bold'>Archive:</span> <code>$backup</code></p>"
 
 # Deleting temporary data
 rm -rf $tmpdir

+ 3 - 3
func/upgrade.sh

@@ -183,13 +183,13 @@ upgrade_send_notification_to_panel() {
 	# Add notification to panel if variable is set to true or is not set
 	if [[ "$new_version" =~ "alpha" ]]; then
 		# Send notifications for development releases
-		$BIN/v-add-user-notification admin 'Development snapshot installed' '<b>Version:</b> '$new_version'<br><b>Code Branch:</b> '$RELEASE_BRANCH'<br><br>Please report any bugs by <a href="https://github.com/hestiacp/hestiacp/issues" target="_blank">opening an issue on GitHub</a>, and feel free to share your feedback on our <a href="https://forum.hestiacp.com" target="_blank">discussion forum</a>.<br><br><i class="fas fa-heart icon-red"></i> The Hestia Control Panel development team'
+		$BIN/v-add-user-notification admin 'Development snapshot installed' '<p><span class="u-text-bold">Version:</span> '$new_version'<br><span class="u-text-bold">Code Branch:</span> '$RELEASE_BRANCH'</p><p>Please report any bugs by <a href="https://github.com/hestiacp/hestiacp/issues" target="_blank">opening an issue on GitHub</a>, and feel free to share your feedback on our <a href="https://forum.hestiacp.com" target="_blank">discussion forum</a>.</p><p><i class="fas fa-heart icon-red"></i> The Hestia Control Panel development team</p>'
 	elif [[ "$new_version" =~ "beta" ]]; then
 		# Send feedback notification for beta releases
-		$BIN/v-add-user-notification admin 'Thank you for testing Hestia Control Panel '$new_version'.' '<b>Please share your feedback with our development team through our <a href="https://forum.hestiacp.com" target="_blank">discussion forum</a>.<br><br>Found a bug? <a href="https://github.com/hestiacp/hestiacp/issues" target="_blank">Open an issue on GitHub</a>!</b><br><br><i class="fas fa-heart icon-red"></i> The Hestia Control Panel development team'
+		$BIN/v-add-user-notification admin 'Thank you for testing Hestia Control Panel '$new_version'.' '<p>Please share your feedback with our development team through our <a href="https://forum.hestiacp.com" target="_blank">discussion forum</a>.</p><p>Found a bug? <a href="https://github.com/hestiacp/hestiacp/issues" target="_blank">Open an issue on GitHub</a>!</p><p><i class="fas fa-heart icon-red"></i> The Hestia Control Panel development team</p>'
 	else
 		# Send normal upgrade complete notification for stable releases
-		$BIN/v-add-user-notification admin 'Upgrade complete' 'Hestia Control Panel has been updated to <b>v'$new_version'</b>.<br><a href="https://github.com/hestiacp/hestiacp/blob/release/CHANGELOG.md" target="_blank">View release notes</a><br><br>Please report any bugs by <a href="https://github.com/hestiacp/hestiacp/issues" target="_blank">opening an issue on GitHub</a>.<br><br><b>Have a wonderful day!</b><br><br><i class="fas fa-heart icon-red"></i> The Hestia Control Panel development team'
+		$BIN/v-add-user-notification admin 'Upgrade complete' '<p>Hestia Control Panel has been updated to <span class="u-text-bold">v'$new_version'</span>.</p><p><a href="https://github.com/hestiacp/hestiacp/blob/release/CHANGELOG.md" target="_blank">View release notes</a></p><p>Please report any bugs by <a href="https://github.com/hestiacp/hestiacp/issues" target="_blank">opening an issue on GitHub</a>.</p><p class="u-text-bold">Have a wonderful day!</p><p><i class="fas fa-heart icon-red"></i> The Hestia Control Panel development team</p>'
 	fi
 }
 

+ 3 - 3
install/deb/filemanager/install-fm.sh

@@ -29,7 +29,7 @@ COMPOSER_BIN="$HOMEDIR/$user/.composer/composer"
 if [ ! -f "$COMPOSER_BIN" ]; then
 	$BIN/v-add-user-composer "$user"
 	if [ $? -ne 0 ]; then
-		$BIN/v-add-user-notification admin 'Composer installation failed!' '<b>The File Manager will not work without Composer.</b><br><br>Please try running the installer from a shell session:<br>bash $HESTIA/install/deb/filemanager/install-fm.sh<br><br>If this issue continues, please <a href="https://github.com/hestiacp/hestiacp/issues" target="_blank">open an issue on GitHub</a>.'
+		$BIN/v-add-user-notification admin 'Composer installation failed!' '<p class="u-text-bold">The File Manager will not work without Composer.</p><p>Please try running the installer from a shell session:<br><code>bash $HESTIA/install/deb/filemanager/install-fm.sh</code></p><p>If this issue continues, please <a href="https://github.com/hestiacp/hestiacp/issues" target="_blank">open an issue on GitHub</a>.</p>'
 		fm_error='yes'
 	fi
 fi
@@ -54,11 +54,11 @@ if [ "$fm_error" != "yes" ]; then
 	if [ -f "/usr/bin/php7.3" ]; then
 		COMPOSER_HOME="$HOMEDIR/$user/.config/composer" user_exec /usr/bin/php7.3 $COMPOSER_BIN --quiet --no-dev install
 		if [ $? -ne 0 ]; then
-			$BIN/v-add-user-notification admin 'File Manager installation failed!' 'Please try running the installer from a shell session:<br>bash $HESTIA/install/deb/filemanager/install-fm.sh<br><br>If this issue continues, please <a href="https://github.com/hestiacp/hestiacp/issues" target="_blank">open an issue on GitHub</a>.'
+			$BIN/v-add-user-notification admin 'File Manager installation failed!' '<p>Please try running the installer from a shell session:<br><code>bash $HESTIA/install/deb/filemanager/install-fm.sh</code></p><p>If this issue continues, please <a href="https://github.com/hestiacp/hestiacp/issues" target="_blank">open an issue on GitHub</a>.</p>'
 			fm_error="yes"
 		fi
 	else
-		$BIN/v-add-user-notification admin 'File Manager installation failed!' '<b>Unable to proceed with installation of File Manager.</b><br><br>Package <b>php7.3-cli</b> is missing from your system. Please check your PHP installation and environment settings.'
+		$BIN/v-add-user-notification admin 'File Manager installation failed!' '<p class="u-text-bold">Unable to proceed with installation of File Manager.</p><p>Package <span class="u-text-bold">php7.3-cli</span> is missing from your system. Please check your PHP installation and environment settings.</p>'
 		fm_error="yes"
 	fi
 

+ 1 - 1
install/hst-install-debian.sh

@@ -2248,7 +2248,7 @@ cat $tmpfile
 rm -f $tmpfile
 
 # Add welcome message to notification panel
-$HESTIA/bin/v-add-user-notification admin 'Welcome to Hestia Control Panel!' '<br>You are now ready to begin <a href="/add/user/">adding user accounts</a> and <a href="/add/web/">domains</a>. For help and assistance, <a href="https://hestiacp.com/docs/" target="_blank">view the documentation</a> or <a href="https://forum.hestiacp.com/" target="_blank">visit our forum</a>.<br><br>Please <a href="https://github.com/hestiacp/hestiacp/issues" target="_blank">report any issues via GitHub</a>.<br><br><b>Have a wonderful day!</b><br><br><i class="fas fa-heart icon-red"></i> The Hestia Control Panel development team'
+$HESTIA/bin/v-add-user-notification admin 'Welcome to Hestia Control Panel!' '<p>You are now ready to begin adding <a href="/add/user/">user accounts</a> and <a href="/add/web/">domains</a>. For help and assistance, <a href="https://hestiacp.com/docs/" target="_blank">view the documentation</a> or <a href="https://forum.hestiacp.com/" target="_blank">visit our forum</a>.</p><p>Please <a href="https://github.com/hestiacp/hestiacp/issues" target="_blank">report any issues via GitHub</a>.</p><p class="u-text-bold">Have a wonderful day!</p><p><i class="fas fa-heart icon-red"></i> The Hestia Control Panel development team</p>'
 
 # Clean-up
 # Sort final configuration file

+ 1 - 1
install/hst-install-ubuntu.sh

@@ -2242,7 +2242,7 @@ cat $tmpfile
 rm -f $tmpfile
 
 # Add welcome message to notification panel
-$HESTIA/bin/v-add-user-notification admin 'Welcome to Hestia Control Panel!' '<br>You are now ready to begin <a href="/add/user/">adding user accounts</a> and <a href="/add/web/">domains</a>. For help and assistance, <a href="https://hestiacp.com/docs/" target="_blank">view the documentation</a> or <a href="https://forum.hestiacp.com/" target="_blank">visit our forum</a>.<br><br>Please <a href="https://github.com/hestiacp/hestiacp/issues" target="_blank">report any issues via GitHub</a>.<br><br><b>Have a wonderful day!</b><br><br><i class="fas fa-heart icon-red"></i> The Hestia Control Panel development team'
+$HESTIA/bin/v-add-user-notification admin 'Welcome to Hestia Control Panel!' '<p>You are now ready to begin adding <a href="/add/user/">user accounts</a> and <a href="/add/web/">domains</a>. For help and assistance, <a href="https://hestiacp.com/docs/" target="_blank">view the documentation</a> or <a href="https://forum.hestiacp.com/" target="_blank">visit our forum</a>.</p><p>Please <a href="https://github.com/hestiacp/hestiacp/issues" target="_blank">report any issues via GitHub</a>.</p><p class="u-text-bold">Have a wonderful day!</p><p><i class="fas fa-heart icon-red"></i> The Hestia Control Panel development team</p>'
 
 # Clean-up
 # Sort final configuration file

+ 1 - 1
install/upgrade/versions/1.8.0.sh

@@ -156,7 +156,7 @@ if [ "$WEB_SYSTEM" = "nginx" ] || [ "$PROXY_SYSTEM" = "nginx" ]; then
 		if [ "$nginx_conf_compare" != "same" ]; then
 			echo -e "[ ! ] Manual action required, please view:\n[ - ] $HESTIA_BACKUP/message.log"
 			add_upgrade_message "Manual Action Required [IMPORTANT]\n\nTo enable the \"Enhanced and Optimized TLS\" feature, we must update the NGINX configuration file (/etc/nginx/nginx.conf).\n\nBut for unknown reason or you edited it, may not be fully apply all the changes in this upgrade.\n\nPlease follow the default configuration file to sync it:\n$HESTIA_INSTALL_DIR/nginx/nginx.conf\n\nBacked up configuration file:\n$HESTIA_BACKUP/conf/nginx/nginx.conf\n\nLearn more:\nhttps://github.com/hestiacp/hestiacp/pull/3555"
-			"$BIN"/v-add-user-notification admin "IMPORTANT: Manual Action Required" 'To enable the <b>Enhanced and Optimized TLS</b> feature, we must update the NGINX configuration file (/etc/nginx/nginx.conf).<br><br>But for unknown reason or you edited it, may not be fully apply all the changes in this upgrade.<br><br>Please follow the default configuration file to sync it:<br>'"$HESTIA_INSTALL_DIR"'/nginx/nginx.conf<br><br>Backed up configuration file:<br>'"$HESTIA_BACKUP"'/conf/nginx/nginx.conf<br><br>Visit PR <a href="https://github.com/hestiacp/hestiacp/pull/3555" target="_blank">#3555</a> on GitHub to learn more.'
+			"$BIN"/v-add-user-notification admin "IMPORTANT: Manual Action Required" '<p>To enable the "Enhanced and Optimized TLS" feature, we must update the NGINX configuration file at <code>/etc/nginx/nginx.conf</code>.</p><p>But for unknown reason or you edited it, may not be fully apply all the changes in this upgrade.</p><p>Please follow the default configuration file to sync it:<br><code>'"$HESTIA_INSTALL_DIR"'/nginx/nginx.conf</code></p><p>Backed up configuration file:<br><code>'"$HESTIA_BACKUP"'/conf/nginx/nginx.conf</code></p><p>Visit PR <a href="https://github.com/hestiacp/hestiacp/pull/3555" target="_blank">#3555</a> on GitHub to learn more.</p>'
 
 			sed -i "s/""$(grep -m 1 "IMPORTANT: Manual Action Required" "$HESTIA"/data/users/admin/notifications.conf | awk '{print $1}')""/NID='1'/" "$HESTIA"/data/users/admin/notifications.conf
 

+ 171 - 171
package-lock.json

@@ -19,10 +19,10 @@
 			},
 			"devDependencies": {
 				"@prettier/plugin-php": "^0.19.6",
-				"@typescript-eslint/eslint-plugin": "^5.59.11",
-				"@typescript-eslint/parser": "^5.59.11",
+				"@typescript-eslint/eslint-plugin": "^5.60.0",
+				"@typescript-eslint/parser": "^5.60.0",
 				"cssnano": "^6.0.1",
-				"esbuild": "^0.18.4",
+				"esbuild": "^0.18.6",
 				"eslint": "^8.43.0",
 				"eslint-config-prettier": "^8.8.0",
 				"eslint-plugin-editorconfig": "^4.0.3",
@@ -33,7 +33,7 @@
 				"postcss": "^8.4.24",
 				"postcss-import": "^15.1.0",
 				"postcss-path-replace": "^1.0.4",
-				"postcss-preset-env": "^8.5.0",
+				"postcss-preset-env": "^8.5.1",
 				"postcss-size": "^4.0.1",
 				"prettier": "^2.8.8",
 				"prettier-plugin-nginx": "^1.0.3",
@@ -42,7 +42,7 @@
 				"stylelint": "^15.8.0",
 				"stylelint-config-standard": "^33.0.0",
 				"typescript": "^5.1.3",
-				"vitepress": "1.0.0-beta.2",
+				"vitepress": "1.0.0-beta.3",
 				"vue": "^3.3.4"
 			}
 		},
@@ -454,9 +454,9 @@
 			}
 		},
 		"node_modules/@csstools/media-query-list-parser": {
-			"version": "2.1.0",
-			"resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-2.1.0.tgz",
-			"integrity": "sha512-MXkR+TeaS2q9IkpyO6jVCdtA/bfpABJxIrfkLswThFN8EZZgI2RfAHhm6sDNDuYV25d5+b8Lj1fpTccIcSLPsQ==",
+			"version": "2.1.1",
+			"resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-2.1.1.tgz",
+			"integrity": "sha512-pUjtFbaKbiFNjJo8pprrIaXLvQvWIlwPiFnRI4sEnc4F0NIGTOsw8kaJSR3CmZAKEvV8QYckovgAnWQC0bgLLQ==",
 			"dev": true,
 			"funding": [
 				{
@@ -472,7 +472,7 @@
 				"node": "^14 || ^16 || >=18"
 			},
 			"peerDependencies": {
-				"@csstools/css-parser-algorithms": "^2.1.1",
+				"@csstools/css-parser-algorithms": "^2.2.0",
 				"@csstools/css-tokenizer": "^2.1.1"
 			}
 		},
@@ -733,9 +733,9 @@
 			}
 		},
 		"node_modules/@csstools/postcss-media-minmax": {
-			"version": "1.0.3",
-			"resolved": "https://registry.npmjs.org/@csstools/postcss-media-minmax/-/postcss-media-minmax-1.0.3.tgz",
-			"integrity": "sha512-os7qe2HV/qBILKCGa/dl5AbpO6c+MZyunFBWPWJBrEVhulCYo13FgEWbhyERFM5FeJghiqYgJxM54oiJASpBnw==",
+			"version": "1.0.4",
+			"resolved": "https://registry.npmjs.org/@csstools/postcss-media-minmax/-/postcss-media-minmax-1.0.4.tgz",
+			"integrity": "sha512-olnKTQk9+RMzpIpkjv55d44L4Ni02j8ZJoedJezQC5M03a56npcM1hx0apaTRG4Fz1wfPCQ0DBjQ8zsiJFelmA==",
 			"dev": true,
 			"funding": [
 				{
@@ -749,9 +749,9 @@
 			],
 			"dependencies": {
 				"@csstools/css-calc": "^1.1.1",
-				"@csstools/css-parser-algorithms": "^2.1.1",
+				"@csstools/css-parser-algorithms": "^2.2.0",
 				"@csstools/css-tokenizer": "^2.1.1",
-				"@csstools/media-query-list-parser": "^2.1.0"
+				"@csstools/media-query-list-parser": "^2.1.1"
 			},
 			"engines": {
 				"node": "^14 || ^16 || >=18"
@@ -761,9 +761,9 @@
 			}
 		},
 		"node_modules/@csstools/postcss-media-queries-aspect-ratio-number-values": {
-			"version": "1.0.3",
-			"resolved": "https://registry.npmjs.org/@csstools/postcss-media-queries-aspect-ratio-number-values/-/postcss-media-queries-aspect-ratio-number-values-1.0.3.tgz",
-			"integrity": "sha512-JHdwBSNZsur/mJXwzuC/gxyekhfSdWJaTiSOhUITk2D8pYRYcjV1MZiCiWupQNfM2Qp2W7w1A/gEU6U/xlpIyA==",
+			"version": "1.0.4",
+			"resolved": "https://registry.npmjs.org/@csstools/postcss-media-queries-aspect-ratio-number-values/-/postcss-media-queries-aspect-ratio-number-values-1.0.4.tgz",
+			"integrity": "sha512-IwyTbyR8E2y3kh6Fhrs251KjKBJeUPV5GlnUKnpU70PRFEN2DolWbf2V4+o/B9+Oj77P/DullLTulWEQ8uFtAA==",
 			"dev": true,
 			"funding": [
 				{
@@ -776,9 +776,9 @@
 				}
 			],
 			"dependencies": {
-				"@csstools/css-parser-algorithms": "^2.1.1",
+				"@csstools/css-parser-algorithms": "^2.2.0",
 				"@csstools/css-tokenizer": "^2.1.1",
-				"@csstools/media-query-list-parser": "^2.1.0"
+				"@csstools/media-query-list-parser": "^2.1.1"
 			},
 			"engines": {
 				"node": "^14 || ^16 || >=18"
@@ -1070,9 +1070,9 @@
 			}
 		},
 		"node_modules/@esbuild/android-arm": {
-			"version": "0.18.4",
-			"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.4.tgz",
-			"integrity": "sha512-yKmQC9IiuvHdsNEbPHSprnMHg6OhL1cSeQZLzPpgzJBJ9ppEg9GAZN8MKj1TcmB4tZZUrq5xjK7KCmhwZP8iDA==",
+			"version": "0.18.6",
+			"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.6.tgz",
+			"integrity": "sha512-J3lwhDSXBBppSzm/LC1uZ8yKSIpExc+5T8MxrYD9KNVZG81FOAu2VF2gXi/6A/LwDDQQ+b6DpQbYlo3VwxFepQ==",
 			"cpu": [
 				"arm"
 			],
@@ -1086,9 +1086,9 @@
 			}
 		},
 		"node_modules/@esbuild/android-arm64": {
-			"version": "0.18.4",
-			"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.4.tgz",
-			"integrity": "sha512-yQVgO+V307hA2XhzELQ6F91CBGX7gSnlVGAj5YIqjQOxThDpM7fOcHT2YLJbE6gNdPtgRSafQrsK8rJ9xHCaZg==",
+			"version": "0.18.6",
+			"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.6.tgz",
+			"integrity": "sha512-pL0Ci8P9q1sWbtPx8CXbc8JvPvvYdJJQ+LO09PLFsbz3aYNdFBGWJjiHU+CaObO4Ames+GOFpXRAJZS2L3ZK/A==",
 			"cpu": [
 				"arm64"
 			],
@@ -1102,9 +1102,9 @@
 			}
 		},
 		"node_modules/@esbuild/android-x64": {
-			"version": "0.18.4",
-			"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.4.tgz",
-			"integrity": "sha512-yLKXMxQg6sk1ntftxQ5uwyVgG4/S2E7UoOCc5N4YZW7fdkfRiYEXqm7CMuIfY2Vs3FTrNyKmSfNevIuIvJnMww==",
+			"version": "0.18.6",
+			"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.6.tgz",
+			"integrity": "sha512-hE2vZxOlJ05aY28lUpB0y0RokngtZtcUB+TVl9vnLEnY0z/8BicSvrkThg5/iI1rbf8TwXrbr2heEjl9fLf+EA==",
 			"cpu": [
 				"x64"
 			],
@@ -1118,9 +1118,9 @@
 			}
 		},
 		"node_modules/@esbuild/darwin-arm64": {
-			"version": "0.18.4",
-			"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.4.tgz",
-			"integrity": "sha512-MVPEoZjZpk2xQ1zckZrb8eQuQib+QCzdmMs3YZAYEQPg+Rztk5pUxGyk8htZOC8Z38NMM29W+MqY9Sqo/sDGKw==",
+			"version": "0.18.6",
+			"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.6.tgz",
+			"integrity": "sha512-/tuyl4R+QhhoROQtuQj9E/yfJtZNdv2HKaHwYhhHGQDN1Teziem2Kh7BWQMumfiY7Lu9g5rO7scWdGE4OsQ6MQ==",
 			"cpu": [
 				"arm64"
 			],
@@ -1134,9 +1134,9 @@
 			}
 		},
 		"node_modules/@esbuild/darwin-x64": {
-			"version": "0.18.4",
-			"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.4.tgz",
-			"integrity": "sha512-uEsRtYRUDsz7i2tXg/t/SyF+5gU1cvi9B6B8i5ebJgtUUHJYWyIPIesmIOL4/+bywjxsDMA/XrNFMgMffLnh5A==",
+			"version": "0.18.6",
+			"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.6.tgz",
+			"integrity": "sha512-L7IQga2pDT+14Ti8HZwsVfbCjuKP4U213T3tuPggOzyK/p4KaUJxQFXJgfUFHKzU0zOXx8QcYRYZf0hSQtppkw==",
 			"cpu": [
 				"x64"
 			],
@@ -1150,9 +1150,9 @@
 			}
 		},
 		"node_modules/@esbuild/freebsd-arm64": {
-			"version": "0.18.4",
-			"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.4.tgz",
-			"integrity": "sha512-I8EOigqWnOHRin6Zp5Y1cfH3oT54bd7Sdz/VnpUNksbOtfp8IWRTH4pgkgO5jWaRQPjCpJcOpdRjYAMjPt8wXg==",
+			"version": "0.18.6",
+			"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.6.tgz",
+			"integrity": "sha512-bq10jFv42V20Kk77NvmO+WEZaLHBKuXcvEowixnBOMkaBgS7kQaqTc77ZJDbsUpXU3KKNLQFZctfaeINmeTsZA==",
 			"cpu": [
 				"arm64"
 			],
@@ -1166,9 +1166,9 @@
 			}
 		},
 		"node_modules/@esbuild/freebsd-x64": {
-			"version": "0.18.4",
-			"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.4.tgz",
-			"integrity": "sha512-1bHfgMz/cNMjbpsYxjVgMJ1iwKq+NdDPlACBrWULD7ZdFmBQrhMicMaKb5CdmdVyvIwXmasOuF4r6Iq574kUTA==",
+			"version": "0.18.6",
+			"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.6.tgz",
+			"integrity": "sha512-HbDLlkDZqUMBQaiday0pJzB6/8Xx/10dI3xRebJBReOEeDSeS+7GzTtW9h8ZnfB7/wBCqvtAjGtWQLTNPbR2+g==",
 			"cpu": [
 				"x64"
 			],
@@ -1182,9 +1182,9 @@
 			}
 		},
 		"node_modules/@esbuild/linux-arm": {
-			"version": "0.18.4",
-			"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.4.tgz",
-			"integrity": "sha512-4XCGqM/Ay1LCXUBH59bL4JbSbbTK1K22dWHymWMGaEh2sQCDOUw+OQxozYV/YdBb91leK2NbuSrE2BRamwgaYw==",
+			"version": "0.18.6",
+			"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.6.tgz",
+			"integrity": "sha512-C+5kb6rgsGMmvIdUI7v1PPgC98A6BMv233e97aXZ5AE03iMdlILFD/20HlHrOi0x2CzbspXn9HOnlE4/Ijn5Kw==",
 			"cpu": [
 				"arm"
 			],
@@ -1198,9 +1198,9 @@
 			}
 		},
 		"node_modules/@esbuild/linux-arm64": {
-			"version": "0.18.4",
-			"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.4.tgz",
-			"integrity": "sha512-J42vLHaYREyiBwH0eQE4/7H1DTfZx8FuxyWSictx4d7ezzuKE3XOkIvOg+SQzRz7T9HLVKzq2tvbAov4UfufBw==",
+			"version": "0.18.6",
+			"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.6.tgz",
+			"integrity": "sha512-NMY9yg/88MskEZH2s4i6biz/3av+M8xY5ua4HE7CCz5DBz542cr7REe317+v7oKjnYBCijHpkzo5vU85bkXQmQ==",
 			"cpu": [
 				"arm64"
 			],
@@ -1214,9 +1214,9 @@
 			}
 		},
 		"node_modules/@esbuild/linux-ia32": {
-			"version": "0.18.4",
-			"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.4.tgz",
-			"integrity": "sha512-4ksIqFwhq7OExty7Sl1n0vqQSCqTG4sU6i99G2yuMr28CEOUZ/60N+IO9hwI8sIxBqmKmDgncE1n5CMu/3m0IA==",
+			"version": "0.18.6",
+			"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.6.tgz",
+			"integrity": "sha512-AXazA0ljvQEp7cA9jscABNXsjodKbEcqPcAE3rDzKN82Vb3lYOq6INd+HOCA7hk8IegEyHW4T72Z7QGIhyCQEA==",
 			"cpu": [
 				"ia32"
 			],
@@ -1230,9 +1230,9 @@
 			}
 		},
 		"node_modules/@esbuild/linux-loong64": {
-			"version": "0.18.4",
-			"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.4.tgz",
-			"integrity": "sha512-bsWtoVHkGQgAsFXioDueXRiUIfSGrVkJjBBz4gcBJxXcD461cWFQFyu8Fxdj9TP+zEeqJ8C/O4LFFMBNi6Fscw==",
+			"version": "0.18.6",
+			"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.6.tgz",
+			"integrity": "sha512-JjBf7TwY7ldcPgHYt9UcrjZB03+WZqg/jSwMAfzOzM5ZG+tu5umUqzy5ugH/crGI4eoDIhSOTDp1NL3Uo/05Fw==",
 			"cpu": [
 				"loong64"
 			],
@@ -1246,9 +1246,9 @@
 			}
 		},
 		"node_modules/@esbuild/linux-mips64el": {
-			"version": "0.18.4",
-			"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.4.tgz",
-			"integrity": "sha512-LRD9Fu8wJQgIOOV1o3nRyzrheFYjxA0C1IVWZ93eNRRWBKgarYFejd5WBtrp43cE4y4D4t3qWWyklm73Mrsd/g==",
+			"version": "0.18.6",
+			"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.6.tgz",
+			"integrity": "sha512-kATNsslryVxcH1sO3KP2nnyUWtZZVkgyhAUnyTVVa0OQQ9pmDRjTpHaE+2EQHoCM5wt/uav2edrAUqbwn3tkKQ==",
 			"cpu": [
 				"mips64el"
 			],
@@ -1262,9 +1262,9 @@
 			}
 		},
 		"node_modules/@esbuild/linux-ppc64": {
-			"version": "0.18.4",
-			"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.4.tgz",
-			"integrity": "sha512-jtQgoZjM92gauVRxNaaG/TpL3Pr4WcL3Pwqi9QgdrBGrEXzB+twohQiWNSTycs6lUygakos4mm2h0B9/SHveng==",
+			"version": "0.18.6",
+			"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.6.tgz",
+			"integrity": "sha512-B+wTKz+8pi7mcWXFQV0LA79dJ+qhiut5uK9q0omoKnq8yRIwQJwfg3/vclXoqqcX89Ri5Y5538V0Se2v5qlcLA==",
 			"cpu": [
 				"ppc64"
 			],
@@ -1278,9 +1278,9 @@
 			}
 		},
 		"node_modules/@esbuild/linux-riscv64": {
-			"version": "0.18.4",
-			"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.4.tgz",
-			"integrity": "sha512-7WaU/kRZG0VCV09Xdlkg6LNAsfU9SAxo6XEdaZ8ffO4lh+DZoAhGTx7+vTMOXKxa+r2w1LYDGxfJa2rcgagMRA==",
+			"version": "0.18.6",
+			"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.6.tgz",
+			"integrity": "sha512-h44RBLVXFUSjvhOfseE+5UxQ/r9LVeqK2S8JziJKOm9W7SePYRPDyn7MhzhNCCFPkcjIy+soCxfhlJXHXXCR0A==",
 			"cpu": [
 				"riscv64"
 			],
@@ -1294,9 +1294,9 @@
 			}
 		},
 		"node_modules/@esbuild/linux-s390x": {
-			"version": "0.18.4",
-			"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.4.tgz",
-			"integrity": "sha512-D19ed0xreKQvC5t+ArE2njSnm18WPpE+1fhwaiJHf+Xwqsq+/SUaV8Mx0M27nszdU+Atq1HahrgCOZCNNEASUg==",
+			"version": "0.18.6",
+			"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.6.tgz",
+			"integrity": "sha512-FlYpyr2Xc2AUePoAbc84NRV+mj7xpsISeQ36HGf9etrY5rTBEA+IU9HzWVmw5mDFtC62EQxzkLRj8h5Hq85yOQ==",
 			"cpu": [
 				"s390x"
 			],
@@ -1310,9 +1310,9 @@
 			}
 		},
 		"node_modules/@esbuild/linux-x64": {
-			"version": "0.18.4",
-			"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.4.tgz",
-			"integrity": "sha512-Rx3AY1sxyiO/gvCGP00nL69L60dfmWyjKWY06ugpB8Ydpdsfi3BHW58HWC24K3CAjAPSwxcajozC2PzA9JBS1g==",
+			"version": "0.18.6",
+			"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.6.tgz",
+			"integrity": "sha512-Mc4EUSYwzLci77u0Kao6ajB2WbTe5fNc7+lHwS3a+vJISC/oprwURezUYu1SdWAYoczbsyOvKAJwuNftoAdjjg==",
 			"cpu": [
 				"x64"
 			],
@@ -1326,9 +1326,9 @@
 			}
 		},
 		"node_modules/@esbuild/netbsd-x64": {
-			"version": "0.18.4",
-			"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.4.tgz",
-			"integrity": "sha512-AaShPmN9c6w1mKRpliKFlaWcSkpBT4KOlk93UfFgeI3F3cbjzdDKGsbKnOZozmYbE1izZKLmNJiW0sFM+A5JPA==",
+			"version": "0.18.6",
+			"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.6.tgz",
+			"integrity": "sha512-3hgZlp7NqIM5lNG3fpdhBI5rUnPmdahraSmwAi+YX/bp7iZ7mpTv2NkypGs/XngdMtpzljICxnUG3uPfqLFd3w==",
 			"cpu": [
 				"x64"
 			],
@@ -1342,9 +1342,9 @@
 			}
 		},
 		"node_modules/@esbuild/openbsd-x64": {
-			"version": "0.18.4",
-			"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.4.tgz",
-			"integrity": "sha512-tRGvGwou3BrvHVvF8HxTqEiC5VtPzySudS9fh2jBIKpLX7HCW8jIkW+LunkFDNwhslx4xMAgh0jAHsx/iCymaQ==",
+			"version": "0.18.6",
+			"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.6.tgz",
+			"integrity": "sha512-aEWTdZQHtSRROlDYn7ygB8yAqtnall/UnmoVIJVqccKitkAWVVSYocQUWrBOxLEFk8XdlRouVrLZe6WXszyviA==",
 			"cpu": [
 				"x64"
 			],
@@ -1358,9 +1358,9 @@
 			}
 		},
 		"node_modules/@esbuild/sunos-x64": {
-			"version": "0.18.4",
-			"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.4.tgz",
-			"integrity": "sha512-acORFDI95GKhmAnlH8EarBeuqoy/j3yxIU+FDB91H3+ZON+8HhTadtT450YkaMzX6lEWbhi+mjVUCj00M5yyOQ==",
+			"version": "0.18.6",
+			"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.6.tgz",
+			"integrity": "sha512-uxk/5yAGpjKZUHOECtI9W+9IcLjKj+2m0qf+RG7f7eRBHr8wP6wsr3XbNbgtOD1qSpPapd6R2ZfSeXTkCcAo5g==",
 			"cpu": [
 				"x64"
 			],
@@ -1374,9 +1374,9 @@
 			}
 		},
 		"node_modules/@esbuild/win32-arm64": {
-			"version": "0.18.4",
-			"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.4.tgz",
-			"integrity": "sha512-1NxP+iOk8KSvS1L9SSxEvBAJk39U0GiGZkiiJGbuDF9G4fG7DSDw6XLxZMecAgmvQrwwx7yVKdNN3GgNh0UfKg==",
+			"version": "0.18.6",
+			"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.6.tgz",
+			"integrity": "sha512-oXlXGS9zvNCGoAT/tLHAsFKrIKye1JaIIP0anCdpaI+Dc10ftaNZcqfLzEwyhdzFAYInXYH4V7kEdH4hPyo9GA==",
 			"cpu": [
 				"arm64"
 			],
@@ -1390,9 +1390,9 @@
 			}
 		},
 		"node_modules/@esbuild/win32-ia32": {
-			"version": "0.18.4",
-			"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.4.tgz",
-			"integrity": "sha512-OKr8jze93vbgqZ/r23woWciTixUwLa976C9W7yNBujtnVHyvsL/ocYG61tsktUfJOpyIz5TsohkBZ6Lo2+PCcQ==",
+			"version": "0.18.6",
+			"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.6.tgz",
+			"integrity": "sha512-qh7IcAHUvvmMBmoIG+V+BbE9ZWSR0ohF51e5g8JZvU08kZF58uDFL5tHs0eoYz31H6Finv17te3W3QB042GqVA==",
 			"cpu": [
 				"ia32"
 			],
@@ -1406,9 +1406,9 @@
 			}
 		},
 		"node_modules/@esbuild/win32-x64": {
-			"version": "0.18.4",
-			"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.4.tgz",
-			"integrity": "sha512-qJr3wVvcLjPFcV4AMDS3iquhBfTef2zo/jlm8RMxmiRp3Vy2HY8WMxrykJlcbCnqLXZPA0YZxZGND6eug85ogg==",
+			"version": "0.18.6",
+			"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.6.tgz",
+			"integrity": "sha512-9UDwkz7Wlm4N9jnv+4NL7F8vxLhSZfEkRArz2gD33HesAFfMLGIGNVXRoIHtWNw8feKsnGly9Hq1EUuRkWl0zA==",
 			"cpu": [
 				"x64"
 			],
@@ -1651,15 +1651,15 @@
 			"dev": true
 		},
 		"node_modules/@typescript-eslint/eslint-plugin": {
-			"version": "5.59.11",
-			"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.59.11.tgz",
-			"integrity": "sha512-XxuOfTkCUiOSyBWIvHlUraLw/JT/6Io1365RO6ZuI88STKMavJZPNMU0lFcUTeQXEhHiv64CbxYxBNoDVSmghg==",
+			"version": "5.60.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.60.0.tgz",
+			"integrity": "sha512-78B+anHLF1TI8Jn/cD0Q00TBYdMgjdOn980JfAVa9yw5sop8nyTfVOQAv6LWywkOGLclDBtv5z3oxN4w7jxyNg==",
 			"dev": true,
 			"dependencies": {
 				"@eslint-community/regexpp": "^4.4.0",
-				"@typescript-eslint/scope-manager": "5.59.11",
-				"@typescript-eslint/type-utils": "5.59.11",
-				"@typescript-eslint/utils": "5.59.11",
+				"@typescript-eslint/scope-manager": "5.60.0",
+				"@typescript-eslint/type-utils": "5.60.0",
+				"@typescript-eslint/utils": "5.60.0",
 				"debug": "^4.3.4",
 				"grapheme-splitter": "^1.0.4",
 				"ignore": "^5.2.0",
@@ -1685,14 +1685,14 @@
 			}
 		},
 		"node_modules/@typescript-eslint/parser": {
-			"version": "5.59.11",
-			"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.59.11.tgz",
-			"integrity": "sha512-s9ZF3M+Nym6CAZEkJJeO2TFHHDsKAM3ecNkLuH4i4s8/RCPnF5JRip2GyviYkeEAcwGMJxkqG9h2dAsnA1nZpA==",
+			"version": "5.60.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.60.0.tgz",
+			"integrity": "sha512-jBONcBsDJ9UoTWrARkRRCgDz6wUggmH5RpQVlt7BimSwaTkTjwypGzKORXbR4/2Hqjk9hgwlon2rVQAjWNpkyQ==",
 			"dev": true,
 			"dependencies": {
-				"@typescript-eslint/scope-manager": "5.59.11",
-				"@typescript-eslint/types": "5.59.11",
-				"@typescript-eslint/typescript-estree": "5.59.11",
+				"@typescript-eslint/scope-manager": "5.60.0",
+				"@typescript-eslint/types": "5.60.0",
+				"@typescript-eslint/typescript-estree": "5.60.0",
 				"debug": "^4.3.4"
 			},
 			"engines": {
@@ -1712,13 +1712,13 @@
 			}
 		},
 		"node_modules/@typescript-eslint/scope-manager": {
-			"version": "5.59.11",
-			"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.59.11.tgz",
-			"integrity": "sha512-dHFOsxoLFtrIcSj5h0QoBT/89hxQONwmn3FOQ0GOQcLOOXm+MIrS8zEAhs4tWl5MraxCY3ZJpaXQQdFMc2Tu+Q==",
+			"version": "5.60.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.60.0.tgz",
+			"integrity": "sha512-hakuzcxPwXi2ihf9WQu1BbRj1e/Pd8ZZwVTG9kfbxAMZstKz8/9OoexIwnmLzShtsdap5U/CoQGRCWlSuPbYxQ==",
 			"dev": true,
 			"dependencies": {
-				"@typescript-eslint/types": "5.59.11",
-				"@typescript-eslint/visitor-keys": "5.59.11"
+				"@typescript-eslint/types": "5.60.0",
+				"@typescript-eslint/visitor-keys": "5.60.0"
 			},
 			"engines": {
 				"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
@@ -1729,13 +1729,13 @@
 			}
 		},
 		"node_modules/@typescript-eslint/type-utils": {
-			"version": "5.59.11",
-			"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.59.11.tgz",
-			"integrity": "sha512-LZqVY8hMiVRF2a7/swmkStMYSoXMFlzL6sXV6U/2gL5cwnLWQgLEG8tjWPpaE4rMIdZ6VKWwcffPlo1jPfk43g==",
+			"version": "5.60.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.60.0.tgz",
+			"integrity": "sha512-X7NsRQddORMYRFH7FWo6sA9Y/zbJ8s1x1RIAtnlj6YprbToTiQnM6vxcMu7iYhdunmoC0rUWlca13D5DVHkK2g==",
 			"dev": true,
 			"dependencies": {
-				"@typescript-eslint/typescript-estree": "5.59.11",
-				"@typescript-eslint/utils": "5.59.11",
+				"@typescript-eslint/typescript-estree": "5.60.0",
+				"@typescript-eslint/utils": "5.60.0",
 				"debug": "^4.3.4",
 				"tsutils": "^3.21.0"
 			},
@@ -1756,9 +1756,9 @@
 			}
 		},
 		"node_modules/@typescript-eslint/types": {
-			"version": "5.59.11",
-			"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.59.11.tgz",
-			"integrity": "sha512-epoN6R6tkvBYSc+cllrz+c2sOFWkbisJZWkOE+y3xHtvYaOE6Wk6B8e114McRJwFRjGvYdJwLXQH5c9osME/AA==",
+			"version": "5.60.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.60.0.tgz",
+			"integrity": "sha512-ascOuoCpNZBccFVNJRSC6rPq4EmJ2NkuoKnd6LDNyAQmdDnziAtxbCGWCbefG1CNzmDvd05zO36AmB7H8RzKPA==",
 			"dev": true,
 			"engines": {
 				"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
@@ -1769,13 +1769,13 @@
 			}
 		},
 		"node_modules/@typescript-eslint/typescript-estree": {
-			"version": "5.59.11",
-			"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.11.tgz",
-			"integrity": "sha512-YupOpot5hJO0maupJXixi6l5ETdrITxeo5eBOeuV7RSKgYdU3G5cxO49/9WRnJq9EMrB7AuTSLH/bqOsXi7wPA==",
+			"version": "5.60.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.60.0.tgz",
+			"integrity": "sha512-R43thAuwarC99SnvrBmh26tc7F6sPa2B3evkXp/8q954kYL6Ro56AwASYWtEEi+4j09GbiNAHqYwNNZuNlARGQ==",
 			"dev": true,
 			"dependencies": {
-				"@typescript-eslint/types": "5.59.11",
-				"@typescript-eslint/visitor-keys": "5.59.11",
+				"@typescript-eslint/types": "5.60.0",
+				"@typescript-eslint/visitor-keys": "5.60.0",
 				"debug": "^4.3.4",
 				"globby": "^11.1.0",
 				"is-glob": "^4.0.3",
@@ -1796,17 +1796,17 @@
 			}
 		},
 		"node_modules/@typescript-eslint/utils": {
-			"version": "5.59.11",
-			"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.59.11.tgz",
-			"integrity": "sha512-didu2rHSOMUdJThLk4aZ1Or8IcO3HzCw/ZvEjTTIfjIrcdd5cvSIwwDy2AOlE7htSNp7QIZ10fLMyRCveesMLg==",
+			"version": "5.60.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.60.0.tgz",
+			"integrity": "sha512-ba51uMqDtfLQ5+xHtwlO84vkdjrqNzOnqrnwbMHMRY8Tqeme8C2Q8Fc7LajfGR+e3/4LoYiWXUM6BpIIbHJ4hQ==",
 			"dev": true,
 			"dependencies": {
 				"@eslint-community/eslint-utils": "^4.2.0",
 				"@types/json-schema": "^7.0.9",
 				"@types/semver": "^7.3.12",
-				"@typescript-eslint/scope-manager": "5.59.11",
-				"@typescript-eslint/types": "5.59.11",
-				"@typescript-eslint/typescript-estree": "5.59.11",
+				"@typescript-eslint/scope-manager": "5.60.0",
+				"@typescript-eslint/types": "5.60.0",
+				"@typescript-eslint/typescript-estree": "5.60.0",
 				"eslint-scope": "^5.1.1",
 				"semver": "^7.3.7"
 			},
@@ -1822,12 +1822,12 @@
 			}
 		},
 		"node_modules/@typescript-eslint/visitor-keys": {
-			"version": "5.59.11",
-			"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.11.tgz",
-			"integrity": "sha512-KGYniTGG3AMTuKF9QBD7EIrvufkB6O6uX3knP73xbKLMpH+QRPcgnCxjWXSHjMRuOxFLovljqQgQpR0c7GvjoA==",
+			"version": "5.60.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.60.0.tgz",
+			"integrity": "sha512-wm9Uz71SbCyhUKgcaPRauBdTegUyY/ZWl8gLwD/i/ybJqscrrdVSFImpvUz16BLPChIeKBK5Fa9s6KDQjsjyWw==",
 			"dev": true,
 			"dependencies": {
-				"@typescript-eslint/types": "5.59.11",
+				"@typescript-eslint/types": "5.60.0",
 				"eslint-visitor-keys": "^3.3.0"
 			},
 			"engines": {
@@ -2656,9 +2656,9 @@
 			}
 		},
 		"node_modules/caniuse-lite": {
-			"version": "1.0.30001503",
-			"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001503.tgz",
-			"integrity": "sha512-Sf9NiF+wZxPfzv8Z3iS0rXM1Do+iOy2Lxvib38glFX+08TCYYYGR5fRJXk4d77C4AYwhUjgYgMsMudbh2TqCKw==",
+			"version": "1.0.30001506",
+			"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001506.tgz",
+			"integrity": "sha512-6XNEcpygZMCKaufIcgpQNZNf00GEqc7VQON+9Rd0K1bMYo8xhMZRAo5zpbnbMNizi4YNgIDAFrdykWsvY3H4Hw==",
 			"dev": true,
 			"funding": [
 				{
@@ -3346,9 +3346,9 @@
 			}
 		},
 		"node_modules/electron-to-chromium": {
-			"version": "1.4.433",
-			"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.433.tgz",
-			"integrity": "sha512-MGO1k0w1RgrfdbLVwmXcDhHHuxCn2qRgR7dYsJvWFKDttvYPx6FNzCGG0c/fBBvzK2LDh3UV7Tt9awnHnvAAUQ==",
+			"version": "1.4.436",
+			"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.436.tgz",
+			"integrity": "sha512-aktOxo8fnrMC8vOIBMVS3PXbT1nrPQ+SouUuN7Y0a+Rw3pOMrvIV92Ybnax7x4tugA+ZpYA5fOHTby7ama8OQQ==",
 			"dev": true
 		},
 		"node_modules/emoji-regex": {
@@ -3467,9 +3467,9 @@
 			}
 		},
 		"node_modules/esbuild": {
-			"version": "0.18.4",
-			"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.4.tgz",
-			"integrity": "sha512-9rxWV/Cb2DMUXfe9aUsYtqg0KTlw146ElFH22kYeK9KVV1qT082X4lpmiKsa12ePiCcIcB686TQJxaGAa9TFvA==",
+			"version": "0.18.6",
+			"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.6.tgz",
+			"integrity": "sha512-5QgxWaAhU/tPBpvkxUmnFv2YINHuZzjbk0LeUUnC2i3aJHjfi5yR49lgKgF7cb98bclOp/kans8M5TGbGFfJlQ==",
 			"dev": true,
 			"hasInstallScript": true,
 			"bin": {
@@ -3479,28 +3479,28 @@
 				"node": ">=12"
 			},
 			"optionalDependencies": {
-				"@esbuild/android-arm": "0.18.4",
-				"@esbuild/android-arm64": "0.18.4",
-				"@esbuild/android-x64": "0.18.4",
-				"@esbuild/darwin-arm64": "0.18.4",
-				"@esbuild/darwin-x64": "0.18.4",
-				"@esbuild/freebsd-arm64": "0.18.4",
-				"@esbuild/freebsd-x64": "0.18.4",
-				"@esbuild/linux-arm": "0.18.4",
-				"@esbuild/linux-arm64": "0.18.4",
-				"@esbuild/linux-ia32": "0.18.4",
-				"@esbuild/linux-loong64": "0.18.4",
-				"@esbuild/linux-mips64el": "0.18.4",
-				"@esbuild/linux-ppc64": "0.18.4",
-				"@esbuild/linux-riscv64": "0.18.4",
-				"@esbuild/linux-s390x": "0.18.4",
-				"@esbuild/linux-x64": "0.18.4",
-				"@esbuild/netbsd-x64": "0.18.4",
-				"@esbuild/openbsd-x64": "0.18.4",
-				"@esbuild/sunos-x64": "0.18.4",
-				"@esbuild/win32-arm64": "0.18.4",
-				"@esbuild/win32-ia32": "0.18.4",
-				"@esbuild/win32-x64": "0.18.4"
+				"@esbuild/android-arm": "0.18.6",
+				"@esbuild/android-arm64": "0.18.6",
+				"@esbuild/android-x64": "0.18.6",
+				"@esbuild/darwin-arm64": "0.18.6",
+				"@esbuild/darwin-x64": "0.18.6",
+				"@esbuild/freebsd-arm64": "0.18.6",
+				"@esbuild/freebsd-x64": "0.18.6",
+				"@esbuild/linux-arm": "0.18.6",
+				"@esbuild/linux-arm64": "0.18.6",
+				"@esbuild/linux-ia32": "0.18.6",
+				"@esbuild/linux-loong64": "0.18.6",
+				"@esbuild/linux-mips64el": "0.18.6",
+				"@esbuild/linux-ppc64": "0.18.6",
+				"@esbuild/linux-riscv64": "0.18.6",
+				"@esbuild/linux-s390x": "0.18.6",
+				"@esbuild/linux-x64": "0.18.6",
+				"@esbuild/netbsd-x64": "0.18.6",
+				"@esbuild/openbsd-x64": "0.18.6",
+				"@esbuild/sunos-x64": "0.18.6",
+				"@esbuild/win32-arm64": "0.18.6",
+				"@esbuild/win32-ia32": "0.18.6",
+				"@esbuild/win32-x64": "0.18.6"
 			}
 		},
 		"node_modules/escalade": {
@@ -6190,9 +6190,9 @@
 			}
 		},
 		"node_modules/postcss-custom-media": {
-			"version": "9.1.4",
-			"resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-9.1.4.tgz",
-			"integrity": "sha512-4A7WEG3iIyKwfpxL5bkuSlHoHHGRTHl0212Z3uvpwJPyVfZJlkZAQNNgVC+oogrJgksDnfKyuuMbG6HafZPW8Q==",
+			"version": "9.1.5",
+			"resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-9.1.5.tgz",
+			"integrity": "sha512-GStyWMz7Qbo/Gtw1xVspzVSX8eipgNg4lpsO3CAeY4/A1mzok+RV6MCv3fg62trWijh/lYEj6vps4o8JcBBpDA==",
 			"dev": true,
 			"funding": [
 				{
@@ -6206,9 +6206,9 @@
 			],
 			"dependencies": {
 				"@csstools/cascade-layer-name-parser": "^1.0.2",
-				"@csstools/css-parser-algorithms": "^2.1.1",
+				"@csstools/css-parser-algorithms": "^2.2.0",
 				"@csstools/css-tokenizer": "^2.1.1",
-				"@csstools/media-query-list-parser": "^2.1.0"
+				"@csstools/media-query-list-parser": "^2.1.1"
 			},
 			"engines": {
 				"node": "^14 || ^16 || >=18"
@@ -6967,9 +6967,9 @@
 			}
 		},
 		"node_modules/postcss-preset-env": {
-			"version": "8.5.0",
-			"resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-8.5.0.tgz",
-			"integrity": "sha512-aqAbT5dXqYX5ZvicGKQpaW/eDEZFRfnhV6Hn1Jn2bCKEB9L2MgsTdnIsXsZyFUQflIV2wIs9HTEQgkH5duMCNg==",
+			"version": "8.5.1",
+			"resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-8.5.1.tgz",
+			"integrity": "sha512-qhWnJJjP6ArLUINWJ38t6Aftxnv9NW6cXK0NuwcLCcRilbuw72dSFLkCVUJeCfHGgJiKzX+pnhkGiki0PEynWg==",
 			"dev": true,
 			"funding": [
 				{
@@ -6993,8 +6993,8 @@
 				"@csstools/postcss-logical-float-and-clear": "^1.0.1",
 				"@csstools/postcss-logical-resize": "^1.0.1",
 				"@csstools/postcss-logical-viewport-units": "^1.0.3",
-				"@csstools/postcss-media-minmax": "^1.0.3",
-				"@csstools/postcss-media-queries-aspect-ratio-number-values": "^1.0.3",
+				"@csstools/postcss-media-minmax": "^1.0.4",
+				"@csstools/postcss-media-queries-aspect-ratio-number-values": "^1.0.4",
 				"@csstools/postcss-nested-calc": "^2.0.2",
 				"@csstools/postcss-normalize-display-values": "^2.0.1",
 				"@csstools/postcss-oklab-function": "^2.2.3",
@@ -7006,7 +7006,7 @@
 				"@csstools/postcss-trigonometric-functions": "^2.1.1",
 				"@csstools/postcss-unset-value": "^2.0.1",
 				"autoprefixer": "^10.4.14",
-				"browserslist": "^4.21.5",
+				"browserslist": "^4.21.9",
 				"css-blank-pseudo": "^5.0.2",
 				"css-has-pseudo": "^5.0.2",
 				"css-prefers-color-scheme": "^8.0.2",
@@ -7016,7 +7016,7 @@
 				"postcss-color-functional-notation": "^5.1.0",
 				"postcss-color-hex-alpha": "^9.0.2",
 				"postcss-color-rebeccapurple": "^8.0.2",
-				"postcss-custom-media": "^9.1.4",
+				"postcss-custom-media": "^9.1.5",
 				"postcss-custom-properties": "^13.2.0",
 				"postcss-custom-selectors": "^7.1.3",
 				"postcss-dir-pseudo-class": "^7.0.2",
@@ -9147,9 +9147,9 @@
 			}
 		},
 		"node_modules/vitepress": {
-			"version": "1.0.0-beta.2",
-			"resolved": "https://registry.npmjs.org/vitepress/-/vitepress-1.0.0-beta.2.tgz",
-			"integrity": "sha512-DBXYjtYbm3W1IPPJ2TiCaK/XK+o/2XmL2+jslOGKm+txcbmG0kbeB+vadC5tCUZA9NdA+9Ywj3M4548c7t/SDg==",
+			"version": "1.0.0-beta.3",
+			"resolved": "https://registry.npmjs.org/vitepress/-/vitepress-1.0.0-beta.3.tgz",
+			"integrity": "sha512-GR5Pvr/o343NN1M4Na1shhDYZRrQbjmLq7WE0lla0H8iDPAsHE8agTHLWfu3FWx+3q2KA29sv16+0O9RQKGjlA==",
 			"dev": true,
 			"dependencies": {
 				"@docsearch/css": "^3.5.0",

+ 5 - 5
package.json

@@ -27,10 +27,10 @@
 	},
 	"devDependencies": {
 		"@prettier/plugin-php": "^0.19.6",
-		"@typescript-eslint/eslint-plugin": "^5.59.11",
-		"@typescript-eslint/parser": "^5.59.11",
+		"@typescript-eslint/eslint-plugin": "^5.60.0",
+		"@typescript-eslint/parser": "^5.60.0",
 		"cssnano": "^6.0.1",
-		"esbuild": "^0.18.4",
+		"esbuild": "^0.18.6",
 		"eslint": "^8.43.0",
 		"eslint-config-prettier": "^8.8.0",
 		"eslint-plugin-editorconfig": "^4.0.3",
@@ -41,7 +41,7 @@
 		"postcss": "^8.4.24",
 		"postcss-import": "^15.1.0",
 		"postcss-path-replace": "^1.0.4",
-		"postcss-preset-env": "^8.5.0",
+		"postcss-preset-env": "^8.5.1",
 		"postcss-size": "^4.0.1",
 		"prettier": "^2.8.8",
 		"prettier-plugin-nginx": "^1.0.3",
@@ -50,7 +50,7 @@
 		"stylelint": "^15.8.0",
 		"stylelint-config-standard": "^33.0.0",
 		"typescript": "^5.1.3",
-		"vitepress": "1.0.0-beta.2",
+		"vitepress": "1.0.0-beta.3",
 		"vue": "^3.3.4"
 	},
 	"browserslist": [

+ 4 - 4
web/add/db/index.php

@@ -200,13 +200,13 @@ if (!empty($_POST["ok"])) {
 				htmlentities($user_plain) . "_" . htmlentities($_POST["v_database"]),
 				htmlentities($user_plain) . "_" . htmlentities($_POST["v_database"]),
 			),
-			"</b></a>",
-			'<a href="/edit/db/?database=' .
+			"</a>",
+			'<a class="u-text-bold" href="/edit/db/?database=' .
 				htmlentities($user_plain) .
 				"_" .
 				htmlentities($_POST["v_database"]) .
-				'"><b>',
-			'<a href="' . $db_admin_link . '" target="_blank"><b>',
+				'">',
+			'<a class="u-text-bold" href="' . $db_admin_link . '" target="_blank">',
 		);
 		unset($v_database);
 		unset($v_dbuser);

+ 6 - 4
web/add/dns/index.php

@@ -199,8 +199,10 @@ if (!empty($_POST["ok"])) {
 				_("DNS zone {%s} has been created successfully."),
 				htmlentities($_POST["v_domain"]),
 			),
-			"</b></a>",
-			'<a href="/edit/dns/?domain=' . htmlentities($_POST["v_domain"]) . '"><b>',
+			"</a>",
+			'<a class="u-text-bold" href="/edit/dns/?domain=' .
+				htmlentities($_POST["v_domain"]) .
+				'">',
 		);
 
 		unset($v_domain);
@@ -281,8 +283,8 @@ if (!empty($_POST["ok_rec"])) {
 				htmlentities($_POST["v_rec"]),
 				htmlentities($_POST["v_domain"]),
 			),
-			"</b>",
-			"<b>",
+			"</span>",
+			"<span class='u-text-bold'>",
 		);
 		unset($v_domain);
 		unset($v_rec);

+ 2 - 2
web/add/ip/index.php

@@ -93,8 +93,8 @@ if (!empty($_POST["ok"])) {
 				_("IP address {%s} has been created successfully."),
 				htmlentities($_POST["v_ip"]),
 			),
-			"</b></a>",
-			'<a href="/edit/ip/?ip=' . htmlentities($_POST["v_ip"]) . '"><b>',
+			"</a>",
+			'<a class="u-text-bold" href="/edit/ip/?ip=' . htmlentities($_POST["v_ip"]) . '">',
 		);
 		unset($v_ip);
 		unset($v_netmask);

+ 7 - 5
web/add/mail/index.php

@@ -189,8 +189,10 @@ if (!empty($_POST["ok"])) {
 				_("Mail domain {%s} has been created successfully."),
 				htmlentities($_POST["v_domain"]),
 			),
-			"</b></a>",
-			'<a href="/list/mail/?domain=' . htmlentities($_POST["v_domain"]) . '"><b>',
+			"</a>",
+			'<a class="u-text-bold" href="/list/mail/?domain=' .
+				htmlentities($_POST["v_domain"]) .
+				'">',
 		);
 		unset($v_domain, $v_webmail);
 	}
@@ -509,12 +511,12 @@ if (!empty($_POST["ok_acc"])) {
 				htmlentities(strtolower($_POST["v_account"])),
 				htmlentities($_POST["v_domain"]),
 			),
-			"</b></a>",
-			'<a href="/edit/mail/?account=' .
+			"</a>",
+			'<a class="u-text-bold" href="/edit/mail/?account=' .
 				htmlentities(strtolower($_POST["v_account"])) .
 				"&domain=" .
 				htmlentities($_POST["v_domain"]) .
-				'"><b>',
+				'">',
 		);
 		unset($v_account);
 		unset($v_password);

+ 4 - 2
web/add/package/index.php

@@ -202,8 +202,10 @@ if (!empty($_POST["ok"])) {
 				_("Package {%s} has been created successfully."),
 				htmlentities($_POST["v_package"]),
 			),
-			"</b></a>",
-			'<a href="/edit/package/?package=' . htmlentities($_POST["v_package"]) . '"><b>',
+			"</a>",
+			'<a class="u-text-bold" href="/edit/package/?package=' .
+				htmlentities($_POST["v_package"]) .
+				'">',
 		);
 		unset($v_package);
 	}

+ 6 - 4
web/add/user/index.php

@@ -228,13 +228,15 @@ if (!empty($_POST["ok"])) {
 				htmlentities($_POST["v_username"]),
 				htmlentities($_POST["v_username"]),
 			),
-			"</b></a>",
-			'<a href="/edit/user/?user=' . htmlentities($_POST["v_username"]) . '"><b>',
-			'<a href="/login/?loginas=' .
+			"</a>",
+			'<a class="u-text-bold" href="/edit/user/?user=' .
+				htmlentities($_POST["v_username"]) .
+				'">',
+			'<a class="u-text-bold" href="/login/?loginas=' .
 				htmlentities($_POST["v_username"]) .
 				"&token=" .
 				htmlentities($_SESSION["token"]) .
-				'"><b>',
+				'">',
 		);
 		unset($v_username);
 		unset($v_password);

+ 2 - 2
web/add/web/index.php

@@ -126,8 +126,8 @@ if (!empty($_POST["ok"])) {
 	if (empty($_SESSION["error_msg"])) {
 		$_SESSION["ok_msg"] = htmlify_trans(
 			sprintf(_("Domain {%s} has been created successfully."), htmlentities($v_domain)),
-			"</b></a>",
-			'<a href="/edit/web/?domain=' . htmlentities($v_domain) . '"><b>',
+			"</a>",
+			'<a class="u-text-bold" href="/edit/web/?domain=' . htmlentities($v_domain) . '">',
 		);
 		unset($v_domain);
 		unset($v_aliases);

+ 14 - 13
web/css/src/themes/dark.css

@@ -27,11 +27,6 @@
 	--chart-grid-color: #434343;
 }
 
-b,
-strong {
-	color: #cacaca;
-}
-
 /* Top bar
    ========================================================================== */
 
@@ -45,21 +40,23 @@ strong {
 	color: #909090;
 }
 
-.top-bar-notifications-list {
+.top-bar-notifications-panel {
 	background-color: rgb(50 50 50 / 99%);
 	border: 1px solid #404040;
 }
 
-.top-bar-notification-item {
-	text-shadow: 0 1px rgb(0 0 0 / 50%);
+.top-bar-notifications-empty {
 	color: #dadada;
-	border-bottom: 1px solid #282828;
 
-	&.empty {
-		& .fas {
-			color: #dadada;
-		}
+	& .fas {
+		color: #dadada;
 	}
+}
+
+.top-bar-notification-item {
+	text-shadow: 0 1px rgb(0 0 0 / 50%);
+	color: #dadada;
+	border-bottom-color: #282828;
 
 	&.unseen .top-bar-notification-title {
 		color: #fff;
@@ -72,6 +69,10 @@ strong {
 	}
 }
 
+.top-bar-notifications-delete-all {
+	border-top-color: #282828;
+}
+
 .top-bar-menu-list {
 	background-color: #454545;
 }

+ 28 - 15
web/css/src/themes/default.css

@@ -139,7 +139,7 @@
 	position: relative;
 }
 
-.top-bar-notifications-list {
+.top-bar-notifications-panel {
 	background-color: #fff;
 	box-shadow: 0 3px 20px 0 rgb(0 0 0 / 40%);
 	max-height: 500px;
@@ -163,6 +163,18 @@
 	}
 }
 
+.top-bar-notifications-empty {
+	text-align: center;
+	padding: 20px;
+	font-size: 1rem;
+	color: #6f6f6f;
+
+	& .fas {
+		font-size: 3rem;
+		margin-bottom: 20px;
+	}
+}
+
 .top-bar-notification-item {
 	color: #6f6f6f;
 	font-size: 0.8rem;
@@ -173,18 +185,6 @@
 		border-bottom: none;
 	}
 
-	&.empty {
-		text-align: center;
-		font-size: 1.2rem;
-		font-weight: normal;
-		padding: 4rem;
-
-		& .fas {
-			font-size: 4rem;
-			margin-bottom: 20px;
-		}
-	}
-
 	&.unseen {
 		& .top-bar-notification-title {
 			color: #c36;
@@ -224,6 +224,18 @@
 	}
 }
 
+.top-bar-notification-content {
+	word-break: break-word;
+
+	& p {
+		margin-bottom: 15px;
+
+		&:last-child {
+			margin-bottom: 0;
+		}
+	}
+}
+
 .top-bar-notification-delete {
 	display: none;
 	background-color: transparent;
@@ -242,11 +254,12 @@
 	}
 }
 
-.top-bar-notification-delete-all {
+.top-bar-notifications-delete-all {
 	display: block;
+	border: 0;
+	border-top: 1px solid #e9e4e4;
 	background-color: transparent;
 	text-align: center;
-	border: 0;
 	padding: 10px 15px;
 	width: 100%;
 	background: none;

+ 2 - 7
web/css/src/themes/flat.css

@@ -12,11 +12,6 @@
 	--alert-text-shadow: none;
 }
 
-b,
-strong {
-	font-weight: 600;
-}
-
 /* Top bar
    ========================================================================== */
 
@@ -29,9 +24,9 @@ strong {
 	text-shadow: none;
 }
 
-.top-bar-notifications-list {
+.top-bar-notifications-panel {
 	box-shadow: none;
-	border: 1px solid #ccc;
+	border-color: #ccc;
 }
 
 .top-bar-menu-link {

+ 2 - 7
web/css/src/themes/vestia.css

@@ -17,11 +17,6 @@
 	--alert-text-shadow: none;
 }
 
-b,
-strong {
-	font-weight: 600;
-}
-
 /* Top bar
    ========================================================================== */
 
@@ -30,8 +25,8 @@ strong {
 	background: #5d5d5d;
 }
 
-.top-bar-notifications-list {
-	border: 1px solid #ccc;
+.top-bar-notifications-panel {
+	border-color: #ccc;
 	border-bottom-left-radius: 2px;
 	border-bottom-right-radius: 2px;
 	box-shadow: 0 2px 10px 0 rgb(0 0 0 / 25%);

+ 1 - 1
web/schedule/backup/index.php

@@ -19,7 +19,7 @@ if ($return_var == 0) {
 
 	if ($return_var == 4) {
 		$_SESSION["error_msg"] = _(
-			"An existing backup is already running. Please wait for that backup to finish.",
+			"An existing backup task is already running, please wait for it to complete.",
 		);
 	}
 }

+ 3 - 3
web/templates/footer.php

@@ -6,10 +6,10 @@
 ) {
 ?>
 	<p x-data="{ open: true }" x-cloak x-show="open" class="updates-banner">
-		<strong>New updates are available!</strong> To upgrade your server now, run
+		<span class="u-text-bold">New updates are available!</span> To upgrade your server now, run
 		<code>apt update && apt upgrade</code> from a shell session.
-		(<button type="button" x-on:click="open = false">
-			<strong>hide</strong>
+		(<button type="button" class="u-text-bold" x-on:click="open = false">
+			hide
 		</button>)
 	</p>
 <?php } ?>

+ 47 - 39
web/templates/includes/panel.php

@@ -74,57 +74,65 @@
 							></i>
 							<span class="u-hidden"><?= _("Notifications") ?></span>
 						</button>
-						<ul
+						<div
 							x-cloak
 							x-show="open"
 							x-on:click.outside="open = false"
-							class="top-bar-notifications-list"
+							class="top-bar-notifications-panel"
 						>
+							<template x-if="!initialized">
+								<div class="top-bar-notifications-empty">
+									<i class="fas fa-circle-notch fa-spin icon-dim"></i>
+									<p><?= _("Loading...") ?></p>
+								</div>
+							</template>
 							<template x-if="initialized && notifications.length == 0">
-								<li class="top-bar-notification-item empty">
+								<div class="top-bar-notifications-empty">
 									<i class="fas fa-bell-slash icon-dim"></i>
 									<p><?= _("No notifications") ?></p>
-								</li>
+								</div>
 							</template>
-							<template x-for="notification in notifications" :key="notification.ID">
-								<li
-									x-bind:id="`notification-${notification.ID}`"
-									x-bind:class="notification.ACK && 'unseen'"
-									class="top-bar-notification-item"
-								>
-									<div class="top-bar-notification-header">
-										<p x-text="notification.TOPIC" class="top-bar-notification-title"></p>
-										<button
-											x-on:click="remove(notification.ID)"
-											type="button"
-											class="top-bar-notification-delete"
-											title="<?= _("Delete notification") ?>"
+							<template x-if="initialized && notifications.length > 0">
+								<ul>
+									<template x-for="notification in notifications" :key="notification.ID">
+										<li
+											x-bind:id="`notification-${notification.ID}`"
+											x-bind:class="notification.ACK && 'unseen'"
+											class="top-bar-notification-item"
 										>
-											<i class="fas fa-xmark"></i>
-										</button>
-									</div>
-									<div x-html="notification.NOTICE"></div>
-									<p class="top-bar-notification-timestamp">
-										<time
-											:datetime="`${notification.DATE}T${notification.TIME}`"
-											x-text="`${notification.TIME} ${notification.DATE}`"
-										></time>
-									</p>
-								</li>
+											<div class="top-bar-notification-header">
+												<p x-text="notification.TOPIC" class="top-bar-notification-title"></p>
+												<button
+													x-on:click="remove(notification.ID)"
+													type="button"
+													class="top-bar-notification-delete"
+													title="<?= _("Delete notification") ?>"
+												>
+													<i class="fas fa-xmark"></i>
+												</button>
+											</div>
+											<div class="top-bar-notification-content" x-html="notification.NOTICE"></div>
+											<p class="top-bar-notification-timestamp">
+												<time
+													:datetime="`${notification.DATE}T${notification.TIME}`"
+													x-text="`${notification.TIME} ${notification.DATE}`"
+												></time>
+											</p>
+										</li>
+									</template>
+								</ul>
 							</template>
 							<template x-if="initialized && notifications.length > 2">
-								<li>
-									<button
-										x-on:click="removeAll()"
-										type="button"
-										class="top-bar-notification-delete-all"
-									>
-										<i class="fas fa-check"></i>
-										<?= _("Delete all notifications") ?>
-									</button>
-								</li>
+								<button
+									x-on:click="removeAll()"
+									type="button"
+									class="top-bar-notifications-delete-all"
+								>
+									<i class="fas fa-check"></i>
+									<?= _("Delete all notifications") ?>
+								</button>
 							</template>
-						</ul>
+						</div>
 					</div>
 				<?php } ?>