-
-
Notifications
You must be signed in to change notification settings - Fork 34
feat(persons): add CSV import functionality #4326
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?
feat(persons): add CSV import functionality #4326
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
WalkthroughAdds a full CSV import/export feature (UI, parsing, template download), many person-mutating utilities (enrollments, privileges, spiritual-status, emergency contacts), assignment structures and toggle logic, new SVG icon components, All Persons wiring for data-exchange, and removes a legacy export button. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant U as User
participant AP as All Persons (header)
participant D as ImportExport Dialog
participant I as Import (useImport)
participant C as ConfirmImport UI
participant P as CSV Parser (useCSVImport)
participant H as Field Handlers / Mutators
participant DB as Database
U->>AP: Click Import/Export
AP->>D: open dialog
U->>D: Select Import tab
U->>I: Drop CSV file
I->>P: getCSVHeaders(contents)
I->>D: set fileData, navigate to confirm
D->>C: render with fileData
C->>P: parseCsvToPersonsAndGroups(contents, selectedFields)
loop per CSV row
P->>H: map columns → handlers (convert/enrollment/privilege/assignment/fields)
H->>P: mutate Person objects
end
C->>P: addPersonsToDB / addGroupsToDB
P->>DB: upsert persons & groups
P-->>C: return import results
C-->>D: close dialog
sequenceDiagram
autonumber
participant CI as ConfirmImport
participant CFG as usePersonsImportConfig
participant HF as HandlerFactory
participant MUT as Mutators (utils)
participant PR as Person object
CI->>CFG: request PERSON_FIELD_META
CI->>HF: call makeXHandler for a field
HF->>MUT: invoke specific mutator (e.g., toggleAssignment, privilegesAddHistory)
MUT->>PR: mutate person.person_data and updatedAt
MUT-->>HF: done
HF-->>CI: handler complete
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120 minutes Areas needing careful review:
Possibly related PRs
Suggested labels
Suggested reviewers
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Tip 📝 Customizable high-level summaries are now available in beta!You can now customize how CodeRabbit generates the high-level summary in your pull requests — including its content, structure, tone, and formatting.
Example instruction:
Note: This feature is currently in beta for Pro-tier users, and pricing will be announced later. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
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: 16
🧹 Nitpick comments (7)
src/features/persons/import_persons/index.tsx (1)
60-60: Consider adding download icon for consistency.The commented-out download icon could enhance the user experience by providing visual context for the template download action.
- // startIcon={<Download />} + startIcon={<IconDownload />}Don't forget to import the IconDownload component if you decide to add it.
src/components/icons/IconUpload.tsx (2)
3-9: Consider reusing the existing IconProps type for consistency.There's already an
IconPropstype defined insrc/views/components/icons/index.types.ts. Consider importing and extending it if needed, rather than creating a duplicate definition.-type IconProps = { - color?: string; - width?: number; - height?: number; - sx?: SxProps<Theme>; - className?: string; -}; +import { IconProps as BaseIconProps } from '@views/components/icons/index.types'; + +type IconProps = BaseIconProps & { + width?: number; + height?: number; + sx?: SxProps<Theme>; + className?: string; +};
20-20: Handle undefined className to avoid "undefined" in class string.When
classNameis undefined, the template literal will produce "organized-icon-upload undefined".- className={`organized-icon-upload ${className}`} + className={`organized-icon-upload${className ? ` ${className}` : ''}`}src/utils/person.ts (1)
11-74: Document that these functions mutate the input PersonType object.All functions in this file mutate the passed
PersonTypeobject. Consider adding JSDoc comments to make this behavior explicit.Example for one function:
+/** + * Updates the first name of a person and recalculates the display name. + * @param newPerson - The person object to update (will be mutated) + * @param value - The new first name + */ export const changeFirstname = (newPerson: PersonType, value: string) => {src/features/persons/import_persons/useImportPersons.ts (2)
226-226: Remove empty line.return dataLines .map((line) => { - try {
369-374: Remove commented out code.errorReason = String(error); console.error('Fehler beim Speichern:', error); - /* displaySnackNotification({ - header: t('tr_personAdded'), - message: 'Fehler beim Speichern:' + error, - severity: 'error', - }); */ }src/utils/spiritual_status.ts (1)
175-279: Consider extracting common history management logicThis function shares significant logic with
toggleUnbaptizedPublisher. Consider extracting the common pattern of closing/deleting history records based on date comparisons into a reusable helper function.Would you like me to help create a reusable helper function to reduce the code duplication between these toggle functions?
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (2)
src/locales/de-DE/general.jsonis excluded by!**/*.jsonsrc/locales/en/general.jsonis excluded by!**/*.json
📒 Files selected for processing (12)
src/components/icons/IconUpload.tsx(1 hunks)src/components/icons/index.ts(1 hunks)src/definition/person.ts(1 hunks)src/features/persons/assignments/useAssignments.tsx(2 hunks)src/features/persons/import_persons/index.tsx(1 hunks)src/features/persons/import_persons/useImportPersons.ts(1 hunks)src/pages/persons/all_persons/index.tsx(2 hunks)src/utils/assignments.ts(1 hunks)src/utils/enrollments.ts(1 hunks)src/utils/person.ts(1 hunks)src/utils/privileges.ts(1 hunks)src/utils/spiritual_status.ts(1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (6)
src/components/icons/IconUpload.tsx (1)
src/views/components/icons/index.types.ts (1)
IconProps(1-5)
src/utils/assignments.ts (1)
src/definition/person.ts (1)
PersonType(66-104)
src/utils/person.ts (1)
src/definition/person.ts (1)
PersonType(66-104)
src/utils/privileges.ts (2)
src/definition/person.ts (2)
PersonType(66-104)PrivilegeType(4-4)src/utils/date.ts (1)
formatDate(15-17)
src/utils/enrollments.ts (2)
src/definition/person.ts (2)
PersonType(66-104)EnrollmentType(11-11)src/utils/date.ts (1)
formatDate(15-17)
src/utils/spiritual_status.ts (2)
src/definition/person.ts (1)
PersonType(66-104)src/utils/date.ts (2)
formatDate(15-17)dateFirstDayMonth(26-31)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Socket Security: Pull Request Alerts
- GitHub Check: Summary
🔇 Additional comments (19)
src/definition/person.ts (4)
3-4: LGTM! Excellent type safety improvement.The pattern of using
constarrays to derive types enables both compile-time type checking and runtime validation, which is essential for the CSV import functionality.
6-8: LGTM! Robust type guard implementation.The type guard correctly uses the
includesmethod with proper type assertion. This will be valuable for validating CSV data at runtime.
10-11: LGTM! Consistent pattern for enrollment types.Following the same pattern as privilege types ensures consistency across the codebase.
13-15: LGTM! Consistent type guard implementation.The enrollment type guard follows the same robust pattern as the privilege type guard.
src/components/icons/index.ts (1)
302-302: LGTM! Standard icon export.The export follows the established pattern and properly integrates the new IconUpload component.
src/pages/persons/all_persons/index.tsx (2)
18-18: LGTM! Proper import placement.The import is correctly placed with other feature imports and follows the established pattern.
44-44: LGTM! Appropriate UI integration.The PersonsImport component is properly placed within the person editor conditional, alongside other person management actions. This provides a logical user experience.
src/features/persons/assignments/useAssignments.tsx (3)
141-160: LGTM! Well-structured assignment lookup.The memoized lookup object efficiently maps assignment codes to human-readable names. The logic correctly handles both string and enum codes, and the dot notation provides a clear hierarchical structure.
162-164: LGTM! Robust assignment name resolver.The function provides a clean interface for resolving assignment codes to names, with appropriate fallback behavior when no match is found.
335-335: LGTM! Proper hook integration.The getAssignmentName function is correctly added to the hook's return value, making it accessible to consumers.
src/features/persons/import_persons/index.tsx (5)
1-9: LGTM! Clean imports and dependencies.The imports are well-organized and include all necessary dependencies for the component functionality.
10-28: LGTM! Proper state management and event handlers.The component correctly uses React hooks for state management and provides clean event handlers for user interactions.
32-38: LGTM! Clean import button implementation.The button properly uses the IconUpload component and follows the established button pattern in the application.
40-69: LGTM! Well-structured dialog with clear user flow.The dialog provides a clear user experience with descriptive text and appropriate action buttons. The three-step flow (cancel, download template, choose file) is intuitive.
71-77: LGTM! Proper hidden file input implementation.The hidden file input correctly restricts file types to CSV and uses the ref pattern for programmatic access. This is a standard and accessible approach.
src/features/persons/import_persons/useImportPersons.ts (3)
399-411: Verify file encoding handling for international characters.The FileReader uses default encoding. For CSV files with international characters, explicit UTF-8 encoding might be needed.
Consider specifying the encoding:
- reader.readAsText(file); + reader.readAsText(file, 'UTF-8');
288-291: Fix type error: convertValue returns Date, not string.The
convertValuefunction with 'date' type returns a Date object, buttoLowerCase()is being called on it.privilegeStartDateChange( csvperson, csvperson.person_data.privileges[0].id, - convertValue(cols[index].toLowerCase(), 'date') + convertValue(cols[index], 'date') );Likely an incorrect or invalid review comment.
317-321: Fix type error: convertValue returns Date, not string.Same issue as before - calling
toLowerCase()on a Date result.enrollmentStartDateChange( csvperson, csvperson.person_data.enrollments[0].id, - convertValue(cols[index].toLowerCase(), 'date') + convertValue(cols[index], 'date') );Likely an incorrect or invalid review comment.
src/utils/spiritual_status.ts (1)
105-173: Well-structured mutual exclusivity logicThe function correctly handles the complex state transitions between midweek meeting student and unbaptized publisher statuses, with proper consideration for the
isAddPersonflag and date boundaries.
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
♻️ Duplicate comments (5)
src/features/persons/import_persons/useImportPersons.ts (5)
258-263: Add null check before accessing enrollment array.The code assumes the enrollment array has at least one element without verification.
+ if (csvperson.person_data.enrollments.length > 0) { enrollmentChange( csvperson, csvperson.person_data.enrollments[0].id, enrollmentType ); + }
367-367: Use translation for error message.The error message is hardcoded in German instead of using the translation system.
- console.error('Fehler beim Parsen der Zeile:', line, error); + console.error('Error parsing line:', line, error);
386-387: Use translation for error messages.Error messages are hardcoded in German.
- console.error('UUID-Kollision:', person.person_uid); + console.error('UUID collision:', person.person_uid);
396-396: Use translation for error message.The error message is hardcoded in German instead of using the translation system.
- console.error('Fehler beim Speichern:', error); + console.error('Error saving person:', error);
265-274: Add null check before accessing enrollment array.Similar to the previous issue, this code accesses
enrollments[0]without checking if the array has elements.if ( + Array.isArray(csvperson.person_data.enrollments) && + csvperson.person_data.enrollments.length > 0 && csvperson.person_data.enrollments[0].id !== undefined && enrollmentType === 'start_date' ) {
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
src/features/persons/assignments/useAssignments.tsx(2 hunks)src/features/persons/import_persons/useImportPersons.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- src/features/persons/assignments/useAssignments.tsx
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/features/persons/import_persons/useImportPersons.ts (11)
src/states/settings.ts (1)
userDataViewState(485-494)src/states/field_service_groups.ts (1)
languageGroupsState(91-95)src/services/dexie/schema.ts (1)
personSchema(192-237)src/states/persons.ts (1)
personsState(17-17)src/definition/person.ts (5)
PersonType(66-104)ALL_PRIVILEGE_TYPES(3-3)ALL_ENROLLMENT_TYPES(10-10)isPrivilegeType(6-8)isEnrollmentType(13-15)src/utils/person.ts (7)
changeFirstname(11-22)changeLastname(24-35)toggleGender(37-53)changeBirthDate(55-59)changeEmailAddress(61-64)changeAddress(71-74)changePhone(66-69)src/utils/spiritual_status.ts (6)
toggleMidweekMeetingStudent(5-55)midweekMeetingStudentStartDateChange(57-68)toggleBaptizedPublisher(175-279)changeBaptismDate(281-307)toggleUnbaptizedPublisher(105-173)toggleActive(309-361)src/utils/assignments.ts (1)
toggleAssignment(6-92)src/utils/privileges.ts (3)
privilegesAddHistory(4-15)privilegeChange(32-45)privilegeStartDateChange(17-30)src/utils/enrollments.ts (3)
enrollmentsAddHistory(4-13)enrollmentChange(30-43)enrollmentStartDateChange(15-28)src/services/dexie/persons.ts (1)
dbPersonsSave(19-106)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Socket Security: Pull Request Alerts
- GitHub Check: Summary
🔇 Additional comments (6)
src/features/persons/import_persons/useImportPersons.ts (6)
63-89: LGTM: Well-structured handler mapping.The handlers mapping provides a clean approach to connect CSV column paths to person property updates. The comment about translation variables is helpful for maintainability.
145-180: LGTM: Sophisticated CSV delimiter detection.The delimiter detection algorithm is well-implemented, using multiple sample lines and consistency scoring to determine the most appropriate delimiter. The bonus scoring for consistent column counts is a nice touch.
114-143: LGTM: Comprehensive value conversion function.The value conversion function handles multiple data types appropriately with proper error handling for JSON parsing and date conversion.
437-444: LGTM: Smart CSV delimiter selection based on locale.Using the locale's decimal separator to determine the appropriate CSV delimiter is a thoughtful approach that will improve user experience across different regions.
489-503: LGTM: Proper CSV template generation with Excel compatibility.The BOM addition for Excel compatibility and proper blob handling for file download are well-implemented. The cleanup of the object URL is also handled correctly.
235-241: Add null check before accessing privileges array.The code accesses
privileges[0]without verifying the array has elements, which could cause a runtime error.if ( Array.isArray(csvperson.person_data.privileges) && csvperson.person_data.privileges.length !== 0 && privilegeName === 'start_date' ) { + if (csvperson.person_data.privileges.length > 0) { privilegeStartDateChange( csvperson, csvperson.person_data.privileges[0].id, convertValue(privilegeValue.toLowerCase(), 'date') ); + } }Likely an incorrect or invalid review comment.
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
♻️ Duplicate comments (4)
src/features/persons/import_persons/useImportPersons.ts (4)
367-367: Use translation for error messages.The error message is hardcoded in German instead of using the translation system.
- console.error('Fehler beim Parsen der Zeile:', line, error); + console.error('Error parsing line:', line, error);
386-387: Use translation for error messages.Error messages are hardcoded in German.
- console.error('UUID-Kollision:', person.person_uid); + console.error('UUID collision:', person.person_uid);
396-396: Use translation for error messages.Error message is hardcoded in German.
- console.error('Fehler beim Speichern:', error); + console.error('Error saving person:', error);
409-409: Fix inverted logic condition.The condition
!(importedPersons.length !== 0)is equivalent toimportedPersons.length === 0, which means the success notification will only show when no persons were imported. This seems incorrect.- if (Array.isArray(importedPersons) && importedPersons.length !== 0) { + if (Array.isArray(importedPersons) && importedPersons.length > 0) {
🧹 Nitpick comments (1)
src/features/persons/import_persons/useImportPersons.ts (1)
63-89: Consider adding JSDoc documentation for the handlers object.The handlers object is well-structured with clear mapping between CSV paths and person property handlers. The comment on line 64 is helpful but could be enhanced with JSDoc for better maintainability.
+ /** + * Maps CSV column paths to their corresponding person property handlers. + * The path after 'person_data.' should correspond to the translation variable. + * @example 'person_data.date' → tr_date + */ const handlers: Record<string, (p: PersonType, value: string) => void> = { - // for the correct translation in the csv template it is important that the part after 'person_data.' is correspending to the translation variable like date -> tr_date 'person_data.firstname': (p, v) => changeFirstname(p, v),
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
src/features/persons/import_persons/useImportPersons.ts(1 hunks)src/utils/assignments.ts(1 hunks)src/utils/person.ts(1 hunks)src/utils/spiritual_status.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
- src/utils/person.ts
- src/utils/assignments.ts
- src/utils/spiritual_status.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Socket Security: Pull Request Alerts
🔇 Additional comments (7)
src/features/persons/import_persons/useImportPersons.ts (7)
1-48: LGTM: Comprehensive imports and setup.The imports are well-organized and cover all necessary dependencies for the CSV import functionality. The integration with utility functions and type definitions shows good modular design.
114-143: LGTM: Robust value conversion with proper type handling.The
convertValuefunction handles various data types appropriately with null checks and error handling for JSON parsing and date conversion.
145-180: LGTM: Sophisticated delimiter detection algorithm.The delimiter detection logic is well-implemented with scoring based on column count and consistency. The bonus for consistent column counts across sample lines is a good approach.
437-444: LGTM: Clever locale-based delimiter selection.The logic to determine CSV delimiter based on the locale's decimal separator is a thoughtful approach for better compatibility with regional Excel settings.
481-504: LGTM: Proper CSV template download implementation.The template download function correctly handles BOM for Excel compatibility and properly cleans up the object URL after download.
506-513: LGTM: Clean hook interface.The hook returns a focused API with the three main functions needed for CSV import functionality.
205-242: Add array bounds checking for privileges access.The privilege handling logic correctly checks for empty arrays before adding history, but there's a potential issue when accessing
privileges[0]without verifying the array has elements after the history is added.if ( isPrivilegeType(privilegeName) && ['1', 'true'].includes(privilegeValue.toLowerCase()) ) { privilegesAddHistory(csvperson); - if ( - Array.isArray(csvperson.person_data.privileges) && - csvperson.person_data.privileges.length !== 0 - ) { + if ( + Array.isArray(csvperson.person_data.privileges) && + csvperson.person_data.privileges.length > 0 + ) { privilegeChange( csvperson, csvperson.person_data.privileges[0].id, privilegeName ); } }Likely an incorrect or invalid review comment.
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)
CHANGELOG.md (1)
1-1: Fix header spelling + markdown-lint violationThe heading is missing the mandatory space after the hashes and “Unrealesed” is misspelled.
Please align with the Keep-a-Changelog convention:-##[Unrealesed] +## [Unreleased]
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
CHANGELOG.md(1 hunks)
🧰 Additional context used
🪛 markdownlint-cli2 (0.17.2)
CHANGELOG.md
1-1: No space after hash on atx style heading
(MD018, no-missing-space-atx)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Socket Security: Pull Request Alerts
- GitHub Check: Summary
Signed-off-by: nobodyzero1 <[email protected]>
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)
CHANGELOG.md (1)
1-5: Fix heading typo & follow Markdown/Keep-a-Changelog conventions‒ “Unrealesed” is misspelled
‒ Missing space after the hashes triggers MD018 (##[Unrealesed])
‒ Bullet sentence should start with a capital and end with a period for consistency.-##[Unrealesed] - -### Features - -- **persons:** add button to import persons from CSV file on "All Persons" page, including template download and dynamic header translation for easier onboarding +## [Unreleased] + +### Features + +- **persons:** Add button to import persons from CSV file on "All Persons" page, including template download and dynamic header translation for easier onboarding.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
src/locales/de-DE/general.jsonis excluded by!**/*.json
📒 Files selected for processing (1)
CHANGELOG.md(2 hunks)
🧰 Additional context used
🪛 markdownlint-cli2 (0.17.2)
CHANGELOG.md
1-1: No space after hash on atx style heading
(MD018, no-missing-space-atx)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Summary
🔇 Additional comments (1)
CHANGELOG.md (1)
41-50: No additional feedback on these unchanged historical entries.
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: 2
🧹 Nitpick comments (2)
src/features/persons/import_export/confirm_import/usePersonsImportConfig.ts (2)
163-170: Standardize boolean examples to "yes" (lowercase) per UX guidance.Examples currently mix "Yes"/"yes". Normalize to "yes" to match parsing expectations and UX copy.
- examples: ['Yes', '', 'Yes', ''], + examples: ['yes', '', 'yes', ''], - examples: ['Yes', 'yes', 'yes', ''], + examples: ['yes', 'yes', 'yes', ''], - examples: ['', '', '', 'Yes'], + examples: ['', '', '', 'yes'],Also applies to: 185-192, 202-209
285-296: Nice refactor: centralized assignment examples via lookup.The
ASSIGNMENT_EXAMPLES+getExamplesForAssignmentpattern improves maintainability over inline includes-chains.Optional: move
ASSIGNMENT_EXAMPLESto a small module (e.g.,assignmentExamples.ts) co-located withassignmentStructureto keep data/config together.Also applies to: 33-46
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/features/persons/import_export/confirm_import/usePersonsImportConfig.ts(1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-07-09T14:24:33.861Z
Learnt from: nobodyzero1
PR: sws2apps/organized-app#4326
File: src/utils/enrollments.ts:20-25
Timestamp: 2025-07-09T14:24:33.861Z
Learning: In src/utils/enrollments.ts, the enrollmentStartDateChange function already includes a null check for the current variable to handle cases where the find operation returns undefined.
Applied to files:
src/features/persons/import_export/confirm_import/usePersonsImportConfig.ts
🧬 Code graph analysis (1)
src/features/persons/import_export/confirm_import/usePersonsImportConfig.ts (6)
src/definition/person.ts (3)
PersonType(66-109)ALL_PRIVILEGE_TYPES(3-3)ALL_ENROLLMENT_TYPES(10-10)src/utils/person.ts (7)
changeFirstname(4-15)changeLastname(17-28)toggleGender(30-49)changeBirthDate(51-55)changeEmailAddress(57-60)changeAddress(67-70)changePhone(62-65)src/utils/emergencyContacts.tsx (1)
stringAddEmergencyContacts(4-39)src/utils/spiritual_status.ts (6)
toggleMidweekMeetingStudent(23-62)midweekMeetingStudentStartDateChange(64-85)toggleBaptizedPublisher(148-172)changeBaptismDate(174-203)toggleUnbaptizedPublisher(123-147)toggleActive(212-278)src/utils/privileges.ts (1)
privilegeStartDateChange(19-38)src/utils/enrollments.ts (1)
enrollmentStartDateChange(16-30)
🔇 Additional comments (2)
src/features/persons/import_export/confirm_import/usePersonsImportConfig.ts (2)
172-183: Good defensive guards on history/privilege/enrollment ids.Using optional chaining with
?? ''aligns with the change helpers that early-return on empty ids; avoids runtime crashes when start-date is provided without a prior toggle. Nice.Based on learnings.
Also applies to: 236-250, 269-283
98-108: Implement handler or hide field_service_group from import template.The handler is confirmed to be a no-op (lines 103–107). Since the field appears in exported templates via
useTemplateDownload.tsxand flows throughPERSON_FIELD_METAintouseConfirmImport.tsx, users will see and be able to fill this field, but it will be silently discarded on import—confusing behavior.Two options:
- Wire the handler to a setter (if group assignment logic exists elsewhere in the codebase)
- Add
showCheckbox: falseto hide it from the template (the interface supports this at line 61)
src/features/persons/import_export/confirm_import/usePersonsImportConfig.ts
Outdated
Show resolved
Hide resolved
src/features/persons/import_export/confirm_import/usePersonsImportConfig.ts
Outdated
Show resolved
Hide resolved
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)
src/features/persons/import_export/confirm_import/usePersonsImportConfig.ts (1)
56-64: Remove the unusedshowCheckboxproperty fromPersonFieldMetainterface.The
showCheckboxoptional property at line 63 is defined but never instantiated or referenced anywhere in the codebase. It should be removed to keep the interface clean.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/features/persons/import_export/confirm_import/usePersonsImportConfig.ts(1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-07-09T14:24:33.861Z
Learnt from: nobodyzero1
PR: sws2apps/organized-app#4326
File: src/utils/enrollments.ts:20-25
Timestamp: 2025-07-09T14:24:33.861Z
Learning: In src/utils/enrollments.ts, the enrollmentStartDateChange function already includes a null check for the current variable to handle cases where the find operation returns undefined.
Applied to files:
src/features/persons/import_export/confirm_import/usePersonsImportConfig.ts
🧬 Code graph analysis (1)
src/features/persons/import_export/confirm_import/usePersonsImportConfig.ts (6)
src/definition/person.ts (3)
PersonType(66-109)ALL_PRIVILEGE_TYPES(3-3)ALL_ENROLLMENT_TYPES(10-10)src/utils/person.ts (7)
changeFirstname(4-15)changeLastname(17-28)toggleGender(30-49)changeBirthDate(51-55)changeEmailAddress(57-60)changeAddress(67-70)changePhone(62-65)src/utils/emergencyContacts.tsx (1)
stringAddEmergencyContacts(4-39)src/utils/spiritual_status.ts (6)
toggleMidweekMeetingStudent(23-62)midweekMeetingStudentStartDateChange(64-85)toggleBaptizedPublisher(148-172)changeBaptismDate(174-203)toggleUnbaptizedPublisher(123-147)toggleActive(212-278)src/utils/privileges.ts (1)
privilegeStartDateChange(19-38)src/utils/enrollments.ts (1)
enrollmentStartDateChange(16-30)
🔇 Additional comments (9)
src/features/persons/import_export/confirm_import/usePersonsImportConfig.ts (9)
33-48: LGTM! Clean refactor of assignment examples.The lookup structure with typed 4-tuple examples and readonly return type is well implemented. The nullish coalescing provides a safe fallback, and the separation of data from logic makes future updates straightforward.
173-185: Good fix for safe array access.The handler now uses optional chaining (
history[0]?.id) and nullish coalescing (?? '') to safely access the midweek meeting student history, preventing the runtime error flagged in previous reviews.
222-236: LGTM! Well-structured dynamic privilege fields.The mapping from
ALL_PRIVILEGE_TYPEScorrectly generates privilege fields with appropriate examples (elder in first, MS in second) and properly transforms the 'ms' key to 'ministerialServant' for the translation label.
238-252: Good fix for safe privilege access.The handler now uses optional chaining (
privileges[0]?.id) and nullish coalescing (?? '') to safely handle cases where no privilege exists. The underlyingprivilegeStartDateChangefunction handles the empty string ID gracefully by returning early.
254-263: LGTM! Clean dynamic enrollment field generation.The mapping correctly generates enrollment fields from
ALL_ENROLLMENT_TYPES, with examples showing appropriate variety (AP for the first person, FR for the third).
281-291: LGTM! Elegant dynamic assignment field generation.The
flatMapoverASSIGNMENT_SECTIONScleanly transforms the nested structure into a flat array of field metadata. UsinggetExamplesForAssignmentwith the refactored lookup structure makes this code maintainable and easy to extend.
293-303: LGTM! Clean aggregation and return.The final assembly spreads all field arrays in a logical order: personal information, privileges with start dates, enrollments with start dates, and assignments. The return structure is simple and clear.
99-110: Remove review comment: field_service_group handler is intentionally empty by design.The
field_service_grouphandler inusePersonsImportConfig.tsis not incomplete. InuseCSVImport.tsx, the import processor explicitly checks forfield_service_groupand delegates toaddPersonToGroupBySortIndex()to handle group assignment. This is a deliberate architectural pattern where the actual processing logic is centralized in the import flow rather than duplicated in the handler function. The empty handler is correct for this field.Likely an incorrect or invalid review comment.
265-279: Critical null safety bug in enrollmentStartDateChange requires immediate fix.The learnings claim a null check exists in
src/utils/enrollments.ts, but verification shows the function (lines 16-30) does NOT check ifcurrentis defined after calling.find(). Accessingcurrent.start_datewhencurrentisundefinedwill crash at runtime.While the call site mitigates this by passing an empty string (
'') when no enrollments exist (triggering an early return), the utility function remains unsafe for any scenario wherefind()returnsundefined. This needs a defensive check:const current = newPerson.person_data.enrollments.find( (history) => history.id === id ); if (!current) { return; }⛔ Skipped due to learnings
Learnt from: nobodyzero1 PR: sws2apps/organized-app#4326 File: src/utils/enrollments.ts:20-25 Timestamp: 2025-07-09T14:24:33.861Z Learning: In src/utils/enrollments.ts, the enrollmentStartDateChange function already includes a null check for the current variable to handle cases where the find operation returns undefined.
Hi @ux-git, Thank you so much for your extensive and very helpful feedback from July 25th! I've done my best to implement the points you suggested. Here is a summary of the changes:
I'm looking forward to any further feedback you might have |
Signed-off-by: nobodyzero1 <[email protected]>
|
Hello @ux-git ! Since I'm new to software development and open source contributions, I would appreciate some further feedback on the implementation if you have time. Specifically, I would like to know if the overall architecture/design is the right direction. If so, I could proceed with expanding the functionality. Thank you very much! |
|
@nobodyzero1 awesome work done! From the UX design perspective it's great, thank you! Some really minor styling comments after trying the live version:
@rhahao please share your comments as well. Also, would you want to standartize the import and export formats? Currently as I can see the export is in xslx and import is in csv. Perhaps having both in csv would be more consistent? Or implementing both xslx and csv to be used based on user preference? |
This commit implements several UI/UX adjustments to enhance consistency and simplicity: - **Link Style:** Removed text link underlines, retaining blue color for visual distinction. - **Template Tips Hover:** Removed background hover fill from the 'Template filling tips' area to simplify the composition, while maintaining clickability. - **Text Style:** Updated 'Template filling tips' body text to use the 'body-small-regular' global style for a more compact block appearance. - **Drag-and-Drop:** Added the standardized hover fill effect to the main drag-and-drop area for improved user feedback, consistent with congregation import (sws2apps#4647).
|
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)
src/features/persons/import_export/index.tsx (3)
1-2: Combine React imports into a single statement
reactis imported twice; consolidate into one import to satisfy Sonar and keep things tidy.You can do:
-import { useState } from 'react'; +import React, { useState } from 'react'; @@ -import React from 'react';This keeps the default
Reactimport available (for JSX/runtime) while avoiding duplicate imports.Based on static analysis hints, this should clear the “react imported multiple times” warning.
Also applies to: 21-21
103-128: Prefer a real<button>element for the tips toggle headerThe header is now keyboard-accessible with ARIA (good), but Sonar flags the
role="button"pattern. Rendering this Box as a native<button>gives better semantics and built‑in keyboard behavior, letting you drop extra ARIA/handlers.For example:
- <Box - role="button" - aria-expanded={tipsExpanded} - aria-controls="tips-content" - tabIndex={0} - onClick={handleToggleTips} - onKeyDown={(e: React.KeyboardEvent<HTMLDivElement>) => { - if (e.key === 'Enter' || e.key === ' ') { - e.preventDefault(); - handleToggleTips(); - } - }} - sx={{ - display: 'flex', - alignItems: 'center', - gap: '8px', - height: '24px', - width: '100%', - padding: 0, - cursor: 'pointer', - '&:focus-visible': { - outline: '2px solid blue', - outlineOffset: '2px', - }, - }} - > + <Box + component="button" + type="button" + aria-expanded={tipsExpanded} + aria-controls="tips-content" + onClick={handleToggleTips} + sx={{ + display: 'flex', + alignItems: 'center', + gap: '8px', + height: '24px', + width: '100%', + padding: 0, + border: 'none', + background: 'transparent', + textAlign: 'left', + cursor: 'pointer', + '&:focus-visible': { + outline: '2px solid blue', + outlineOffset: '2px', + }, + }} + >This keeps UX the same, improves semantics, and removes the need for custom key handling.
Based on static analysis hints about using real button elements instead of
role="button".
159-174: Droprole="region"from the tips list containerThe collapsible content is rendered as a
<ul>(viacomponent="ul"), which already has clear list semantics. Addingrole="region"on a list is unnecessary and is exactly what Sonar flags (it prefers a<section>for “regions”).You can simply rely on the list semantics:
- <Box - className="body-small-regular" - id="tips-content" - role="region" - component="ul" + <Box + className="body-small-regular" + id="tips-content" + component="ul"
aria-controls="tips-content"on the header will still work fine without the extra role.Based on static analysis hints around
role="region"usage.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (2)
package-lock.jsonis excluded by!**/package-lock.json,!**/*.jsonpackage.jsonis excluded by!**/*.json
📒 Files selected for processing (3)
CHANGELOG.md(1 hunks)src/features/persons/import_export/import/index.tsx(1 hunks)src/features/persons/import_export/index.tsx(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- src/features/persons/import_export/import/index.tsx
- CHANGELOG.md
🧰 Additional context used
🧬 Code graph analysis (1)
src/features/persons/import_export/index.tsx (1)
src/features/congregation/settings/import_export/index.types.ts (1)
ImportExportType(1-4)
🪛 GitHub Check: SonarCloud Code Analysis
src/features/persons/import_export/index.tsx
[warning] 103-128: Use , , , , or instead of the "button" role to ensure accessibility across all devices.
[warning] 1-1: 'react' imported multiple times.
[warning] 159-174: Use
[warning] 21-21: 'react' imported multiple times.
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Summary
🔇 Additional comments (1)
src/features/persons/import_export/index.tsx (1)
23-42: Hook wiring and overall component structure look goodThe
ImportExportcomponent cleanly wiresuseImportExport,useTemplateDownload, date formatting, and dialog state; the public props align with the sharedImportExportType. Nothing stands out as risky here.
|
Hi @ux-git ! Thank you very much for the thorough review and the positive feedback on the UX design! I have implemented all the minor styling adjustments requested:
I have pushed the changes to the branch. Please let me know if any further changes are needed! |












Description
This pull request adds a button for importing person records via CSV on the "All Persons" page, next to the "Add Person" button. The import makes it easier for users to add multiple people at once, which is especially helpful for onboarding and initial setup.
Importing persons data could significantly speed up the onboarding process, as adding each person manually can be tedious.
To implement the import, I extracted reusable logic from the existing "handle" functions into separate utility functions. The CSV import uses these, and the original handlers could also call these functions to avoid code duplication, but to make the testing of the new code easer I did not do this now.
When the import button is clicked, a dialog opens with instructions and an option to download a CSV template. The template headers are based on the PersonType properties, enrollments, privileges, and assignments. The second row contains translations for each header in the user's language. Assignment columns are generated dynamically, so future changes to assignments require no code changes.
The error handling and messaging are still quite basic at this stage, but in my opinion, the feature is already sufficiently robust for practical use and users would benefit from having it available.
Since I am new to contributing to open source projects like this, I am grateful for any suggestions or corrections. Please let me know if there is anything I can improve or if I should follow a different approach.
Dependencies:
No new dependencies were added.
Fixes # (issue)
No related issue
Type of change
Please delete options that are not relevant.
Checklist: