-
-
Notifications
You must be signed in to change notification settings - Fork 71
Remove all alert() in script.js and create a custom and nice user-friendly alert by css/js (#631) #727
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Remove all alert() in script.js and create a custom and nice user-friendly alert by css/js (#631) #727
Changes from all commits
dcbc584
fbfd745
99e4756
e53a219
9da9f63
a89b77c
4069961
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,153 @@ | ||
| <div | ||
| id="alert-container" class="fixed top-4 right-4 z-50 space-y-2"> | ||
| </div> | ||
| <script> | ||
| const AlertSystem = { | ||
| container: null, | ||
| init() { | ||
| this.container = document.getElementById('alert-container'); | ||
| if (!this.container) { | ||
| this.container = document.createElement('div'); | ||
| this.container.id = 'alert-container'; | ||
| this.container.className = 'fixed top-4 right-4 z-50 space-y-2'; | ||
| document.body.appendChild(this.container); | ||
| } | ||
| }, | ||
| show(options = {}) { | ||
| if (!this.container) { | ||
| this.init(); | ||
| } | ||
| const { | ||
| message = '', | ||
| type = 'info', | ||
| duration = 5000, | ||
| dismissible = true | ||
| } = options; | ||
| const typeConfig = { | ||
| success: { | ||
| bg: 'bg-green-700', | ||
| border: 'border-green-800', | ||
| text: 'text-white', | ||
| }, | ||
| error: { | ||
| bg: 'bg-red-500', | ||
| border: 'border-red-700', | ||
| text: 'text-white', | ||
| }, | ||
| warning: { | ||
| bg: 'bg-orange-100', | ||
| border: 'border-orange-500', | ||
| text: 'text-orange-700' | ||
| }, | ||
| info: { | ||
| bg: 'bg-indigo-900', | ||
| border: 'border-indigo-800', | ||
| text: 'text-white', | ||
| } | ||
| }; | ||
| const config = typeConfig[type] || typeConfig.info; | ||
| const alertId = 'alert-' + crypto.randomUUID(); | ||
| const alertHTML = ` | ||
| <div id="${alertId}" class="w-lg w-full ${config.bg} border ${config.border} ${config.text} px-4 py-3 rounded relative shadow-lg transform transition-all duration-300 ease-in-out translate-x-full opacity-0" role="alert"> | ||
| <span class="block pr-2">${message}</span> | ||
| ${dismissible ? ` | ||
| <button class="absolute top-2 right-2 cursor-pointer alert-dismiss-button"> | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For better accessibility and to follow best practices, the dismiss button should have an |
||
| <svg class="fill-current h-6 w-6 ${config.text}" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><title>Close</title><path d="M18.3 5.71a1 1 0 0 0-1.41 0L12 10.59 7.11 5.7A1 1 0 0 0 5.7 7.11L10.59 12l-4.89 4.89a1 1 0 1 0 1.41 1.41L12 13.41l4.89 4.89a1 1 0 0 0 1.41-1.41L13.41 12l4.89-4.89a1 1 0 0 0 0-1.4z"/></svg> | ||
| </button> | ||
| ` : ''} | ||
| </div> | ||
| `; | ||
| this.container.insertAdjacentHTML('beforeend', alertHTML); | ||
| const alertElement = document.getElementById(alertId); | ||
| const dismissButton = alertElement.querySelector('.alert-dismiss-button'); | ||
| if (dismissButton) { | ||
| dismissButton.addEventListener('click', () => this.dismiss(alertId)); | ||
| } | ||
| setTimeout(() => { | ||
| alertElement.classList.remove('translate-x-full', 'opacity-0'); | ||
| alertElement.classList.add('translate-x-0', 'opacity-100'); | ||
| }, 10); | ||
| if (duration > 0) { | ||
| setTimeout(() => { | ||
| this.dismiss(alertId); | ||
| }, duration); | ||
| } | ||
| return alertId; | ||
| }, | ||
| showSuccess(message, options = {}) { | ||
| return this.show({ | ||
| message: formatMessage(message), | ||
| type: 'success', | ||
| ...options | ||
| }); | ||
| }, | ||
| showError(message, options = {}) { | ||
| return this.show({ | ||
| message: formatMessage(message), | ||
| type: 'error', | ||
| ...options | ||
| }); | ||
| }, | ||
| showWarning(message, options = {}) { | ||
| return this.show({ | ||
| message: formatMessage(message), | ||
| type: 'warning', | ||
| ...options | ||
| }); | ||
| }, | ||
| showInfo(message, options = {}) { | ||
| return this.show({ | ||
| message: formatMessage(message), | ||
| type: 'info', | ||
| ...options | ||
| }); | ||
| }, | ||
| dismiss(alertId) { | ||
| const alertElement = document.getElementById(alertId); | ||
| if (alertElement) { | ||
| alertElement.classList.remove('translate-x-0', 'opacity-100'); | ||
| alertElement.classList.add('translate-x-full', 'opacity-0'); | ||
| setTimeout(() => { | ||
| if (alertElement.parentNode) { | ||
| alertElement.parentNode.removeChild(alertElement); | ||
| } | ||
| }, 300); | ||
| } | ||
| }, | ||
| dismissAll() { | ||
| const alerts = this.container.querySelectorAll('[id^="alert-"]'); | ||
| alerts.forEach(alert => { | ||
| const alertId = alert.id; | ||
| this.dismiss(alertId); | ||
| }); | ||
| } | ||
| }; | ||
| window.AlertSystem = AlertSystem; | ||
| function formatMessage(message) { | ||
| if (Array.isArray(message)) { | ||
| if (message.length === 1) { | ||
| return `Please fix the error: ${escapeHtml(message[0])}`; | ||
| } else { | ||
| return `Please fix the following:<br>- ${message.map(escapeHtml).join('<br>- ')}`; | ||
| } | ||
| } | ||
| // Escape HTML to prevent XSS | ||
| return escapeHtml(message); | ||
| } | ||
| function escapeHtml(text) { | ||
| const temp = document.createElement('div'); | ||
| temp.textContent = text; | ||
| return temp.innerHTML; | ||
| } | ||
| </script> | ||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -175,12 +175,27 @@ function applyStoredRandomTheme(forceNew = false) { | |||||
| } | ||||||
|
|
||||||
| /** | ||||||
| * Displays an alert message with a list of validation errors. | ||||||
| * @param {string[]} errors - The list of error messages. | ||||||
| * Displays a warning alert message. | ||||||
| * @param {string|string[]} message - The list of error messages. | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The JSDoc for the
Suggested change
|
||||||
| */ | ||||||
| function showAlert(errors) { | ||||||
| if (errors.length === 1) alert(`Please fix the error: ${errors.join(' ')}`); | ||||||
| else alert(`Please fix the following:\n- ${errors.join('\n- ')}`); | ||||||
| function showWarning(message) { | ||||||
| window.AlertSystem.showWarning(message); | ||||||
| } | ||||||
|
|
||||||
| /** | ||||||
| * Displays a success alert message. | ||||||
| * @param {string|string[]} message - The success message(s). | ||||||
| */ | ||||||
| function showSuccess(message) { | ||||||
| window.AlertSystem.showSuccess(message); | ||||||
| } | ||||||
|
|
||||||
| /** | ||||||
| * Displays an error alert message. | ||||||
| * @param {string|string[]} message - The error message(s). | ||||||
| */ | ||||||
| function showError(message) { | ||||||
| window.AlertSystem.showError(message); | ||||||
| } | ||||||
|
|
||||||
| /** | ||||||
|
|
@@ -253,7 +268,7 @@ async function handleFormSubmit(form, endpoint, validateFn) { | |||||
|
|
||||||
| const errors = validateFn(data); | ||||||
| if (errors.length > 0) { | ||||||
| showAlert(errors); | ||||||
| showWarning(errors); | ||||||
| return; | ||||||
| } | ||||||
|
|
||||||
|
|
@@ -265,21 +280,21 @@ async function handleFormSubmit(form, endpoint, validateFn) { | |||||
| }); | ||||||
|
|
||||||
| if (response?.ok && response?.status === 200) { | ||||||
| alert('✅ Submission successful!'); | ||||||
| showSuccess('Submission successful!'); | ||||||
| form.reset(); | ||||||
| } else { | ||||||
| const defaultErrorMessage = 'Something went wrong.'; | ||||||
| try { | ||||||
| const result = await response.json(); | ||||||
| const errorMessage = result.message || defaultErrorMessage; | ||||||
| alert(`❌ Error: ${errorMessage}`); | ||||||
| showError(errorMessage); | ||||||
| } catch (e) { | ||||||
| console.error('Error parsing JSON response:', e); | ||||||
| alert(`❌ Error: ${defaultErrorMessage}`); | ||||||
| showError(defaultErrorMessage); | ||||||
| } | ||||||
| } | ||||||
| } catch (err) { | ||||||
| alert('❌ Network error. Please try again.'); | ||||||
| showError('Network error. Please try again.'); | ||||||
| console.error(err); | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The CSS class
w-lgis not a standard Tailwind CSS class and will have no effect. It seems you intended to set a maximum width for the alert. I suggest changing it tomax-w-lgto constrain the alert's width on larger screens, which improves readability.