Conversation
… Updated application status handling, improved tenant selection logic, and integrated Zustand for state management. Adjusted UI components for better user experience and added support for new application statuses. Enhanced late fee settings and deposit management with improved data handling.
|
Caution Review failedThe pull request is closed. 📝 WalkthroughWalkthroughCentralized Move-In form state added via a new Zustand store; many MoveIn step components migrated to use store-backed state. Application status literals renamed (UNDER_REVIEW→REVIEWING, WITHDRAWN→CANCELLED), an application update mutation and confirmation UI were added, and tenant selection now fetches and merges live data. Changes
Sequence Diagram(s)sequenceDiagram
actor User
participant Dashboard as Application Dashboard
participant Card as ApplicationCard
participant Modal as Confirmation Modal
participant Mutation as useUpdateApplication
participant API as Backend API
User->>Dashboard: Open applications list
Dashboard->>Card: Render card with onStatusChange/onMoveIn
User->>Card: Choose status action (Approve/Review/Decline)
Card->>Modal: Open confirmation modal
User->>Modal: Confirm
Modal->>Mutation: Call update mutation (id, newStatus)
Mutation->>API: PATCH /applications/:id
API-->>Mutation: Return updated application
Mutation-->>Dashboard: Invalidate/refresh queries
Dashboard-->>User: UI reflects updated status
sequenceDiagram
actor User
participant MoveIn as MoveIn Form
participant Store as useMoveInStore
participant Steps as MoveIn Steps
participant Service as tenant/application API
User->>MoveIn: Start flow
MoveIn->>Store: Initialize formData & currentStep
User->>Steps: Select property/tenant/recurring rent/etc.
Steps->>Store: setPropertyId / setTenantId / setRecurringRent / setLateFees / setDeposit
Store-->>Steps: Provide store-derived values for rendering
Steps->>Service: (Tenant step) fetch approved applications & tenants
Service-->>Steps: Return data
Steps->>Store: setTenantId(selectedTenantId)
User->>MoveIn: Finish flow
MoveIn->>Store: Persist final formData / reset or proceed
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
📜 Recent review detailsConfiguration used: defaults Review profile: CHILL Plan: Pro 📒 Files selected for processing (3)
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.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/pages/Dashboard/features/MoveIn/MoveIn.tsx (1)
329-343: Replace hardcoded placeholder values with actual data.
propertyName="abc"andleaseNumber="9"are hardcoded placeholders. The success modal should display the actual property name and lease number from the form data or the API response after move-in completion.🔧 Suggested approach
Fetch or derive the actual property name and lease number, then pass them to the modal:
<MoveInSuccessModal isOpen={isSuccessModalOpen} onClose={() => setIsSuccessModalOpen(false)} onBackToLease={() => { setIsSuccessModalOpen(false); }} onRequestSignature={() => { setIsSuccessModalOpen(false); }} - propertyName="abc" - leaseNumber="9" + propertyName={/* derive from store or API response */} + leaseNumber={/* derive from API response after move-in */} />You may need to store the created lease details in state after the move-in API call completes.
🤖 Fix all issues with AI agents
In @src/pages/Dashboard/features/MoveIn/steps/MoveInDeposit.tsx:
- Around line 9-17: The selection state in MoveInDeposit is not initialized from
the move-in store, so previous hasDeposit values are not reflected when
returning to this step; update MoveInDeposit to read the current hasDeposit from
useMoveInStore on mount (and subscribe to changes) and set selection to 'yes' if
hasDeposit is true, 'no' if false, or null if undefined; implement this by
deriving initial state from the store or adding a useEffect that calls
setSelection based on the store's hasDeposit, leaving handleSelect (which calls
setDeposit and onNext) unchanged.
In @src/pages/Dashboard/features/MoveIn/steps/MoveInRecurringRentSettings.tsx:
- Around line 28-33: The amount state uses formData.recurringRent.amount || ''
which treats 0 as falsy and wipes zero values; update the initialization of
amount in the useState call (amount, setAmount) to use a string fallback of '0'
(i.e., formData.recurringRent.amount ?? '0' or formData.recurringRent.amount !==
undefined ? String(formData.recurringRent.amount) : '0') so zero is preserved
and matches the string type used with the number input; alternatively, convert
the amount state and related form handling to a numeric type throughout if you
prefer storing numbers instead of strings.
In @src/pages/Dashboard/features/MoveIn/steps/MoveInTenantSelection.tsx:
- Around line 38-48: You are calling useGetAllApplications and useGetAllTenants
twice which triggers duplicate queries; replace the duplicate calls by a single
destructuring for each hook that returns all needed fields (data, isLoading,
error) — e.g., from useGetAllApplications get { data: applications = [],
isLoading: isLoadingApplications, error: applicationsError } and from
useGetAllTenants get { data: backendTenants = [], isLoading: isLoadingTenants,
error: tenantsError }, then compute isLoading = isLoadingApplications ||
isLoadingTenants and error = applicationsError || tenantsError; remove the extra
calls so each hook is invoked only once.
In
@src/pages/Dashboard/features/Tenants/components/TenantApplicationsSection.tsx:
- Around line 55-66: The statusMap in TenantApplicationsSection.tsx currently
maps 'REVIEWING' to 'Pending' creating an inconsistency with
ApplicationDetail.tsx which uses 'In Review'; update the mapping in
TenantApplicationsSection.tsx (the statusMap used to derive status) to map
'REVIEWING' to 'In Review' and verify that Application.tsx and
ApplicationDetail.tsx use the exact same display string for 'REVIEWING' so all
application-related components share the same label; adjust any other mismatched
status entries across those components to the standardized display strings.
🧹 Nitpick comments (19)
src/pages/Dashboard/features/MoveIn/steps/MoveInBothLateFees.tsx (2)
36-52: Consider debouncing or consolidating store updates.The
useEffectsyncs local state to the store on every field change. While Zustand'ssetis optimized to only trigger updates on actual state changes, this pattern callssetLateFeesfrequently during user input. For forms with many fields, consider:
- Syncing only on blur/step navigation, or
- Using the store directly without local state intermediaries
The current approach works correctly but may cause more store updates than necessary.
♻️ Alternative: Direct store updates without local state
// Instead of local state + useEffect sync, update store directly: const MoveInBothLateFees: React.FC<MoveInBothLateFeesProps> = ({ onNext }) => { const { formData, setLateFees } = useMoveInStore(); const oneTimeFee = formData.lateFees.oneTimeFee; const dailyFee = formData.lateFees.dailyFee; const handleOneTimeTypeChange = (value: string) => { setLateFees({ oneTimeFee: { ...oneTimeFee, type: value } }); }; // ... similar handlers for other fields };This eliminates the sync layer but requires more handler functions.
30-31: Clarify the hardcoded grace period default.The
gracePeriodvariable is initialized from the store or defaults to'none', but it's declared asconstand never updated in the UI for "both" mode. Consider adding a comment explaining why the grace period is fixed in this mode, or expose it in the UI if users should be able to configure it.src/pages/Dashboard/features/MoveIn/steps/MoveInLateFeesType.tsx (1)
10-10: Consider destructuringonBackif it will be used, or removing it from the interface.The
onBackprop is defined in the interface but not destructured or used in the component. If back navigation isn't needed here, consider removing it fromMoveInLateFeesTypePropsfor clarity.src/pages/Dashboard/features/MoveIn/steps/MoveInRecurringRent.tsx (1)
11-11: Theselectionlocal state is effectively unused.Since
onNext(enabled)is called immediately aftersetSelection(value), the component navigates away before the selection state visually affects the UI. The state update on line 14 serves no practical purpose.Consider removing the
selectionstate if it's not needed for visual feedback before navigation:♻️ Suggested simplification
const MoveInRecurringRent: React.FC<MoveInRecurringRentProps> = ({ onNext }) => { const { setRecurringRent } = useMoveInStore(); - const [selection, setSelection] = useState<'yes' | 'no' | null>(null); const handleSelect = (value: 'yes' | 'no') => { - setSelection(value); const enabled = value === 'yes'; setRecurringRent({ enabled }); onNext(enabled); };If visual feedback is intended before navigation, consider adding a brief delay or transition.
Also applies to: 14-14
src/pages/Dashboard/features/MoveIn/steps/MoveInLateFees.tsx (1)
9-56: Consider extracting a shared component for Yes/No selection steps.This component is nearly identical to
MoveInRecurringRent.tsx- both present Yes/No options with the same styling, update different store slices, and callonNext. Consider creating a reusableYesNoSelectionStepcomponent to reduce duplication.♻️ Example abstraction
// Shared component concept interface YesNoSelectionStepProps { title: string; description: string; onSelect: (enabled: boolean) => void; } const YesNoSelectionStep: React.FC<YesNoSelectionStepProps> = ({ title, description, onSelect }) => { // Shared Yes/No button UI }; // Usage in MoveInLateFees const MoveInLateFees = ({ onNext }) => { const { setLateFees } = useMoveInStore(); return ( <YesNoSelectionStep title="Do you want to enable automatic Late fees?" description="The system will automatically generate..." onSelect={(enabled) => { setLateFees({ enabled }); onNext(enabled); }} /> ); };src/pages/Dashboard/features/MoveIn/steps/MoveInRecurringRentSettings.tsx (2)
36-45: Redundant store update —useEffectalready syncs state on every change.The
useEffecton lines 36-45 updates the store whenever any local field changes. The explicitsetRecurringRentcall in theonClickhandler (lines 143-150) is redundant since the store is already up-to-date by the time the user clicks "Next".♻️ Remove duplicate store update
<button - onClick={() => { - setRecurringRent({ - amount, - invoiceSchedule, - startOn, - endOn, - isMonthToMonth, - markPastPaid, - }); - onNext(); - }} + onClick={onNext} className="px-12 py-3 rounded-lg font-medium text-white transition-all bg-[#3D7475] hover:bg-[#2c5554] shadow-md hover:shadow-lg transform hover:-translate-y-0.5" >Also applies to: 142-152
36-45: Consider updating the store only on submit instead of on every field change.Syncing to the store via
useEffecton every keystroke/change is chatty and may cause unnecessary re-renders in other components subscribed toformData.recurringRent. A more efficient pattern is to keep local state during editing and commit to the store only when the user clicks "Next".♻️ Alternative: Update store only on submit
- // Update store when local state changes - useEffect(() => { - setRecurringRent({ - amount, - invoiceSchedule, - startOn, - endOn, - isMonthToMonth, - markPastPaid, - }); - }, [amount, invoiceSchedule, startOn, endOn, isMonthToMonth, markPastPaid, setRecurringRent]); // ... rest of component ... <button onClick={() => { + setRecurringRent({ + amount, + invoiceSchedule, + startOn, + endOn, + isMonthToMonth, + markPastPaid, + }); onNext(); }}This approach commits changes atomically and avoids intermediate store updates.
src/pages/Dashboard/features/MoveIn/steps/MoveInDailyLateFees.tsx (2)
6-9: UnusedonBackprop in interface.The
onBackprop is declared inMoveInDailyLateFeesPropsbut is not destructured or used in the component (line 11 only destructuresonNext). Consider removing it from the interface if it's not needed, or add it to the destructuring if navigation back is required.
23-34: Store sync on mount may overwrite existing data.The
useEffectruns immediately on mount and will callsetLateFeeswith current local state values. IfexistingDailyFeeisundefinedon initial render but gets populated shortly after (e.g., due to async store hydration), the defaults will overwrite the intended values.Consider either:
- Adding a guard to skip the initial sync if data hasn't changed
- Using a ref to track if initial data was loaded from the store
♻️ Suggested approach using initialization guard
+ const [isInitialized, setIsInitialized] = useState(false); + // Update store when values change useEffect(() => { + if (!isInitialized) { + setIsInitialized(true); + return; + } setLateFees({ dailyFee: { type: lateFeeType, amount, maxMonthlyBalance, gracePeriod, time, }, }); - }, [lateFeeType, amount, maxMonthlyBalance, gracePeriod, time, setLateFees]); + }, [lateFeeType, amount, maxMonthlyBalance, gracePeriod, time, setLateFees, isInitialized]);src/pages/Dashboard/features/MoveIn/steps/MoveInOneTimeLateFees.tsx (2)
6-9: UnusedonBackprop in interface.Same as in
MoveInDailyLateFees.tsx, theonBackprop is declared but not used. Consider removing it for consistency.
22-32: Same store sync concern on mount.This component has the same pattern as
MoveInDailyLateFeeswhere theuseEffectfires on mount and may overwrite store data. Consider applying the same initialization guard pattern across all late fee components for consistency.src/pages/Dashboard/features/Application/components/ApplicationCard.tsx (3)
106-127: Menu items have inconsistent typing.The
menuItemsarray items have inconsistent properties (colorvsisDestructive). The type assertion on line 163 ((item as any).color) is a symptom of this inconsistency.♻️ Suggested type-safe approach
+interface MenuItem { + label: string; + action: () => void; + color?: string; + isDestructive?: boolean; +} - const menuItems = [ + const menuItems: MenuItem[] = [ ...(backendStatus !== 'APPROVED' ? [{ label: 'Approve', action: () => setConfirmModal({ type: 'approve', isOpen: true }), color: 'text-green-600' }] : []), // ... rest of items ];Then update line 163:
- : (item as any).color || 'text-gray-700 hover:bg-gray-50' + : item.color ? `${item.color} hover:bg-gray-50` : 'text-gray-700 hover:bg-gray-50'
84-86: Avoid usingalert()for error feedback.Using
alert()blocks the UI thread and provides poor user experience. Consider using a toast notification or inline error message instead.♻️ Consider using a toast or state-based error display
} catch (error) { console.error('Failed to update application status:', error); - alert('Failed to update application status. Please try again.'); + // Consider using a toast library or local error state + // Example with local state: + // setErrorMessage('Failed to update application status. Please try again.'); } finally {
219-275: Confirmation modals implementation is functional but could be DRY.The three confirmation modals share similar structure with only different colors, titles, and messages. Consider extracting the modal configuration to reduce repetition, though the current implementation works correctly.
src/pages/Dashboard/features/MoveIn/steps/MoveInTenantSelection.tsx (2)
134-159: Contact book tenant status mapping may miss values.The status mapping only handles
PENDINGandACCEPTEDexplicitly, defaulting everything else toDeclined. If the backend introduces new status values, they'll incorrectly show asDeclined.♻️ Consider explicit status handling
- status: tenant.contactBookEntry?.status === 'PENDING' - ? 'Pending' - : tenant.contactBookEntry?.status === 'ACCEPTED' - ? 'Accepted' - : 'Declined', + status: (() => { + const s = tenant.contactBookEntry?.status; + if (s === 'PENDING') return 'Pending'; + if (s === 'ACCEPTED') return 'Accepted'; + if (s === 'DECLINED') return 'Declined'; + return undefined; // Unknown status + })(),
264-269: Empty state message exposes internal property ID.When no tenants are found, the message includes
${propertyId}which may be a UUID or internal identifier. Consider showing a user-friendly property name instead.♻️ Suggested improvement
{propertyId - ? `No approved applications found for this property. Please approve an application for property ${propertyId} first.` + ? 'No approved applications found for this property. Please approve an application first.' : 'No tenants found. Select a property first or add tenants to your contact book.'}src/pages/Dashboard/features/MoveIn/steps/MoveInDepositSettings.tsx (2)
36-43: Same store sync concern on mount as other components.The
useEffectpattern here matches the late fee components and has the same potential issue of overwriting store data on mount. Consider applying the initialization guard pattern consistently across all move-in step components.
104-111: Redundant store update beforeonNext.The
setDepositcall here is redundant since theuseEffecton lines 37-43 already syncs state changes to the store. The explicit call is harmless but unnecessary.♻️ Simplified onClick handler
onClick={() => { - setDeposit({ - category: selectedCategory, - amount, - invoiceDate, - }); onNext(); }}src/pages/Dashboard/features/MoveIn/MoveIn.tsx (1)
287-306: Consider adding a fallback for step 10 whenscheduleTypeis unset.If
formData.lateFees.scheduleTypeisundefinedor has an unexpected value when the user reaches step 10, none of the three conditions will match and nothing will render, potentially leaving users stuck with a blank screen.💡 Suggested approach
Add a fallback case after the existing conditions:
{currentStep === 10 && formData.lateFees.scheduleType === 'both' && ( <MoveInBothLateFees onNext={handleCompleteMoveIn} onBack={handleBack} /> )} +{currentStep === 10 && !['one-time', 'daily', 'both'].includes(formData.lateFees.scheduleType) && ( + <div className="text-center text-gray-500"> + Please go back and select a late fee type. + </div> +)}Alternatively, guard against this in
handleLateFeesTypeNextby preventing navigation to step 10 unless a valid type is selected.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (19)
index.htmlsrc/pages/Dashboard/features/Application/Application.tsxsrc/pages/Dashboard/features/Application/ApplicationDetail.tsxsrc/pages/Dashboard/features/Application/components/ApplicationCard.tsxsrc/pages/Dashboard/features/MoveIn/MoveIn.tsxsrc/pages/Dashboard/features/MoveIn/steps/MoveInBothLateFees.tsxsrc/pages/Dashboard/features/MoveIn/steps/MoveInDailyLateFees.tsxsrc/pages/Dashboard/features/MoveIn/steps/MoveInDeposit.tsxsrc/pages/Dashboard/features/MoveIn/steps/MoveInDepositSettings.tsxsrc/pages/Dashboard/features/MoveIn/steps/MoveInLateFees.tsxsrc/pages/Dashboard/features/MoveIn/steps/MoveInLateFeesType.tsxsrc/pages/Dashboard/features/MoveIn/steps/MoveInOneTimeLateFees.tsxsrc/pages/Dashboard/features/MoveIn/steps/MoveInPropertySelection.tsxsrc/pages/Dashboard/features/MoveIn/steps/MoveInRecurringRent.tsxsrc/pages/Dashboard/features/MoveIn/steps/MoveInRecurringRentSettings.tsxsrc/pages/Dashboard/features/MoveIn/steps/MoveInTenantSelection.tsxsrc/pages/Dashboard/features/MoveIn/store/moveInStore.tssrc/pages/Dashboard/features/Tenants/components/TenantApplicationsSection.tsxsrc/services/application.service.ts
🧰 Additional context used
🧬 Code graph analysis (14)
src/pages/Dashboard/features/Application/Application.tsx (1)
src/hooks/useApplicationQueries.ts (1)
useUpdateApplication(75-88)
src/pages/Dashboard/features/MoveIn/steps/MoveInDeposit.tsx (1)
src/pages/Dashboard/features/MoveIn/store/moveInStore.ts (1)
useMoveInStore(108-178)
src/pages/Dashboard/features/MoveIn/steps/MoveInDepositSettings.tsx (1)
src/pages/Dashboard/features/MoveIn/store/moveInStore.ts (1)
useMoveInStore(108-178)
src/pages/Dashboard/features/MoveIn/steps/MoveInPropertySelection.tsx (1)
src/pages/Dashboard/features/MoveIn/store/moveInStore.ts (1)
useMoveInStore(108-178)
src/pages/Dashboard/features/MoveIn/steps/MoveInRecurringRent.tsx (1)
src/pages/Dashboard/features/MoveIn/store/moveInStore.ts (1)
useMoveInStore(108-178)
src/pages/Dashboard/features/MoveIn/steps/MoveInOneTimeLateFees.tsx (1)
src/pages/Dashboard/features/MoveIn/store/moveInStore.ts (1)
useMoveInStore(108-178)
src/pages/Dashboard/features/MoveIn/steps/MoveInBothLateFees.tsx (1)
src/pages/Dashboard/features/MoveIn/store/moveInStore.ts (1)
useMoveInStore(108-178)
src/pages/Dashboard/features/MoveIn/steps/MoveInRecurringRentSettings.tsx (1)
src/pages/Dashboard/features/MoveIn/store/moveInStore.ts (1)
useMoveInStore(108-178)
src/pages/Dashboard/features/MoveIn/MoveIn.tsx (1)
src/pages/Dashboard/features/MoveIn/store/moveInStore.ts (1)
useMoveInStore(108-178)
src/pages/Dashboard/features/MoveIn/store/moveInStore.ts (1)
src/services/application.service.ts (1)
create(566-603)
src/pages/Dashboard/features/MoveIn/steps/MoveInDailyLateFees.tsx (1)
src/pages/Dashboard/features/MoveIn/store/moveInStore.ts (1)
useMoveInStore(108-178)
src/pages/Dashboard/features/MoveIn/steps/MoveInLateFeesType.tsx (1)
src/pages/Dashboard/features/MoveIn/store/moveInStore.ts (1)
useMoveInStore(108-178)
src/pages/Dashboard/features/MoveIn/steps/MoveInTenantSelection.tsx (6)
src/services/tenant.service.ts (3)
Tenant(78-84)BackendTenantProfile(4-32)tenantService(618-618)src/pages/Dashboard/features/MoveIn/store/moveInStore.ts (1)
useMoveInStore(108-178)src/hooks/useApplicationQueries.ts (1)
useGetAllApplications(17-26)src/hooks/useTenantQueries.ts (1)
useGetAllTenants(22-31)src/services/application.service.ts (1)
BackendApplication(6-63)src/context/AuthContext.tsx (1)
User(3-6)
src/pages/Dashboard/features/MoveIn/steps/MoveInLateFees.tsx (1)
src/pages/Dashboard/features/MoveIn/store/moveInStore.ts (1)
useMoveInStore(108-178)
🔇 Additional comments (23)
index.html (1)
10-10: LGTM!The title update from "SmartTenantAI-frontend" to "SmartTenantAI" is a sensible branding improvement—users shouldn't see internal project naming conventions in their browser tabs.
src/pages/Dashboard/features/Application/ApplicationDetail.tsx (1)
548-550: Good backward compatibility handling for legacy status values.The explicit mapping of legacy status values (
UNDER_REVIEW→ 'In Review',WITHDRAWN→ 'Cancelled') ensures older data renders correctly alongside the new status vocabulary (REVIEWING/CANCELLED). The inline comment clearly documents the intent.src/services/application.service.ts (2)
10-10: Type definition updated to reflect new status vocabulary.The status type change from
UNDER_REVIEW/WITHDRAWNtoREVIEWING/CANCELLEDaligns with the backend model updates. Ensure the backend API is deployed with these status values before this frontend change goes live, or existing data with old status values may cause TypeScript runtime issues when strict type checking is enabled.
173-173: DTO status type consistently updated.The
CreateApplicationDto.statustype update matches theBackendApplication.statuschange, maintaining consistency between request and response types.src/pages/Dashboard/features/MoveIn/store/moveInStore.ts (3)
1-11: Well-structured Zustand store for move-in form state.The store is cleanly organized with clear interface definitions. The use of
Date | undefinedfor date fields works well for in-memory state, but be aware that if you later add persistence middleware (e.g.,zustand/persist), you'll need to handle Date serialization/deserialization.
60-77: Comprehensive action interface for state management.The action signatures are well-designed:
setFormDatasupports both partial objects and callback patterns for flexibility- Dedicated setters for nested objects (
setRecurringRent,setDeposit,setLateFees) enable partial updates without replacing the entire object
108-178: Immutable state updates correctly implemented.All setters properly spread the existing state before applying updates, ensuring immutability. The nested object setters (e.g.,
setRecurringRent,setDeposit,setLateFees) correctly merge partial updates with existing nested state.src/pages/Dashboard/features/MoveIn/steps/MoveInBothLateFees.tsx (1)
11-18: Good migration to store-based state management.The component now correctly reads from the centralized store, eliminating the need for prop drilling. The initialization from existing store values (
existingOneTimeFee,existingDailyFee) ensures form state is preserved when navigating back to this step.src/pages/Dashboard/features/MoveIn/steps/MoveInLateFeesType.tsx (1)
3-3: LGTM! Clean integration with the centralized store.The handler correctly updates the store before invoking
onNext, ensuring the selected late fee type is persisted informData.lateFees.scheduleType.Also applies to: 10-16
src/pages/Dashboard/features/MoveIn/steps/MoveInRecurringRent.tsx (1)
2-2: LGTM! Store integration follows the established pattern.The handler correctly persists the recurring rent enabled state to the store before advancing.
Also applies to: 10-10, 13-18
src/pages/Dashboard/features/MoveIn/steps/MoveInLateFees.tsx (1)
2-2: LGTM! Consistent store integration pattern.The late fees enabled state is correctly persisted to the store before navigation.
Also applies to: 10-10, 13-18
src/pages/Dashboard/features/MoveIn/steps/MoveInPropertySelection.tsx (1)
4-4: LGTM! Clean migration to store-backed property selection.The component now correctly derives
selectedPropertyIdfrom the store and updates it viasetPropertyId. This aligns well with the centralized state management pattern used across the move-in flow.Also applies to: 13-14, 41-44
src/pages/Dashboard/features/MoveIn/steps/MoveInDailyLateFees.tsx (1)
51-183: LGTM on the UI rendering and summary text generation.The descriptive text logic correctly handles the different fee types (fixed, outstanding, recurring) and formats currency appropriately using
Intl.NumberFormat. The grace period calculation and date formatting are well implemented.src/pages/Dashboard/features/MoveIn/steps/MoveInOneTimeLateFees.tsx (1)
11-21: LGTM on store integration and state initialization.The state initialization correctly reads from
existingOneTimeFeeand the field mapping between local state (gracePeriod) and store field (gracePeriodDays) is handled properly.src/pages/Dashboard/features/Application/Application.tsx (4)
26-35: Status mapping updated correctly for new backend terminology.The status map now correctly handles
REVIEWINGandCANCELLEDstatuses, mapping them to the UI-friendlyPendingstatus. This aligns with the PR objectives.
202-212: Status change handler properly re-throws errors.The
handleStatusChangefunction correctly catches errors, logs them, and re-throws to allowApplicationCardto display user feedback. This is good error propagation practice.
389-394: Placeholder callback in InviteToApplyModal.The
onSendcallback only logs success messages. Verify if additional side effects (e.g., refreshing data, showing toasts) should be implemented here. The comment suggests the modal handles the API call directly.
351-362: LGTM on explicit prop passing to ApplicationCard.Explicit prop passing is cleaner than object spreading and makes the component's dependencies clearer. All required props are properly passed including the new
onStatusChangeandonMoveInhandlers.src/pages/Dashboard/features/MoveIn/steps/MoveInDepositSettings.tsx (1)
28-34: LGTM on store integration pattern.The component correctly reads initial values from the store and maintains local state for form inputs. This pattern is consistent with other move-in step components.
src/pages/Dashboard/features/MoveIn/steps/MoveInTenantSelection.tsx (1)
98-107: Backend integration required for placeholder tenantId format.The
email:${primaryApplicant.email}format is used as a placeholder when a tenant is not found in the contact book (line 104), but the backend moveIn API endpoint appears to be missing from the codebase. Ensure the backend supports resolving this email-based tenant ID format, or modify the frontend to require an actual tenant profile before allowing submission.src/pages/Dashboard/features/MoveIn/MoveIn.tsx (3)
77-84: LGTM!The Zustand store integration is clean. All destructured state and actions are used appropriately throughout the component.
88-93: LGTM!The pre-selection effect correctly reads from navigation state and updates the store. Including
setPropertyIdin the dependency array follows exhaustive-deps rules, and since Zustand actions are stable references, this won't cause unnecessary re-runs.
199-216: LGTM!The scenario selection cards and button disabled state correctly derive from the Zustand store. Optional chaining handles the initial undefined state safely.
| const MoveInDeposit: React.FC<MoveInDepositProps> = ({ onNext }) => { | ||
| const { setDeposit } = useMoveInStore(); | ||
| const [selection, setSelection] = useState<'yes' | 'no' | null>(null); | ||
|
|
||
| const handleSelect = (value: 'yes' | 'no') => { | ||
| setSelection(value); | ||
| onNext(value === 'yes'); | ||
| const hasDeposit = value === 'yes'; | ||
| setDeposit({ hasDeposit }); | ||
| onNext(hasDeposit); |
There was a problem hiding this comment.
Selection state not initialized from store.
The selection state starts as null but doesn't read the existing hasDeposit value from the store. If a user navigates back to this step, their previous selection won't be visually reflected.
🐛 Proposed fix
const MoveInDeposit: React.FC<MoveInDepositProps> = ({ onNext }) => {
- const { setDeposit } = useMoveInStore();
- const [selection, setSelection] = useState<'yes' | 'no' | null>(null);
+ const { formData, setDeposit } = useMoveInStore();
+ const [selection, setSelection] = useState<'yes' | 'no' | null>(() => {
+ if (formData.deposit.hasDeposit === true) return 'yes';
+ if (formData.deposit.hasDeposit === false) return 'no';
+ return null;
+ });🤖 Prompt for AI Agents
In @src/pages/Dashboard/features/MoveIn/steps/MoveInDeposit.tsx around lines 9 -
17, The selection state in MoveInDeposit is not initialized from the move-in
store, so previous hasDeposit values are not reflected when returning to this
step; update MoveInDeposit to read the current hasDeposit from useMoveInStore on
mount (and subscribe to changes) and set selection to 'yes' if hasDeposit is
true, 'no' if false, or null if undefined; implement this by deriving initial
state from the store or adding a useEffect that calls setSelection based on the
store's hasDeposit, leaving handleSelect (which calls setDeposit and onNext)
unchanged.
| const [invoiceSchedule, setInvoiceSchedule] = useState(formData.recurringRent.invoiceSchedule || 'Monthly'); | ||
| const [startOn, setStartOn] = useState<Date | undefined>(formData.recurringRent.startOn); | ||
| const [endOn, setEndOn] = useState<Date | undefined>(formData.recurringRent.endOn); | ||
| const [isMonthToMonth, setIsMonthToMonth] = useState(formData.recurringRent.isMonthToMonth || false); | ||
| const [markPastPaid, setMarkPastPaid] = useState(formData.recurringRent.markPastPaid || false); | ||
| const [amount, setAmount] = useState(formData.recurringRent.amount || ''); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, let's find the file and understand its context
find . -type f -name "MoveInRecurringRentSettings.tsx" | head -5Repository: scalixity-dev/PMS_Frontend
Length of output: 145
🏁 Script executed:
# Search for type definitions related to formData and recurringRent
rg -t typescript "recurringRent\s*:" --max-count 20Repository: scalixity-dev/PMS_Frontend
Length of output: 104
🏁 Script executed:
# Search for the component file to understand the full context
rg -t typescript "formData\.recurringRent\.amount" -A 3 -B 3Repository: scalixity-dev/PMS_Frontend
Length of output: 104
🏁 Script executed:
# Look for type definitions of the form data
rg -t typescript "interface.*FormData|type.*FormData" -A 10Repository: scalixity-dev/PMS_Frontend
Length of output: 104
🏁 Script executed:
# Search for how amount is used in this component
rg -t typescript "amount" src/pages/Dashboard/features/MoveIn/steps/MoveInRecurringRentSettings.tsx -A 2 -B 2Repository: scalixity-dev/PMS_Frontend
Length of output: 104
🏁 Script executed:
# Search for recurringRent type definitions without type restriction
rg "recurringRent\s*:" --max-count 20Repository: scalixity-dev/PMS_Frontend
Length of output: 437
🏁 Script executed:
# Look for FormData or form data type definitions
rg "interface.*FormData|type.*FormData" -A 10Repository: scalixity-dev/PMS_Frontend
Length of output: 50382
🏁 Script executed:
# Read the specific file to understand the context
wc -l src/pages/Dashboard/features/MoveIn/steps/MoveInRecurringRentSettings.tsxRepository: scalixity-dev/PMS_Frontend
Length of output: 147
🏁 Script executed:
# Read the relevant section of the file
cat -n src/pages/Dashboard/features/MoveIn/steps/MoveInRecurringRentSettings.tsx | head -80Repository: scalixity-dev/PMS_Frontend
Length of output: 4191
🏁 Script executed:
# Search for amount usage in the component file specifically
rg "amount" src/pages/Dashboard/features/MoveIn/steps/MoveInRecurringRentSettings.tsx -B 2 -A 2Repository: scalixity-dev/PMS_Frontend
Length of output: 1157
🏁 Script executed:
# Find the moveInStore.ts file and search for RecurringRentData type definition
cat src/pages/Dashboard/features/MoveIn/store/moveInStore.ts | head -100Repository: scalixity-dev/PMS_Frontend
Length of output: 2634
🏁 Script executed:
# Search for RecurringRentData type definition
rg "RecurringRentData|interface.*Recurring" -A 10Repository: scalixity-dev/PMS_Frontend
Length of output: 7791
🏁 Script executed:
# Check if amount value of 0 is ever handled or validated
rg "amount.*0|validate.*amount" src/pages/Dashboard/features/MoveIn/Repository: scalixity-dev/PMS_Frontend
Length of output: 1623
Use consistent string type for amount with appropriate fallback value.
The amount is initialized as formData.recurringRent.amount || '', but the type is defined as string and the input uses type="number". If amount is ever 0 (numeric), the || '' fallback will treat it as falsy and reset to empty string, causing data loss. Change the fallback to '0' to maintain type consistency and prevent zero values from being lost. Alternatively, consider storing amount as a number type throughout the form data structure.
- const [amount, setAmount] = useState(formData.recurringRent.amount || '');
+ const [amount, setAmount] = useState(formData.recurringRent.amount || '0');📝 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 [invoiceSchedule, setInvoiceSchedule] = useState(formData.recurringRent.invoiceSchedule || 'Monthly'); | |
| const [startOn, setStartOn] = useState<Date | undefined>(formData.recurringRent.startOn); | |
| const [endOn, setEndOn] = useState<Date | undefined>(formData.recurringRent.endOn); | |
| const [isMonthToMonth, setIsMonthToMonth] = useState(formData.recurringRent.isMonthToMonth || false); | |
| const [markPastPaid, setMarkPastPaid] = useState(formData.recurringRent.markPastPaid || false); | |
| const [amount, setAmount] = useState(formData.recurringRent.amount || ''); | |
| const [invoiceSchedule, setInvoiceSchedule] = useState(formData.recurringRent.invoiceSchedule || 'Monthly'); | |
| const [startOn, setStartOn] = useState<Date | undefined>(formData.recurringRent.startOn); | |
| const [endOn, setEndOn] = useState<Date | undefined>(formData.recurringRent.endOn); | |
| const [isMonthToMonth, setIsMonthToMonth] = useState(formData.recurringRent.isMonthToMonth || false); | |
| const [markPastPaid, setMarkPastPaid] = useState(formData.recurringRent.markPastPaid || false); | |
| const [amount, setAmount] = useState(formData.recurringRent.amount || '0'); |
🤖 Prompt for AI Agents
In @src/pages/Dashboard/features/MoveIn/steps/MoveInRecurringRentSettings.tsx
around lines 28 - 33, The amount state uses formData.recurringRent.amount || ''
which treats 0 as falsy and wipes zero values; update the initialization of
amount in the useState call (amount, setAmount) to use a string fallback of '0'
(i.e., formData.recurringRent.amount ?? '0' or formData.recurringRent.amount !==
undefined ? String(formData.recurringRent.amount) : '0') so zero is preserved
and matches the string type used with the number input; alternatively, convert
the amount state and related form handling to a numeric type throughout if you
prefer storing numbers instead of strings.
…n.tsx Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/pages/Dashboard/features/MoveIn/steps/MoveInTenantSelection.tsx (1)
243-246: Minor grammatical issue in placeholder text."Search a Tenants" should be "Search Tenants" or "Search for a Tenant".
Suggested fix
{!selectedTenant ? ( <> - <span className="text-gray-500">Search a Tenants</span> + <span className="text-gray-500">Search for a Tenant</span> </>
🤖 Fix all issues with AI agents
In @src/pages/Dashboard/features/MoveIn/steps/MoveInTenantSelection.tsx:
- Around line 269-274: The empty-state message in component
MoveInTenantSelection currently interpolates the raw propertyId (likely a UUID);
change the JSX to avoid showing propertyId directly by using a human-friendly
property name (e.g., property?.name or a prop/propertyName passed into
MoveInTenantSelection) and fall back to a generic phrase if the name is
unavailable; update the ternary that renders the message to use property?.name
(or propertyName) instead of propertyId and ensure the fallback string still
reads naturally when no property data exists.
🧹 Nitpick comments (4)
src/pages/Dashboard/features/MoveIn/steps/MoveInTenantSelection.tsx (4)
23-30: UnusedonBackprop.The
onBackprop is declared inMoveInTenantSelectionPropsbut not destructured or used in the component. Either remove it from the interface or implement the back functionality.Option 1: Remove if not needed
interface MoveInTenantSelectionProps { onNext: () => void; - onBack: () => void; } const MoveInTenantSelection: React.FC<MoveInTenantSelectionProps> = ({ - onNext + onNext, }) => {Option 2: Use it if needed
const MoveInTenantSelection: React.FC<MoveInTenantSelectionProps> = ({ - onNext + onNext, + onBack, }) => {
140-164: Use proper type instead ofanyand improve status mapping.The
tenant: anytype annotation loses type safety. Additionally, the status mapping defaults to'Declined'for any status that isn't'PENDING'or'ACCEPTED', which could be incorrect if the status is missing or has other values.Suggested improvement
const contactBookTenants: Tenant[] = backendTenants - .filter((tenant: any) => { + .filter((tenant: BackendTenantProfile) => { const tenantEmail = tenant.user?.email || tenant.contactBookEntry?.email; return tenantEmail && !applicationEmails.has(tenantEmail.toLowerCase()); }) - .map((tenant: any) => { + .map((tenant: BackendTenantProfile) => { const transformed = tenantService.transformTenant(tenant); const hasUserAccount = !!tenant.userId; + + // Map contact book status + const contactStatus = tenant.contactBookEntry?.status; + let status: Tenant['status']; + if (contactStatus === 'PENDING') { + status = 'Pending'; + } else if (contactStatus === 'ACCEPTED') { + status = 'Accepted'; + } else if (contactStatus === 'DECLINED' || contactStatus === 'REJECTED') { + status = 'Declined'; + } else { + status = undefined; // Unknown status + } return { id: tenant.userId || tenant.id, tenantProfileId: tenant.id, name: transformed.name, email: transformed.email, phone: transformed.phone, image: transformed.image, - status: tenant.contactBookEntry?.status === 'PENDING' - ? 'Pending' - : tenant.contactBookEntry?.status === 'ACCEPTED' - ? 'Accepted' - : 'Declined', + status, hasUserAccount, source: 'contactBook' as const, }; });
173-184: Consider using a ref to track initial pre-selection.The effect depends on
tenantswhich changes when the memoized array is recreated. While the!selectedTenantIdguard prevents re-selection, using a ref to track whether pre-selection was already attempted would be more explicit and avoid unnecessary effect executions.Optional improvement
+ const preSelectionAttempted = useRef(false); + // Pre-select tenant from application if email is provided in navigation state useEffect(() => { + if (preSelectionAttempted.current) return; + const state = location.state as { preSelectedTenantEmail?: string } | null; - if (state?.preSelectedTenantEmail && !selectedTenantId && tenants.length > 0) { + if (state?.preSelectedTenantEmail && tenants.length > 0) { // Find tenant by email const tenantByEmail = tenants.find( t => t.email.toLowerCase() === state.preSelectedTenantEmail!.toLowerCase() ); if (tenantByEmail) { setTenantId(tenantByEmail.id); + preSelectionAttempted.current = true; } } - }, [location.state, tenants, selectedTenantId, setTenantId]); + }, [location.state, tenants, setTenantId]);
214-225: Consider adding a retry option for error state.The error UI shows the message but doesn't provide a way to retry. Consider adding a retry button that calls
refetchfrom the query hooks to improve user experience.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/pages/Dashboard/features/MoveIn/steps/MoveInTenantSelection.tsx
🧰 Additional context used
🧬 Code graph analysis (1)
src/pages/Dashboard/features/MoveIn/steps/MoveInTenantSelection.tsx (4)
src/services/tenant.service.ts (3)
Tenant(78-84)BackendTenantProfile(4-32)tenantService(618-618)src/hooks/useApplicationQueries.ts (1)
useGetAllApplications(17-26)src/hooks/useTenantQueries.ts (1)
useGetAllTenants(22-31)src/services/application.service.ts (1)
BackendApplication(6-63)
🔇 Additional comments (1)
src/pages/Dashboard/features/MoveIn/steps/MoveInTenantSelection.tsx (1)
56-129: Approve the application filtering and tenant transformation logic.The logic for filtering approved applications by property and transforming them to tenant objects is well-structured. Good defensive checks for missing leasing data, property data, and applicants. The email matching with contact book tenants for deduplication is a solid approach.
| } else { | ||
| // Not in contact book yet - applicant from approved application | ||
| // Since they submitted an application, they should have a user account | ||
| // We'll use the applicant email to find/create tenant profile in backend | ||
| // For now, use a placeholder that backend can resolve by email | ||
| // The backend moveInTenant can look up by email if needed | ||
| tenantId = `email:${primaryApplicant.email}`; // Backend will need to resolve this | ||
| hasUserAccount = true; // Assume they have account since they submitted application | ||
| tenantProfileId = undefined; | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
rg -n "email:" --type ts --type tsx -g '!*.test.*' | head -50Repository: scalixity-dev/PMS_Frontend
Length of output: 97
🏁 Script executed:
# Also search for how tenantId is used throughout the codebase
rg -n "tenantId" --type ts --type tsx -g '!*.test.*' -A 2 -B 2 | head -100Repository: scalixity-dev/PMS_Frontend
Length of output: 97
🏁 Script executed:
# Check if there are any references to email-based resolution in the codebase or comments
rg -n "resolve.*email|email.*resolve" --type ts --type tsx -g '!*.test.*' -i | head -30Repository: scalixity-dev/PMS_Frontend
Length of output: 97
🏁 Script executed:
# Look for backend-related code or API that might handle tenant IDs
rg -n "moveInTenant|tenant.*backend" --type ts --type tsx -g '!*.test.*' -i | head -30Repository: scalixity-dev/PMS_Frontend
Length of output: 97
🏁 Script executed:
rg -n "email:" -g "*.ts" -g "*.tsx" | head -50Repository: scalixity-dev/PMS_Frontend
Length of output: 4643
🏁 Script executed:
# Search for tenantId patterns and how they're used
rg -n "tenantId" -g "*.ts" -g "*.tsx" -A 2 -B 2 | head -100Repository: scalixity-dev/PMS_Frontend
Length of output: 6337
🏁 Script executed:
# Look for moveInTenant or backend API calls
rg -n "moveInTenant|tenant.*backend|backend.*tenant" -g "*.ts" -g "*.tsx" -i | head -30Repository: scalixity-dev/PMS_Frontend
Length of output: 3743
🏁 Script executed:
# Check the actual file to understand context around line 109
cat -n src/pages/Dashboard/features/MoveIn/steps/MoveInTenantSelection.tsx | sed -n '100,120p'Repository: scalixity-dev/PMS_Frontend
Length of output: 1422
🏁 Script executed:
# Search for where tenantsFromApplications is used
rg -n "tenantsFromApplications" -g "*.ts" -g "*.tsx" -A 3 -B 3Repository: scalixity-dev/PMS_Frontend
Length of output: 2557
🏁 Script executed:
# Search for MoveIn related API calls or services
rg -n "moveIn|MoveIn" -g "*.ts" -g "*.tsx" | grep -i "api\|service\|request\|endpoint"Repository: scalixity-dev/PMS_Frontend
Length of output: 1517
🏁 Script executed:
# Check what happens with tenantId in the MoveInTenantSelection component
cat -n src/pages/Dashboard/features/MoveIn/steps/MoveInTenantSelection.tsx | sed -n '1,50p'Repository: scalixity-dev/PMS_Frontend
Length of output: 2464
🏁 Script executed:
# Look for any backend service calls related to MoveIn
find . -name "*.ts" -o -name "*.tsx" | xargs grep -l "moveIn\|MoveIn" | head -20Repository: scalixity-dev/PMS_Frontend
Length of output: 1446
🏁 Script executed:
# Find how selectedTenant is used after selection and any API calls with tenantId
cat -n src/pages/Dashboard/features/MoveIn/steps/MoveInTenantSelection.tsx | sed -n '170,250p'Repository: scalixity-dev/PMS_Frontend
Length of output: 4138
🏁 Script executed:
# Check the moveInStore to see how tenantId is used
rg -n "tenantId" src/pages/Dashboard/features/MoveIn/store/moveInStore.ts -A 3 -B 3Repository: scalixity-dev/PMS_Frontend
Length of output: 1320
🏁 Script executed:
# Search for where the MoveIn data is submitted (likely in a later step)
rg -n "useMoveInStore\|setTenantId" -g "*.ts" -g "*.tsx" --type-listRepository: scalixity-dev/PMS_Frontend
Length of output: 459
🏁 Script executed:
# Look for any MoveIn form submission or API call
find ./src/pages/Dashboard/features/MoveIn -name "*.ts" -o -name "*.tsx" | xargs grep -n "api\|service\|request\|axios\|fetch" | head -40Repository: scalixity-dev/PMS_Frontend
Length of output: 1017
🏁 Script executed:
# Find where moveInStore.formData is submitted
rg -n "formData\|submit\|mutation" src/pages/Dashboard/features/MoveIn/MoveIn.tsx -A 2 -B 2Repository: scalixity-dev/PMS_Frontend
Length of output: 52
🏁 Script executed:
# Check the main MoveIn component to see the flow
cat -n src/pages/Dashboard/features/MoveIn/MoveIn.tsx | head -100Repository: scalixity-dev/PMS_Frontend
Length of output: 5007
🏁 Script executed:
# Look for lease service or any API that creates lease with tenant
rg -n "createLease\|lease.*mutation\|lease.*create" -g "*.ts" -g "*.tsx" -iRepository: scalixity-dev/PMS_Frontend
Length of output: 52
🏁 Script executed:
# Check if tenantId is ever parsed or validated before use
rg -n "email:" -g "*.ts" -g "*.tsx" -B 5 -A 5 | head -80Repository: scalixity-dev/PMS_Frontend
Length of output: 4596
🏁 Script executed:
# Search for lease creation or submit handler
rg -n "handleSubmit\|createLease\|saveLease\|success\|submit" src/pages/Dashboard/features/MoveIn/MoveIn.tsx -B 3 -A 5Repository: scalixity-dev/PMS_Frontend
Length of output: 52
🏁 Script executed:
# Look at the entire MoveIn.tsx to find where formData is used
cat -n src/pages/Dashboard/features/MoveIn/MoveIn.tsx | sed -n '100,250p'Repository: scalixity-dev/PMS_Frontend
Length of output: 7343
🏁 Script executed:
# Search for any lease service or API integration in MoveIn
find src/pages/Dashboard/features/MoveIn -type f \( -name "*.ts" -o -name "*.tsx" \) -exec grep -l "leaseService\|createLease\|mutation\|useMutation" {} \;Repository: scalixity-dev/PMS_Frontend
Length of output: 52
🏁 Script executed:
# Check the entire tenant.service to understand the full API contract
cat -n src/services/tenant.service.ts | head -150Repository: scalixity-dev/PMS_Frontend
Length of output: 4539
🏁 Script executed:
# Check the MoveInSuccessModal for actual submission
cat -n src/pages/Dashboard/features/MoveIn/components/MoveInSuccessModal.tsxRepository: scalixity-dev/PMS_Frontend
Length of output: 12149
🏁 Script executed:
# Search for any lease service or API in the entire codebase
rg -n "lease" src/services -i | head -50Repository: scalixity-dev/PMS_Frontend
Length of output: 1275
🏁 Script executed:
# Check if there's a separate lease/moveIn service
find src -name "*lease*" -o -name "*moveIn*" | grep -i serviceRepository: scalixity-dev/PMS_Frontend
Length of output: 52
🏁 Script executed:
# Check the leasing service to understand lease creation contract
cat -n src/services/leasing.service.ts | head -250Repository: scalixity-dev/PMS_Frontend
Length of output: 8770
🏁 Script executed:
# Search for where MoveIn formData is actually used/submitted
rg -n "moveInStore\|formData" src/pages/Dashboard/features/MoveIn -A 3 | grep -i "submit\|save\|create\|post\|mutation\|api" | head -30Repository: scalixity-dev/PMS_Frontend
Length of output: 52
🏁 Script executed:
# Check if there's any actual mutation or API call in the MoveIn flow
rg -n "useMutation\|useQuery\|axios\|fetch" src/pages/Dashboard/features/MoveInRepository: scalixity-dev/PMS_Frontend
Length of output: 52
Address fragile tenant ID convention that doesn't match backend contract.
When no matching tenant profile exists, the code creates an email:${primaryApplicant.email} placeholder ID and assumes hasUserAccount = true. This introduces critical issues:
- The
email:xxxformat is a non-standard convention with no backend counterpart. The backendCreateTenantProfileDtoexpectsemailas a separate field with an optionaluserId, not as an ID format. - The email-prefixed string will fail when passed to any tenant API that expects a standard ID (UUID or numeric).
- The assumption that all approved applicants have user accounts lacks validation and may not hold in all cases.
Either extract and pass the email separately with an empty/null tenantId for backend resolution, or implement explicit validation before storing this placeholder format.
| {tenants.length === 0 ? ( | ||
| <div className="p-4 text-center text-gray-500 text-sm"> | ||
| {propertyId | ||
| ? `No approved applications found for this property. Please approve an application for property ${propertyId} first.` | ||
| : 'No tenants found. Select a property first or add tenants to your contact book.'} | ||
| </div> |
There was a problem hiding this comment.
Avoid displaying raw propertyId in user-facing message.
The empty state message includes the raw propertyId, which is likely a UUID and not meaningful to users. Consider showing the property name instead, or simplify the message.
Suggested fix
{tenants.length === 0 ? (
<div className="p-4 text-center text-gray-500 text-sm">
{propertyId
- ? `No approved applications found for this property. Please approve an application for property ${propertyId} first.`
+ ? 'No approved applications found for this property. Please approve an application first.'
: 'No tenants found. Select a property first or add tenants to your contact book.'}
</div>📝 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.
| {tenants.length === 0 ? ( | |
| <div className="p-4 text-center text-gray-500 text-sm"> | |
| {propertyId | |
| ? `No approved applications found for this property. Please approve an application for property ${propertyId} first.` | |
| : 'No tenants found. Select a property first or add tenants to your contact book.'} | |
| </div> | |
| {tenants.length === 0 ? ( | |
| <div className="p-4 text-center text-gray-500 text-sm"> | |
| {propertyId | |
| ? 'No approved applications found for this property. Please approve an application first.' | |
| : 'No tenants found. Select a property first or add tenants to your contact book.'} | |
| </div> |
🤖 Prompt for AI Agents
In @src/pages/Dashboard/features/MoveIn/steps/MoveInTenantSelection.tsx around
lines 269 - 274, The empty-state message in component MoveInTenantSelection
currently interpolates the raw propertyId (likely a UUID); change the JSX to
avoid showing propertyId directly by using a human-friendly property name (e.g.,
property?.name or a prop/propertyName passed into MoveInTenantSelection) and
fall back to a generic phrase if the name is unavailable; update the ternary
that renders the message to use property?.name (or propertyName) instead of
propertyId and ensure the fallback string still reads naturally when no property
data exists.
…w', 'Draft', 'Submitted', and 'Cancelled'. Adjusted related components to reflect these changes in the UI and ensure backward compatibility with existing status values.
… Updated application status handling, improved tenant selection logic, and integrated Zustand for state management. Adjusted UI components for better user experience and added support for new application statuses. Enhanced late fee settings and deposit management with improved data handling.
Summary by CodeRabbit
New Features
Bug Fixes
✏️ Tip: You can customize this high-level summary in your review settings.