-
-
Notifications
You must be signed in to change notification settings - Fork 91
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
Creation modal quality of life changes #467
Creation modal quality of life changes #467
Conversation
Caution Review failedThe pull request is closed. WalkthroughThis pull request introduces several enhancements to frontend components, focusing on modal interactions, form management, and keyboard shortcut handling. The changes primarily involve updating component props, modifying CSS classes, and refining modal focus and interaction behaviors. Key modifications include adding a Changes
Assessment against linked issues
Possibly related PRs
Suggested Reviewers
Security Recommendations
📜 Recent review detailsConfiguration used: CodeRabbit UI 📒 Files selected for processing (1)
Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
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.
Caution
Inline review comments failed to post. This is likely due to GitHub's limits when posting large numbers of comments.
Actionable comments posted: 3
🧹 Nitpick comments (2)
frontend/components/Base/Modal.vue (1)
58-62
: Add rate limiting to click outside handlerThe click outside handler could be triggered rapidly, potentially causing race conditions.
Consider debouncing the handler:
if (props.clickOutsideToClose) { + const debouncedClose = useDebounceFn(() => { + close(); + }, 300); onClickOutside(modalBox, () => { - close(); + debouncedClose(); }); }frontend/layouts/default.vue (1)
203-208
: Sanitize shortcut keys before useThe current implementation directly uses shortcut keys without sanitization. While the risk is low since shortcuts are defined internally, it's good practice to sanitize all user inputs.
Apply this diff to add sanitization:
if (option?.shortcut) { - const shortcutKeycode = option.shortcut.replace(/([0-9])/, "digit$&"); + // Sanitize shortcut key to only allow specific characters + const sanitizedShortcut = option.shortcut.replace(/[^0-9!@#]/, ""); + if (sanitizedShortcut !== option.shortcut) { + console.error('Invalid shortcut key'); + return; + } + const shortcutKeycode = sanitizedShortcut.replace(/([0-9])/, "digit$&"); whenever(keys[shortcutKeycode], () => { if (activeElement.value?.tagName !== "INPUT") { option.action(); } }); }
🛑 Comments failed to post (3)
frontend/components/Base/Modal.vue (1)
44-47: 💡 Codebase verification
⚠️ Potential issue🔒 Add event listener cleanup to prevent security vulnerabilities
The click outside handler needs proper cleanup to prevent:
- Memory leaks affecting application performance
- Potential information disclosure if modal content remains accessible
- Resource exhaustion from accumulated event listeners
+ const stopClickOutside = ref(); if (props.clickOutsideToClose) { - onClickOutside(modalBox, () => { + stopClickOutside.value = onClickOutside(modalBox, () => { close(); }); } + onUnmounted(() => { + if (stopClickOutside.value) { + stopClickOutside.value(); + } + });🔗 Analysis chain
Ensure proper cleanup of click outside handler
The click outside handler is added conditionally but there's no cleanup when the component is unmounted.
Add cleanup to prevent memory leaks and potential security issues:
+ const stopClickOutside = ref(); if (props.clickOutsideToClose) { - onClickOutside(modalBox, () => { + stopClickOutside.value = onClickOutside(modalBox, () => { close(); }); } + onUnmounted(() => { + if (stopClickOutside.value) { + stopClickOutside.value(); + } + });🏁 Scripts executed
The following scripts were executed for the analysis:
Error: No shell scripts found
Script:
#!/bin/bash # Find onClickOutside implementation and usage ast-grep --pattern 'const onClickOutside = $_' # Search for similar modal cleanup patterns rg "onClickOutside.*modal" -A 5 -B 5 # Find unmounted cleanup patterns ast-grep --pattern 'onUnmounted(() => { $$$ })'Length of output: 4153
Script:
#!/bin/bash # Search for onClickOutside implementation in composables fd "use.*\.ts$" --exec cat {} \; # Look for vueuse imports as it might be from there rg "@vueuse/.*onClickOutside" -A 2 -B 2Length of output: 26563
frontend/components/Location/CreateModal.vue (1)
98-103:
⚠️ Potential issueValidate route parameters to prevent path traversal
The location ID from route parameters needs validation to prevent potential security issues.
Add validation:
const locationId = computed(() => { if (route.fullPath.includes("/location/")) { - return route.params.id; + const id = route.params.id; + // Ensure id is a valid UUID or your expected format + const validIdPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i; + return validIdPattern.test(id) ? id : null; } return null; });📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.const locationId = computed(() => { if (route.fullPath.includes("/location/")) { const id = route.params.id; // Ensure id is a valid UUID or your expected format const validIdPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i; return validIdPattern.test(id) ? id : null; } return null; });
frontend/components/Item/CreateModal.vue (1)
144-163:
⚠️ Potential issueValidate route parameters before use
The current implementation directly uses route parameters without validation. This could potentially lead to XSS attacks if the route parameters contain malicious content.
Apply this diff to add validation:
if (locationId.value) { + // Validate locationId is a valid UUID + if (!/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(locationId.value)) { + console.error('Invalid location ID format'); + return; + } const found = locations.value.find(l => l.id === locationId.value); if (found) { form.location = found; } } if (labelId.value) { + // Validate labelId is a valid UUID + if (!/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(labelId.value)) { + console.error('Invalid label ID format'); + return; + } form.labels = labels.value.filter(l => l.id === labelId.value); }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.watch( () => modal.value, (open) => { if (open) { useTimeoutFn(() => { focused.value = true }, 50); if (locationId.value) { // Validate locationId is a valid UUID if (!/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(locationId.value)) { console.error('Invalid location ID format'); return; } const found = locations.value.find(l => l.id === locationId.value); if (found) { form.location = found; } } if (labelId.value) { // Validate labelId is a valid UUID if (!/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(labelId.value)) { console.error('Invalid label ID format'); return; } form.labels = labels.value.filter(l => l.id === labelId.value); } } else { focused.value = false; } }
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.
Actionable comments posted: 0
🧹 Nitpick comments (1)
frontend/components/global/QuickMenu/Input.vue (1)
Line range hint
1-116
: Consider additional security measures for the quick menu component.While reviewing the component, I noticed some potential security considerations:
- Input validation: Consider sanitizing user input before using it in the filtering logic to prevent potential XSS attacks through action text.
- Action execution safeguards: The quick select feature (single-character shortcuts) could benefit from an additional confirmation step for destructive actions.
- Rate limiting: Consider adding debounce to prevent rapid-fire action execution.
Here's a suggested implementation for input sanitization:
const filteredActions = computed(() => { - const searchTerm = inputValue.value.toLowerCase(); + const searchTerm = sanitizeInput(inputValue.value.toLowerCase()); return (props.actions || []).filter(action => { return action.text.toLowerCase().includes(searchTerm) || action.shortcut?.includes(searchTerm); }); }); + function sanitizeInput(input: string): string { + // Remove potentially dangerous characters while preserving legitimate search terms + return input.replace(/[<>'"]/g, ''); + }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
frontend/components/Item/CreateModal.vue
(2 hunks)frontend/components/global/QuickMenu/Input.vue
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- frontend/components/Item/CreateModal.vue
⏰ Context from checks skipped due to timeout of 90000ms (7)
- GitHub Check: build (linux/arm/v7)
- GitHub Check: build (linux/arm/v7)
- GitHub Check: Frontend and End-to-End Tests / Integration Tests
- GitHub Check: build (linux/arm64)
- GitHub Check: build (linux/arm64)
- GitHub Check: build (linux/amd64)
- GitHub Check: build (linux/amd64)
🔇 Additional comments (1)
frontend/components/global/QuickMenu/Input.vue (1)
84-85
: LGTM! The focus handling change improves security.The simplified focus handling logic that only clears input on blur is better than automatically revealing actions, as it reduces the risk of unintended action execution.
026fbae
to
224b669
Compare
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.
Caution
Inline review comments failed to post. This is likely due to GitHub's limits when posting large numbers of comments.
Actionable comments posted: 2
🔭 Outside diff range comments (1)
frontend/components/Location/CreateModal.vue (1)
Line range hint
108-143
: Enhance security and error handling in create function.The current implementation has several security and reliability concerns:
- No rate limiting on form submission
- Basic error handling
- Unconstrained navigation
Consider implementing:
- Rate limiting for form submissions
- More detailed error handling
- Navigation validation
+ const SUBMIT_COOLDOWN = 1000; // 1 second + let lastSubmitTime = 0; + async function create(close = true) { if (loading.value) { toast.error("Already creating a location"); return; } + + // Rate limiting + const now = Date.now(); + if (now - lastSubmitTime < SUBMIT_COOLDOWN) { + toast.error("Please wait before submitting again"); + return; + } + lastSubmitTime = now; + loading.value = true; if (shift.value) { close = false; } const { data, error } = await api.locations.create({ name: form.name, description: form.description, parentId: form.parent ? form.parent.id : null, }); if (error) { loading.value = false; - toast.error("Couldn't create location"); + // More detailed error handling + toast.error(`Failed to create location: ${error.message || 'Unknown error'}`); + return; } if (data) { + // Validate data.id before navigation + if (!data.id || typeof data.id !== 'string') { + toast.error("Invalid response from server"); + loading.value = false; + return; + } toast.success("Location created"); } reset(); if (close) { modal.value = false; - navigateTo(`/location/${data.id}`); + // Safe navigation with validated id + if (data?.id) { + navigateTo(`/location/${encodeURIComponent(data.id)}`); + } } }
🛑 Comments failed to post (2)
frontend/components/Location/CreateModal.vue (2)
100-105:
⚠️ Potential issueEnhance route parameter validation for security.
The current implementation directly uses route parameters without proper validation, which could lead to security vulnerabilities.
Consider this safer implementation:
const locationId = computed(() => { if (route.fullPath.includes("/location/")) { - return route.params.id; + const id = route.params.id; + // Validate id format (assuming UUID format) + return typeof id === 'string' && /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(id) + ? id + : null; } return null; });📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.const locationId = computed(() => { if (route.fullPath.includes("/location/")) { const id = route.params.id; // Validate id format (assuming UUID format) return typeof id === 'string' && /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(id) ? id : null; } return null; });
62-79: 🛠️ Refactor suggestion
Improve timing and cleanup in watch handler.
The current implementation has several potential issues:
- The 50ms timeout is arbitrary and might not be reliable across different devices
- Missing cleanup for the timeout on component unmount
- Potential race condition between timeout and location finding
Consider this improved implementation:
+ const timeoutRef = ref<number | null>(null); + watch( () => modal.value, open => { if (open) { - useTimeoutFn(() => { - focused.value = true; - }, 50); + // Clear any existing timeout + if (timeoutRef.value) clearTimeout(timeoutRef.value); + // Set new timeout and store reference + timeoutRef.value = setTimeout(() => { + focused.value = true; + timeoutRef.value = null; + }, 50); if (locationId.value) { const found = locations.value.find(l => l.id === locationId.value); if (found) { form.parent = found; } } } else { focused.value = false; + // Clear timeout on modal close + if (timeoutRef.value) { + clearTimeout(timeoutRef.value); + timeoutRef.value = null; + } } } ); + + // Cleanup on component unmount + onBeforeUnmount(() => { + if (timeoutRef.value) { + clearTimeout(timeoutRef.value); + timeoutRef.value = null; + } + });📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.const timeoutRef = ref<number | null>(null); watch( () => modal.value, open => { if (open) { // Clear any existing timeout if (timeoutRef.value) clearTimeout(timeoutRef.value); // Set new timeout and store reference timeoutRef.value = setTimeout(() => { focused.value = true; timeoutRef.value = null; }, 50); if (locationId.value) { const found = locations.value.find(l => l.id === locationId.value); if (found) { form.parent = found; } } } else { focused.value = false; // Clear timeout on modal close if (timeoutRef.value) { clearTimeout(timeoutRef.value); timeoutRef.value = null; } } } ); // Cleanup on component unmount onBeforeUnmount(() => { if (timeoutRef.value) { clearTimeout(timeoutRef.value); timeoutRef.value = null; } });
Sorry for the broken Docker builds, we're working on sorting that out. However, the linting issues should be correct. |
Ok, the aforementioned problems should be fixed now. I didn't have any problems with the shortcuts though, could you check again to see if they work for you? |
I did the test and this PR do fix #483 👍 (@Blackshadow8910 FYI) |
Hovering the mouse outside the box and then using the arrow keys to select an option and then clicking outside still causes it to be opened. On small screens and mobile the dropdown still seems to be cut off. Ctrl + Shift + # doesnt work for me but ctrl + shift + 3 does as im using a uk keyboard, maybe labelling it as ctr + shift + number would be better? |
Ahhh i had the menu working but then changed a line for some reason and broke it again The shortcuts should be labelled better now, sorry, I forgot other keyboard layouts exist. They should be just shift + number though, and I'm surprised that adding control doesn't trigger any browser shortcuts. Would it be better if I changed it to ctrl+shift+1/2/3? I also had to do a dumb thing to get the modal to move to the top on small screens since this version of daisyui doesn't have modal-top and updating it seems to break the ui. Is that okay? |
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.
Caution
Inline review comments failed to post. This is likely due to GitHub's limits when posting large numbers of comments.
Actionable comments posted: 1
🧹 Nitpick comments (1)
frontend/components/Base/Modal.vue (1)
91-109
: Optimize transform properties for better maintainability.The transform properties can be simplified using Tailwind's transform utilities.
- transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) - skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); + @apply translate-y-10 scale-100;
🛑 Comments failed to post (1)
frontend/layouts/default.vue (1)
205-211:
⚠️ Potential issueValidate action execution context.
Add validation to ensure actions are only executed in appropriate contexts:
whenever(keys[shortcutKeycode], () => { - if (activeElement.value?.tagName !== "INPUT") { + if (activeElement.value?.tagName !== "INPUT" && + document.visibilityState === "visible" && + !modals.quickMenu) { option.action(); } });📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.if (option?.shortcut) { const shortcutKeycode = option.shortcut.replace(/([0-9])/, "digit$&"); whenever(keys[shortcutKeycode], () => { if (activeElement.value?.tagName !== "INPUT" && document.visibilityState === "visible" && !modals.quickMenu) { option.action(); } });
8c1236c
to
38f18b4
Compare
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.
Caution
Inline review comments failed to post. This is likely due to GitHub's limits when posting large numbers of comments.
Actionable comments posted: 1
🧹 Nitpick comments (2)
frontend/components/Base/Modal.vue (2)
4-7
: Consider XSS implications of dynamic class binding.The dynamic class binding using
props.modalTop
is secure as it's a boolean value. However, for future maintainability, consider using a computed property to handle the class logic.+ const modalPositionClass = computed(() => ({ + 'modal-bottom': !props.modalTop, + 'modal-top': props.modalTop + }))Then in template:
- :class="{ 'modal-bottom': !props.modalTop, 'modal-top': props.modalTop }" + :class="modalPositionClass"🧰 Tools
🪛 GitHub Actions: Pull Request CI
[warning] 6-6: Classname 'modal-top' is not a Tailwind CSS class!
Line range hint
1-109
: Security Recommendations:
- Consider adding CSRF protection for modal actions
- Implement rate limiting for modal open/close events
- Sanitize any dynamic content rendered in modal slots
- Add aria-labels and roles for better accessibility
🛑 Comments failed to post (1)
frontend/components/Base/Modal.vue (1)
91-109: 🛠️ Refactor suggestion
Address Tailwind class warning and potential style injection.
The pipeline reports that 'modal-top' is not a Tailwind class. Consider:
- Using Tailwind's built-in classes instead of custom CSS
- If custom CSS is needed, add the class to your Tailwind config
Additionally, the transform properties should be sanitized to prevent potential CSS injection attacks if these values become dynamic in the future.
38f18b4
to
0be0729
Compare
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.
Caution
Inline review comments failed to post. This is likely due to GitHub's limits when posting large numbers of comments.
Actionable comments posted: 2
🧹 Nitpick comments (1)
frontend/components/Base/Modal.vue (1)
Line range hint
1-110
: Add security headers for modal protectionSince this is a modal component that could potentially display sensitive information, ensure that proper security headers are set in your application:
- X-Frame-Options: to prevent clickjacking
- Content-Security-Policy: to control resource loading
Consider implementing these headers in your Nuxt.js configuration:
// nuxt.config.js export default { render: { csp: { policies: { 'frame-ancestors': ["'none'"], } } } }🧰 Tools
🪛 GitHub Actions: Pull Request CI
[warning] 48-48: '<script setup lang=ts>' should be above '<style lang=css scoped>' on line 28
🛑 Comments failed to post (2)
frontend/components/Base/Modal.vue (2)
28-28: 🛠️ Refactor suggestion
Fix linting issues
The pipeline indicates two issues:
- The
<script>
tag should be placed above the<style>
tag- Missing line break at the end of file
Apply these changes to fix the linting issues:
-<style lang="css" scoped> - /* ... style content ... */ -</style> - -<script setup lang="ts"> +<script setup lang="ts"> /* ... script content ... */ </script> + +<style lang="css" scoped> + /* ... style content ... */ +</style> +Also applies to: 110-110
86-90: 🛠️ Refactor suggestion
Add cleanup for click outside handler
The click outside handler should be cleaned up when the component is unmounted to prevent memory leaks.
+import { onUnmounted } from 'vue'; +let stopClickOutside: (() => void) | undefined; if (props.clickOutsideToClose) { - onClickOutside(modalBox, () => { + stopClickOutside = onClickOutside(modalBox, () => { close(); }); } +onUnmounted(() => { + if (stopClickOutside) { + stopClickOutside(); + } +});📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.import { onUnmounted } from 'vue'; let stopClickOutside: (() => void) | undefined; if (props.clickOutsideToClose) { stopClickOutside = onClickOutside(modalBox, () => { close(); }); } onUnmounted(() => { if (stopClickOutside) { stopClickOutside(); } });
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.
Leave it as shift+1/2/3 I think, i was just didnt realist ctrl was not necesary.
We really need to update to a new version of daisyui soon but this is fine for now I think.
Just need task ui:fix
and should be good for merge.
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.
Caution
Inline review comments failed to post. This is likely due to GitHub's limits when posting large numbers of comments.
Actionable comments posted: 1
🧹 Nitpick comments (1)
frontend/layouts/default.vue (1)
183-183
: Consider notifying users about shortcut changes.The change from Control to Shift modifiers should be communicated to users, especially for those who frequently use keyboard shortcuts.
Consider adding a one-time notification or tooltip to inform users about the shortcut changes:
+ const hasSeenShortcutNotice = ref(localStorage.getItem('shortcutNoticeShown')) + // Add to mounted hook or appropriate lifecycle event + if (!hasSeenShortcutNotice.value) { + // Show notification about shortcut changes + localStorage.setItem('shortcutNoticeShown', 'true') + }Also applies to: 191-191, 199-199
🛑 Comments failed to post (1)
frontend/layouts/default.vue (1)
207-212:
⚠️ Potential issueAdd security measures to shortcut handling.
The current implementation has potential security considerations:
- The regex replacement could be vulnerable to injection
- Lacks error handling for action execution
Consider implementing these security measures:
dropdown.forEach(option => { if (option?.shortcut) { - const shortcutKeycode = option.shortcut.replace(/([0-9])/, "digit$&"); + // Validate shortcut format + if (!/^Shift\+[1-9]$/.test(option.shortcut)) { + console.error('Invalid shortcut format:', option.shortcut); + return; + } + const shortcutKeycode = option.shortcut.replace(/([1-9])/, "digit$1"); whenever(keys[shortcutKeycode], () => { if (activeElement.value?.tagName !== "INPUT") { - option.action(); + try { + option.action(); + } catch (error) { + console.error('Error executing shortcut action:', error); + } } }); } });Committable suggestion skipped: line range outside the PR's diff.
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.
Caution
Inline review comments failed to post. This is likely due to GitHub's limits when posting large numbers of comments.
Actionable comments posted: 1
🧹 Nitpick comments (3)
frontend/components/Base/Modal.vue (3)
4-8
: Consider using computed properties for reactive template bindings.While the current implementation works, accessing props directly in the template via
props.modalTop
could lead to reactivity issues. Additionally, the class binding could be more defensive against potential runtime errors.Consider this improvement:
- <div - class="modal overflow-visible sm:modal-middle" - :class="{ 'modal-bottom': !props.modalTop }" - :modal-top="props.modalTop" - > + <div + class="modal overflow-visible sm:modal-middle" + :class="{ 'modal-bottom': !isModalTop }" + :modal-top="isModalTop" + > <script setup> + const isModalTop = computed(() => props.modalTop ?? false) </script>
48-55
: Add JSDoc documentation for new props.The new props lack documentation explaining their purpose and usage, unlike the existing
readonly
prop which has clear documentation.Add documentation for clarity:
+ /** + * When true, clicking outside the modal will close it. + * Use this for non-critical modals where data loss is not a concern. + */ clickOutsideToClose: { type: Boolean, default: false, }, + /** + * Controls the modal's vertical positioning. + * When true, positions the modal at the top of the screen on mobile devices. + */ modalTop: { type: Boolean, default: false, },
93-109
: Simplify transform properties and use design system variables.The mobile styles use hardcoded values and complex transform properties that could be simplified.
Consider these improvements:
- @media (max-width: 640px) { + @media (max-width: theme('screens.sm')) { .modal[modal-top] { align-items: start; } .modal[modal-top] :where(.modal-box) { max-width: none; - --tw-translate-y: 2.5rem /* 40px */; - --tw-scale-x: 1; - --tw-scale-y: 1; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) - skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); + transform: translateY(2.5rem); width: 100%; border-top-left-radius: 0px; border-top-right-radius: 0px; } }
🛑 Comments failed to post (1)
frontend/components/Base/Modal.vue (1)
66-70:
⚠️ Potential issueAdd cleanup for click outside handler and consider security implications.
The click outside handler setup lacks cleanup and could have security implications if the modal contains sensitive information.
Consider these security and performance improvements:
+ const clickOutsideStop = ref() - if (props.clickOutsideToClose) { - onClickOutside(modalBox, () => { - close(); - }); - } + watchEffect((onCleanup) => { + if (props.clickOutsideToClose && modal.value) { + const { stop } = onClickOutside(modalBox, () => { + // Add a small delay to prevent accidental clicks when the modal opens + setTimeout(() => { + if (modal.value) close(); + }, 100); + }); + clickOutsideStop.value = stop; + onCleanup(() => { + clickOutsideStop.value?.(); + }); + } + });Also, consider adding a security warning in the documentation:
/** * @security When using clickOutsideToClose with modals containing sensitive information, * ensure proper confirmation dialogs are in place to prevent accidental data loss */
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.
Actionable comments posted: 0
🧹 Nitpick comments (1)
frontend/layouts/default.vue (1)
209-214
: Consider implementing rate limiting for shortcutsWhile the input field protection is good, consider implementing rate limiting to prevent shortcut abuse.
dropdown.forEach(option => { if (option?.shortcut) { const shortcutKeycode = option.shortcut.replace(/([0-9])/, "digit$&"); + const rateLimiter = useThrottleFn(() => { + if (activeElement.value?.tagName !== "INPUT") { + option.action(); + } + }, 500); whenever(keys[shortcutKeycode], () => { - if (activeElement.value?.tagName !== "INPUT") { - option.action(); - } + rateLimiter(); }); } });
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
frontend/layouts/default.vue
(5 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (6)
- GitHub Check: build (linux/arm/v7)
- GitHub Check: build (linux/arm/v7)
- GitHub Check: build (linux/arm64)
- GitHub Check: build (linux/arm64)
- GitHub Check: build (linux/amd64)
- GitHub Check: build (linux/amd64)
🔇 Additional comments (4)
frontend/layouts/default.vue (4)
75-79
: LGTM! Accessible keyboard shortcut displayThe keyboard shortcut display implementation follows accessibility best practices using semantic HTML.
282-282
: LGTM! Well-scoped quick menu actionsThe quick menu actions are properly scoped and use Vue Router for navigation, maintaining security boundaries.
152-159
: LGTM! Good input field protectionThe active element tracking prevents shortcut execution while typing, which is a good security practice. Consider extending this protection to other sensitive elements like contenteditable.
✅ Verification successful
✅ Current implementation provides sufficient protection
The active element tracking already covers all editable elements in the codebase. All v-html directives are properly sanitized with DOMPurify, and there are no contenteditable elements that would require additional protection.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Check for other potentially sensitive elements that might need protection rg -A 3 "contenteditable|designMode|isContentEditable" --type vueLength of output: 95
Script:
#!/bin/bash # Find .vue files and search for editable elements fd -e vue -x rg -l "contenteditable|designMode|isContentEditable|v-html|textarea|editable" {} # Get more context around matches if found fd -e vue -x rg -A 3 "contenteditable|designMode|isContentEditable|v-html|textarea|editable" {}Length of output: 3211
37-40
: Consider security implications of version exposureThe version and build information is publicly displayed and linked to GitHub releases. While useful for users, this could help attackers identify vulnerable versions.
Consider:
- Limiting version info visibility to authenticated users
- Using a security.txt file for responsible disclosure
- Implementing a version check API that only indicates if an update is needed
✅ Verification successful
Version information exposure follows security best practices
The current implementation already follows security best practices:
- Basic version info is available on the landing page to help users stay up-to-date
- Detailed build information (commit hashes) is only visible in the authenticated layout
- GitHub release links are only exposed to authenticated users
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Check if version information is exposed to unauthenticated users rg -A 3 "version|build.commit" --type vueLength of output: 71
Script:
#!/bin/bash # Search for version and build information patterns rg -l "status\.build\.(version|commit)" # Search for authentication-related code near version display rg -B 5 -A 5 "status\.build\.(version|commit)" # Find status object initialization/assignment ast-grep --pattern 'status = $_'Length of output: 4039
Script:
#!/bin/bash # Search for status object definition and authentication checks rg -B 5 -A 5 "defineProps|status\s*=|useAuth" frontend/ # Check for authentication-related composables or utilities rg -l "auth|isAuthenticated|requireAuth" frontend/Length of output: 37576
[sysadminsmedia#467] Creation autofill
What type of PR is this?
What this PR does / why we need it:
This PR does the following:
I think this layout for the modals is a little more consistent and efficient, since having the labels on top makes it possible to tab to the create button. Any other opinions on this would be appreciated though.
Which issue(s) this PR fixes:
Fixes #411
Summary by CodeRabbit
Release Notes
New Features
Improvements
Style Updates
User Experience