-
Notifications
You must be signed in to change notification settings - Fork 14
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
feat(apps/manage-frontend): auto-save of user inputs in element edit modal #4474
Conversation
Current Aviator status
This PR was merged manually (without Aviator). Merging manually can negatively impact the performance of the queue. Consider using Aviator next time.
See the real-time status of this PR on the
Aviator webapp.
Use the Aviator Chrome Extension
to see the status of your PR within GitHub.
|
📝 WalkthroughWalkthroughThis pull request introduces a comprehensive auto-save functionality for question and element management in the frontend application. The changes span multiple components and files, implementing a new Changes
Possibly related PRs
Suggested labels
Suggested reviewers
✨ Finishing Touches
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.
Actionable comments posted: 1
🔭 Outside diff range comments (1)
apps/frontend-manage/src/pages/index.tsx (1)
Line range hint
266-268
: Add error handling for query and mutation errors.The component doesn't handle query errors or display error messages to users when operations fail.
Add error handling:
{!dataQuestions || loadingQuestions ? ( <Loader /> + ) : errorQuestions ? ( + <div className="flex items-center justify-center p-4 text-red-600"> + {t('manage.general.errorLoadingQuestions')} + </div> ) : ( // ... existing content )}Also, add error handling for mutations:
onClick={async () => { - await toggleIsArchived({ - variables: { - questionIds: Object.keys(selectedElementContent).map(Number), - isArchived: true, - }, - }) - setSelectedQuestions({}) + try { + await toggleIsArchived({ + variables: { + questionIds: Object.keys(selectedElementContent).map(Number), + isArchived: true, + }, + }) + setSelectedQuestions({}) + } catch (error) { + toast.error(t('manage.general.errorArchivingQuestions')) + } }}
🧹 Nitpick comments (8)
apps/frontend-manage/src/pages/index.tsx (2)
56-56
: Consider centralizing toast state management.The success toast state management could be simplified using React Context or a toast library to avoid prop drilling and callback passing.
Consider using a toast library like
react-hot-toast
orreact-toastify
:+ import { toast } from 'react-hot-toast' - const [successToast, setSuccessToast] = useState(false) // Replace toast trigger callbacks with direct toast calls - triggerSuccessToast={() => setSuccessToast(true)} + triggerSuccessToast={() => toast.success(t('manage.questionPool.successMessage'))} // Remove the ElementSuccessToast component - <ElementSuccessToast - open={successToast} - onClose={() => setSuccessToast(false)} - />
Line range hint
124-134
: Optimize memoization dependencies.The
processedQuestions
memo has multiple dependencies that could potentially trigger unnecessary recalculations. Consider breaking down the processing into smaller, more focused memos.Consider this optimization:
+ const filteredQuestions = useMemo( + () => { + if (!dataQuestions?.userQuestions) return [] + return dataQuestions.userQuestions.filter( + (question) => filters.archive === question.isArchived + ) + }, + [dataQuestions?.userQuestions, filters.archive] + ) const processedQuestions = useMemo(() => { - if (dataQuestions?.userQuestions) { + if (filteredQuestions.length > 0) { const items = processItems( - dataQuestions?.userQuestions, + filteredQuestions, filters, sort, index ) return items } - }, [dataQuestions?.userQuestions, filters, index, sort]) + }, [filteredQuestions, filters, index, sort])apps/frontend-manage/src/components/questions/manipulation/AutoSaveMonitor.ts (3)
4-12
: Consider adding JSDoc documentation for the hook.Add documentation to describe the purpose, parameters, and behavior of the hook.
+/** + * Custom hook for auto-saving form values with a delay. + * @param values - The form values to be saved + * @param setAutoSavedElement - Callback to update the auto-saved element + * @param setSaving - Callback to update the saving state + */ function useAutoSave({ values, setAutoSavedElement, setSaving,
13-28
: Consider using debounce instead of setTimeout.The current implementation uses a basic setTimeout for delaying saves. Consider using a debounce utility (e.g., from lodash) for more robust handling of rapid changes.
- const savingTimeout = useRef<NodeJS.Timeout | null>(null) - const autoSaveContent = useCallback( - ({ values }: { values: ElementFormTypes }) => { - if (savingTimeout.current) { - clearTimeout(savingTimeout.current as NodeJS.Timeout) - } - - savingTimeout.current = setTimeout(async () => { - setSaving(false) - setAutoSavedElement(values) - }, 2000) - }, - [setAutoSavedElement, setSaving] - ) + const autoSaveContent = useMemo( + () => + debounce( + ({ values }: { values: ElementFormTypes }) => { + setSaving(false) + setAutoSavedElement(values) + }, + 2000 + ), + [setAutoSavedElement, setSaving] + )
30-39
: Consider adding error handling.The auto-save operation might fail. Consider adding error handling to manage failures gracefully.
useEffect(() => { setSaving(true) - autoSaveContent({ values }) + try { + autoSaveContent({ values }) + } catch (error) { + console.error('Auto-save failed:', error) + setSaving(false) + } return () => { if (savingTimeout.current) { clearTimeout(savingTimeout.current as NodeJS.Timeout) } } }, [values])apps/frontend-manage/src/components/questions/manipulation/StudentElementPreview.tsx (1)
168-180
: Consider extracting the saving indicator into a separate component.The saving indicator logic could be reused in other components. Consider extracting it into a reusable component.
+interface SavingIndicatorProps { + saving: boolean +} + +const SavingIndicator: React.FC<SavingIndicatorProps> = ({ saving }) => ( + <div className="float-right mt-3 text-slate-400"> + {saving ? ( + <div className="flex flex-row items-center gap-2"> + <FontAwesomeIcon icon={faSpinner} className="animate-spin" /> + {t('manage.questionForms.savingTemporarily')} + </div> + ) : ( + <div className="flex flex-row items-center gap-2"> + <FontAwesomeIcon icon={faSave} /> + {t('manage.questionForms.temporarilySaved')} + </div> + )} + </div> +) - <div className="float-right mt-3 text-slate-400"> - {saving ? ( - <div className="flex flex-row items-center gap-2"> - <FontAwesomeIcon icon={faSpinner} className="animate-spin" /> - {t('manage.questionForms.savingTemporarily')} - </div> - ) : ( - <div className="flex flex-row items-center gap-2"> - <FontAwesomeIcon icon={faSave} /> - {t('manage.questionForms.temporarilySaved')} - </div> - )} - </div> + <SavingIndicator saving={saving} />apps/frontend-manage/src/components/questions/manipulation/ElementEditModal.tsx (2)
88-94
: Consider adding a prefix to localStorage keys.Add a prefix to localStorage keys to avoid potential conflicts with other applications using the same domain.
const [autoSavedElement, setAutoSavedElement] = useLocalStorage<ElementFormTypes>( typeof elementId === 'undefined' - ? 'autosave-element-creation' - : `autosave-element-${elementId}`, + ? 'klicker-autosave-element-creation' + : `klicker-autosave-element-${elementId}`, undefined )
298-305
: Consider using a utility function for localStorage cleanup.Extract the localStorage cleanup logic into a utility function for better reusability and maintainability.
+const clearAutoSaveData = (elementId?: number) => { + const key = typeof elementId === 'undefined' + ? 'klicker-autosave-element-creation' + : `klicker-autosave-element-${elementId}` + localStorage.removeItem(key) +} - if (autoSavedElement) { - localStorage.removeItem( - typeof elementId === 'undefined' - ? 'autosave-element-creation' - : `autosave-element-${elementId}` - ) - } + if (autoSavedElement) { + clearAutoSaveData(elementId) + }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
apps/frontend-manage/src/components/questions/manipulation/AutoSaveMonitor.ts
(1 hunks)apps/frontend-manage/src/components/questions/manipulation/ElementEditModal.tsx
(8 hunks)apps/frontend-manage/src/components/questions/manipulation/StudentElementPreview.tsx
(3 hunks)apps/frontend-manage/src/pages/index.tsx
(1 hunks)packages/i18n/messages/de.ts
(1 hunks)packages/i18n/messages/en.ts
(1 hunks)
✅ Files skipped from review due to trivial changes (1)
- packages/i18n/messages/de.ts
⏰ Context from checks skipped due to timeout of 90000ms (5)
- GitHub Check: check
- GitHub Check: SonarCloud
- GitHub Check: build
- GitHub Check: cypress-run
- GitHub Check: Analyze (javascript)
🔇 Additional comments (2)
apps/frontend-manage/src/components/questions/manipulation/ElementEditModal.tsx (1)
364-368
: LGTM! Well-integrated auto-save functionality.The AutoSaveMonitor component is properly integrated with the form values and state management.
packages/i18n/messages/en.ts (1)
985-986
: LGTM! Clear and concise translation messages.The translation messages effectively communicate the auto-save status to users.
klicker-uzh Run #4149
Run Properties:
|
Project |
klicker-uzh
|
Branch Review |
autosave-questions
|
Run status |
Passed #4149
|
Run duration | 19m 08s |
Commit |
fe74da38fd ℹ️: Merge 706376681b07a2bbbfa19cbe0a0b292f20cb2400 into 49da9f6874b2bd8ce2dc88f8e465...
|
Committer | Julius Schlapbach |
View all properties for this run ↗︎ |
Test results | |
---|---|
Failures |
0
|
Flaky |
0
|
Pending |
0
|
Skipped |
0
|
Passing |
311
|
View all changes introduced in this branch ↗︎ |
…ion and/or editing
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 (5)
apps/frontend-manage/src/components/questions/manipulation/RecoveryPrompt.tsx (2)
6-16
: Props interface could be extracted for better maintainability.Consider extracting the props interface into a separate type definition for better code organization and reusability.
+interface RecoveryPromptProps { + open: boolean + onRecovery: () => void + onDiscard: () => void + editMode: boolean +} -function RecoveryPrompt({ +function RecoveryPrompt({ open, onRecovery, onDiscard, editMode, -}: { - open: boolean - onRecovery: () => void - onDiscard: () => void - editMode: boolean -}) { +}: RecoveryPromptProps) {
19-36
: Consider adding aria-label for better accessibility.The modal implementation looks good, but could benefit from additional accessibility attributes.
<Modal hideCloseButton escapeDisabled open={open} onClose={() => null} title={t('manage.questionForms.recoverData')} + aria-label={t('manage.questionForms.recoverData')} className={{ content: 'gap-1' }} >
apps/frontend-manage/src/components/questions/manipulation/ElementEditModal.tsx (1)
138-140
: Consider adding a comment explaining the initialValues logic.The conditional logic for initialValues could benefit from a comment explaining the precedence.
+// For duplicated elements, always use the initial values +// For editing, prefer auto-saved values if they exist, otherwise use initial values initialValues={ isDuplication ? initialValues : (autoSavedElement ?? initialValues) }apps/frontend-manage/src/components/questions/Question.tsx (2)
182-190
: Add error handling for localStorage accessWhile the logic is sound, consider adding try-catch block to handle potential localStorage access errors, which can occur in various scenarios (e.g., private browsing mode, storage quota exceeded).
onClick={() => { - const value = localStorage.getItem(`autosave-element-${id}`) + let value = null + try { + value = localStorage.getItem(`autosave-element-${id}`) + } catch (error) { + console.error('Failed to access localStorage:', error) + } if (value) { setShowRecoveryPrompt(true) } else { setIsModificationModalOpen(true) } }}
198-212
: Consider extracting localStorage operations to a custom hookThe localStorage operations could be moved to a custom hook to improve reusability and separation of concerns.
// useAutoSaveStorage.ts const useAutoSaveStorage = (elementId: number) => { const clearAutosave = useCallback(() => { try { localStorage.removeItem(`autosave-element-${elementId}`) } catch (error) { console.error('Failed to clear autosave:', error) } }, [elementId]) return { clearAutosave } }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
apps/frontend-manage/src/components/questions/Question.tsx
(4 hunks)apps/frontend-manage/src/components/questions/manipulation/ElementEditModal.tsx
(8 hunks)apps/frontend-manage/src/components/questions/manipulation/RecoveryPrompt.tsx
(1 hunks)apps/frontend-manage/src/pages/index.tsx
(4 hunks)packages/i18n/messages/de.ts
(1 hunks)packages/i18n/messages/en.ts
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- apps/frontend-manage/src/pages/index.tsx
- packages/i18n/messages/en.ts
⏰ Context from checks skipped due to timeout of 90000ms (3)
- GitHub Check: check
- GitHub Check: SonarCloud
- GitHub Check: build
🔇 Additional comments (10)
apps/frontend-manage/src/components/questions/manipulation/RecoveryPrompt.tsx (2)
1-4
: LGTM! Clean imports with clear dependencies.The imports are well-organized and include only the necessary dependencies.
37-56
: LGTM! Well-structured button layout with clear actions.The button implementation is clean with appropriate icons and translations.
apps/frontend-manage/src/components/questions/manipulation/ElementEditModal.tsx (3)
16-16
: LGTM! Well-structured auto-save initialization.The auto-save functionality is properly initialized with appropriate state variables and storage key generation.
Also applies to: 77-77, 88-94
301-308
: LGTM! Proper cleanup of auto-saved data.The code correctly removes auto-saved data after successful form submission.
367-371
: LGTM! Clean integration of auto-save monitoring.The AutoSaveMonitor component is properly integrated with necessary props.
packages/i18n/messages/de.ts (1)
984-992
: LGTM! Comprehensive translations for auto-save functionality.The added translations cover all necessary UI text for the auto-save feature, including status messages, recovery prompts, and action buttons.
apps/frontend-manage/src/components/questions/Question.tsx (4)
23-23
: LGTM: Clean import additionThe RecoveryPrompt component import is properly organized with other component imports.
85-85
: LGTM: Well-structured state managementThe showRecoveryPrompt state is properly initialized and follows React state management best practices.
210-210
: Verify the purpose of the editMode propThe editMode prop is hardcoded to false without clear purpose. Please clarify its intended use or remove if unnecessary.
182-212
: Verify integration with autosave mechanismPlease ensure that:
- The autosave key format matches the one used in the actual autosave implementation
- The recovery flow properly restores all necessary form state
Run this script to verify the autosave key format consistency:
✅ Verification successful
✓ Autosave implementation is correct and consistent
The autosave key format and recovery flow are properly implemented across all components:
- Creation mode uses
autosave-element-creation
- Edit mode uses
autosave-element-${id}
- ElementEditModal handles both scenarios consistently
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Search for all autosave-related localStorage operations rg "localStorage\.(get|set|remove)Item.*autosave-element-" --type tsLength of output: 437
Script:
#!/bin/bash # Search for autosave-related code patterns rg "autosave" --type ts -A 3Length of output: 2466
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 (5)
apps/frontend-manage/src/components/questions/manipulation/AutoSaveMonitor.ts (2)
15-34
: Consider optimizing the value comparison logic.The current implementation stringifies the entire values object for comparison. For large forms, this could be inefficient.
Consider implementing a more efficient comparison:
-if (JSON.stringify(values) !== initialValuesString) { +// Use a deep comparison utility +import isEqual from 'lodash/isEqual' + +if (!isEqual(values, JSON.parse(initialValuesString))) { setAutoSavedElement(values) }
36-48
: Consider debouncing the saving indicator to prevent UI flicker.The saving state is set immediately on every value change, which might cause UI flicker with rapid changes.
Consider debouncing the saving indicator:
useEffect(() => { - setSaving(true) + const savingTimeout = setTimeout(() => setSaving(true), 500) autoSaveContent({ values }) return () => { if (savingTimeout.current) { clearTimeout(savingTimeout.current as NodeJS.Timeout) } + clearTimeout(savingTimeout) } }, [values])apps/frontend-manage/src/components/questions/manipulation/ElementEditModal.tsx (3)
Line range hint
77-94
: Consider namespacing the local storage keys.The current storage keys could potentially clash with other applications using the same domain.
Consider adding a namespace:
- typeof elementId === 'undefined' || isDuplication - ? 'autosave-element-creation' - : `autosave-element-${elementId}`, + typeof elementId === 'undefined' || isDuplication + ? '@klicker-uzh/autosave-element-creation' + : `@klicker-uzh/autosave-element-${elementId}`,
130-138
: Improve code documentation and ESLint configuration.The implementation is correct, but there are two areas for improvement:
- The comment could be more descriptive
- The ESLint disable comment might be unnecessary
Consider these improvements:
- // only update the form values on initial rendering in creation or edit mode (not in duplication mode) - // (otherwise, saving the question will directly trigger another save) + // In creation or edit mode, use auto-saved values if available + // In duplication mode, always use initial values to prevent recursive saves const formikInitialValues = useMemo(() => { if (!initialValues) { return undefined } return isDuplication ? initialValues : (autoSavedElement ?? initialValues) - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [isOpen, isDuplication, initialValues]) + }, [isOpen, isDuplication, initialValues, autoSavedElement])
309-316
: Extract the storage key generation logic to reduce duplication.The storage key generation logic is duplicated between initialization and cleanup.
Consider extracting the logic:
+const getStorageKey = (elementId: number | undefined, isDuplication: boolean) => { + return typeof elementId === 'undefined' || isDuplication + ? '@klicker-uzh/autosave-element-creation' + : `@klicker-uzh/autosave-element-${elementId}` +} // Use in initialization const [autoSavedElement, setAutoSavedElement] = useLocalStorage<ElementFormTypes>( - typeof elementId === 'undefined' || isDuplication - ? 'autosave-element-creation' - : `autosave-element-${elementId}`, + getStorageKey(elementId, isDuplication), undefined ) // Use in cleanup if (autoSavedElement) { - localStorage.removeItem( - typeof elementId === 'undefined' || isDuplication - ? 'autosave-element-creation' - : `autosave-element-${elementId}` - ) + localStorage.removeItem(getStorageKey(elementId, isDuplication)) }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
.github/workflows/cypress-testing.yml
(1 hunks)apps/frontend-manage/src/components/questions/manipulation/AutoSaveMonitor.ts
(1 hunks)apps/frontend-manage/src/components/questions/manipulation/ElementEditModal.tsx
(8 hunks)apps/frontend-manage/src/components/questions/manipulation/RecoveryPrompt.tsx
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- apps/frontend-manage/src/components/questions/manipulation/RecoveryPrompt.tsx
⏰ Context from checks skipped due to timeout of 90000ms (6)
- GitHub Check: test
- GitHub Check: check
- GitHub Check: SonarCloud
- GitHub Check: build
- GitHub Check: cypress-run
- GitHub Check: Analyze (javascript)
🔇 Additional comments (2)
apps/frontend-manage/src/components/questions/manipulation/AutoSaveMonitor.ts (1)
1-14
: LGTM! Well-structured hook parameters with proper TypeScript types.The parameter structure follows React best practices with proper type safety.
.github/workflows/cypress-testing.yml (1)
142-142
: LGTM! Appropriate upgrade of GitHub Actions dependency.The upgrade to
actions/upload-artifact@v4
follows GitHub Actions best practices.
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 (3)
apps/frontend-manage/src/components/questions/manipulation/ElementEditModal.tsx (2)
129-137
: Consider adding error handling for JSON parsing.While the memoization of initial values is well implemented, there's no explicit error handling for potential JSON parsing errors from local storage.
Consider wrapping the auto-saved element access in a try-catch:
const formikInitialValues = useMemo(() => { if (!initialValues) { return undefined } + try { return isDuplication ? initialValues : (autoSavedElement ?? initialValues) + } catch (error) { + console.error('Failed to parse auto-saved element:', error) + return initialValues + } }, [isOpen, isDuplication, initialValues])
308-315
: Consider using a cleanup utility function.The local storage cleanup logic is duplicated from the key generation logic.
Consider extracting the key generation to a utility function:
+const getAutoSaveKey = (elementId?: number, isDuplication?: boolean) => + typeof elementId === 'undefined' || isDuplication + ? 'autosave-element-creation' + : `autosave-element-${elementId}` // In the component: -localStorage.removeItem( - typeof elementId === 'undefined' || isDuplication - ? 'autosave-element-creation' - : `autosave-element-${elementId}` -) +localStorage.removeItem(getAutoSaveKey(elementId, isDuplication))apps/frontend-manage/src/pages/index.tsx (1)
395-405
: Consider extracting auto-save check logic.The create question button click handler contains inline local storage checks.
Consider extracting this logic to a separate function:
+const hasAutoSavedContent = () => { + const value = localStorage.getItem('autosave-element-creation') + return !!value +} // In the onClick handler: -const value = localStorage.getItem('autosave-element-creation') -if (value) { +if (hasAutoSavedContent()) { setShowRecoveryPrompt(true) } else { setIsQuestionCreationModalOpen(true) }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (9)
apps/frontend-manage/src/components/questions/manipulation/AutoSaveMonitor.ts
(1 hunks)apps/frontend-manage/src/components/questions/manipulation/ElementContentInput.tsx
(0 hunks)apps/frontend-manage/src/components/questions/manipulation/ElementEditModal.tsx
(6 hunks)apps/frontend-manage/src/pages/index.tsx
(4 hunks)cypress/cypress/e2e/B-feature-access-workflow.cy.ts
(1 hunks)cypress/cypress/e2e/D-questions-workflow.cy.ts
(23 hunks)cypress/cypress/fixtures/D-questions.json
(1 hunks)packages/i18n/messages/de.ts
(1 hunks)packages/i18n/messages/en.ts
(1 hunks)
💤 Files with no reviewable changes (1)
- apps/frontend-manage/src/components/questions/manipulation/ElementContentInput.tsx
✅ Files skipped from review due to trivial changes (1)
- cypress/cypress/e2e/B-feature-access-workflow.cy.ts
🚧 Files skipped from review as they are similar to previous changes (2)
- apps/frontend-manage/src/components/questions/manipulation/AutoSaveMonitor.ts
- packages/i18n/messages/en.ts
⏰ Context from checks skipped due to timeout of 90000ms (6)
- GitHub Check: SonarCloud
- GitHub Check: format
- GitHub Check: cypress-run
- GitHub Check: check
- GitHub Check: Analyze (javascript)
- GitHub Check: build
🔇 Additional comments (8)
cypress/cypress/e2e/D-questions-workflow.cy.ts (2)
62-62
: Consistent renaming of modal close buttons.The modal close button data-cy attribute has been consistently renamed from
close-question-modal
toclose-element-modal
across all test cases. This change aligns with the broader scope of the modal being used for both questions and other elements.Also applies to: 104-104, 165-165, 214-214, 321-321, 496-496, 631-631, 797-797, 979-979, 1050-1050, 1232-1232, 1366-1366, 1411-1411, 1473-1473, 1525-1525, 1643-1643, 1722-1722, 1914-1915
1916-2183
: Well-structured auto-save test suite with comprehensive coverage.The auto-save test suite thoroughly covers key scenarios:
- Empty questions not being stored
- Non-empty questions being stored and loaded correctly
- Recovery and discard functionality
- Local storage cleanup after saving
- Edit mode behavior
- Duplication mode behavior
The test structure follows good practices:
- Clear test descriptions
- Proper waiting for auto-save triggers
- Verification of both UI state and local storage
- Helper function to reduce code duplication
cypress/cypress/fixtures/D-questions.json (1)
104-116
: Well-structured test data for auto-save functionality.The new autoSave section provides comprehensive test data including:
- Base question data (title, content)
- Multiple choices with correct answer marking
- Modified versions for testing edits (titleEdited, contentEdited)
- Duplication data (titleEditedDuplicated)
apps/frontend-manage/src/components/questions/manipulation/ElementEditModal.tsx (2)
87-93
: Well-implemented auto-save storage with proper key management.The useLocalStorage hook implementation correctly handles different storage keys:
- Uses 'autosave-element-creation' for new elements
- Uses
autosave-element-${elementId}
for existing elements- Properly handles duplication mode
374-378
: Well-integrated auto-save monitoring.The AutoSaveMonitor component is properly integrated with:
- Access to current form values
- Initial values for comparison
- Setter for auto-save state
apps/frontend-manage/src/pages/index.tsx (2)
459-471
: Well-implemented recovery prompt with clear user actions.The RecoveryPrompt implementation:
- Properly handles both recovery and discard actions
- Cleans up local storage on discard
- Correctly manages modal states
56-56
: Remove redundantsortBy
state.The
sortBy
state is redundant as the sorting logic is already managed by theuseSortingAndFiltering
hook.packages/i18n/messages/de.ts (1)
984-990
: Well-structured translations for auto-save functionality!The added German translations for the auto-save feature are clear, consistent, and maintain a professional tone. The messages effectively communicate the data recovery functionality to users.
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 (3)
apps/frontend-manage/src/components/questions/manipulation/AutoSaveMonitor.ts (3)
4-12
: Consider using consistent types for comparison.The
initialValuesString
prop is typed as string whilevalues
is of typeElementFormTypes
. This might lead to inconsistent comparisons when checking for changes. Consider using the same type or adding type conversion utilities.- initialValuesString: string + initialValues: ElementFormTypes
15-30
: Optimize the auto-save implementation.Several improvements could enhance the code:
- The
async
keyword on line 22 is unnecessary as there are noawait
operations.- Type casting could be handled more elegantly with proper TypeScript configuration.
- The string comparison could be optimized.
- savingTimeout.current = setTimeout(async () => { + savingTimeout.current = setTimeout(() => { // only update the stored content if it has changed - if (JSON.stringify(values) !== initialValuesString) { + const currentValuesString = JSON.stringify(values) + if (currentValuesString !== initialValuesString) { setAutoSavedElement(values) } }, 2000)
1-45
: Consider enhancing user feedback and configuration.A few architectural improvements could enhance this implementation:
- Consider adding a callback to notify parent components about save status for visual feedback.
- The auto-save delay could be configurable through props.
- Consider implementing a save queue to handle rapid changes more efficiently.
Example implementation:
interface AutoSaveMonitorProps { values: ElementFormTypes; initialValues: ElementFormTypes; setAutoSavedElement: Dispatch<SetStateAction<ElementFormTypes>>; onSaveStatusChange?: (status: 'saving' | 'saved' | 'error') => void; saveDelay?: number; }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
apps/frontend-manage/src/components/questions/manipulation/AutoSaveMonitor.ts
(1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (7)
- GitHub Check: format
- GitHub Check: build
- GitHub Check: cypress-run
- GitHub Check: test
- GitHub Check: SonarCloud
- GitHub Check: check
- GitHub Check: Analyze (javascript)
🔇 Additional comments (3)
apps/frontend-manage/src/components/questions/manipulation/AutoSaveMonitor.ts (3)
1-2
: LGTM! Imports are well-organized.All necessary React hooks and types are properly imported.
32-40
: LGTM! Effect cleanup is properly implemented.The cleanup function correctly handles the timeout clearing, and the dependency array is properly specified.
42-45
: LGTM! Component export is correct.The component appropriately returns null as it's a utility component with no UI elements.
Quality Gate failedFailed conditions |
klicker-uzh Run #4152
Run Properties:
|
Project |
klicker-uzh
|
Branch Review |
v3
|
Run status |
Passed #4152
|
Run duration | 18m 58s |
Commit |
367e5c2541: feat(apps/manage-frontend): auto-save of user inputs in element edit modal (#447...
|
Committer | Julius Schlapbach |
View all properties for this run ↗︎ |
Test results | |
---|---|
Failures |
0
|
Flaky |
0
|
Pending |
0
|
Skipped |
0
|
Passing |
311
|
View all changes introduced in this branch ↗︎ |
Summary by CodeRabbit
New Features
Localization
Bug Fixes
Chores