Skip to content

Refactor Move-In process and enhance application management features.… - #166

Merged
ojshav merged 4 commits into
mainfrom
ojshav
Jan 13, 2026
Merged

Refactor Move-In process and enhance application management features.…#166
ojshav merged 4 commits into
mainfrom
ojshav

Conversation

@ojshav

@ojshav ojshav commented Jan 13, 2026

Copy link
Copy Markdown
Collaborator

… 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

    • Application cards now show applicant email; status actions include Approve / In Review / Decline with confirmation prompts.
    • Move‑in flow uses a centralized form state: supports pre‑selection, store‑driven steps, and improved tenant selection from approved applications.
    • "Move In" action pre-fills move‑in flow with property and tenant info.
  • Bug Fixes

    • Unified status values to REVIEWING/CANCELLED while preserving legacy label mappings for display.

✏️ Tip: You can customize this high-level summary in your review settings.

… 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.
@coderabbitai

coderabbitai Bot commented Jan 13, 2026

Copy link
Copy Markdown
Contributor

Caution

Review failed

The pull request is closed.

📝 Walkthrough

Walkthrough

Centralized 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

Cohort / File(s) Summary
HTML Meta
index.html
Updated page title from SmartTenantAI-frontend to SmartTenantAI.
Type & Service Updates
src/services/application.service.ts
Replaced backend status literals: UNDER_REVIEWREVIEWING, WITHDRAWNCANCELLED on BackendApplication and CreateApplicationDto.
Application Dashboard & Mutations
src/pages/Dashboard/features/Application/Application.tsx
Added useUpdateApplication usage, handleStatusChange, handleMoveIn, expose applicantEmail in transformed card data, and switched to explicit ApplicationCard props (no spread).
Application Card UI
src/pages/Dashboard/features/Application/components/ApplicationCard.tsx
Expanded ApplicationCardProps (added backendStatus, propertyId, applicantEmail, onStatusChange, onMoveIn, extended status union), added confirm-modals, updating state, conditional menu items including Move In, and status color logic.
Application Detail & Tenant Mapping
src/pages/Dashboard/features/Application/ApplicationDetail.tsx, src/pages/Dashboard/features/Tenants/components/TenantApplicationsSection.tsx
Restored/added backward-compatible status mappings so legacy values (UNDER_REVIEW, WITHDRAWN) render as new labels (In Review, Cancelled) while supporting new keys.
Move-In Central Store
src/pages/Dashboard/features/MoveIn/store/moveInStore.ts
New Zustand store useMoveInStore with typed interfaces (RecurringRentData, DepositData, LateFeesData, MoveInFormData) and setters (setPropertyId, setTenantId, setRecurringRent, setDeposit, setLateFees, resetForm, etc.).
Move-In Form Root
src/pages/Dashboard/features/MoveIn/MoveIn.tsx
Replaced local state with store-driven state (formData, currentStep, setPropertyId, setSelectedScenario) and updated step navigation to use store.
Move-In Step Components (store migration)
src/pages/Dashboard/features/MoveIn/steps/*
Migrated many step components to read/write useMoveInStore (removed prop-based values like recurringRentAmount, selectedPropertyId, onSelect where applicable), added useEffect syncs, and adjusted onNext signatures where data is persisted to store before advancing.
Tenant Selection Enhancements
src/pages/Dashboard/features/MoveIn/steps/MoveInTenantSelection.tsx
Replaced static tenants with dynamic fetches (useGetAllApplications, useGetAllTenants), merged/deduped tenants by email, added loading/error UI and status/source badges, and switched to store-based tenant selection.

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
Loading
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
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Poem

🐰🌿
I hopped into the store today — what a treat!
Forms hold hands and data flows neat.
Tenants lined up, statuses shine bright,
Modals cheer on each approving light.
Hooray — move-ins now leap just right!

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: refactoring the Move-In process and enhancing application management features, which aligns with the significant modifications across application status handling, Move-In form state management, and related UI components.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

📜 Recent review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d6ee586 and d20ed4f.

📒 Files selected for processing (3)
  • src/pages/Dashboard/features/Application/Application.tsx
  • src/pages/Dashboard/features/Application/components/ApplicationCard.tsx
  • src/pages/Dashboard/features/Tenants/components/TenantApplicationsSection.tsx

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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" and leaseNumber="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 useEffect syncs local state to the store on every field change. While Zustand's set is optimized to only trigger updates on actual state changes, this pattern calls setLateFees frequently during user input. For forms with many fields, consider:

  1. Syncing only on blur/step navigation, or
  2. 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 gracePeriod variable is initialized from the store or defaults to 'none', but it's declared as const and 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 destructuring onBack if it will be used, or removing it from the interface.

The onBack prop is defined in the interface but not destructured or used in the component. If back navigation isn't needed here, consider removing it from MoveInLateFeesTypeProps for clarity.

src/pages/Dashboard/features/MoveIn/steps/MoveInRecurringRent.tsx (1)

11-11: The selection local state is effectively unused.

Since onNext(enabled) is called immediately after setSelection(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 selection state 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 call onNext. Consider creating a reusable YesNoSelectionStep component 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 — useEffect already syncs state on every change.

The useEffect on lines 36-45 updates the store whenever any local field changes. The explicit setRecurringRent call in the onClick handler (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 useEffect on every keystroke/change is chatty and may cause unnecessary re-renders in other components subscribed to formData.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: Unused onBack prop in interface.

The onBack prop is declared in MoveInDailyLateFeesProps but is not destructured or used in the component (line 11 only destructures onNext). 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 useEffect runs immediately on mount and will call setLateFees with current local state values. If existingDailyFee is undefined on initial render but gets populated shortly after (e.g., due to async store hydration), the defaults will overwrite the intended values.

Consider either:

  1. Adding a guard to skip the initial sync if data hasn't changed
  2. 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: Unused onBack prop in interface.

Same as in MoveInDailyLateFees.tsx, the onBack prop 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 MoveInDailyLateFees where the useEffect fires 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 menuItems array items have inconsistent properties (color vs isDestructive). 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 using alert() 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 PENDING and ACCEPTED explicitly, defaulting everything else to Declined. If the backend introduces new status values, they'll incorrectly show as Declined.

♻️ 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 useEffect pattern 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 before onNext.

The setDeposit call here is redundant since the useEffect on 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 when scheduleType is unset.

If formData.lateFees.scheduleType is undefined or 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 handleLateFeesTypeNext by preventing navigation to step 10 unless a valid type is selected.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5c91c29 and 24c013d.

📒 Files selected for processing (19)
  • index.html
  • src/pages/Dashboard/features/Application/Application.tsx
  • src/pages/Dashboard/features/Application/ApplicationDetail.tsx
  • src/pages/Dashboard/features/Application/components/ApplicationCard.tsx
  • src/pages/Dashboard/features/MoveIn/MoveIn.tsx
  • src/pages/Dashboard/features/MoveIn/steps/MoveInBothLateFees.tsx
  • src/pages/Dashboard/features/MoveIn/steps/MoveInDailyLateFees.tsx
  • src/pages/Dashboard/features/MoveIn/steps/MoveInDeposit.tsx
  • src/pages/Dashboard/features/MoveIn/steps/MoveInDepositSettings.tsx
  • src/pages/Dashboard/features/MoveIn/steps/MoveInLateFees.tsx
  • src/pages/Dashboard/features/MoveIn/steps/MoveInLateFeesType.tsx
  • src/pages/Dashboard/features/MoveIn/steps/MoveInOneTimeLateFees.tsx
  • src/pages/Dashboard/features/MoveIn/steps/MoveInPropertySelection.tsx
  • src/pages/Dashboard/features/MoveIn/steps/MoveInRecurringRent.tsx
  • src/pages/Dashboard/features/MoveIn/steps/MoveInRecurringRentSettings.tsx
  • src/pages/Dashboard/features/MoveIn/steps/MoveInTenantSelection.tsx
  • src/pages/Dashboard/features/MoveIn/store/moveInStore.ts
  • src/pages/Dashboard/features/Tenants/components/TenantApplicationsSection.tsx
  • src/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/WITHDRAWN to REVIEWING/CANCELLED aligns 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.status type update matches the BackendApplication.status change, 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 | undefined for 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:

  • setFormData supports 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 in formData.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 selectedPropertyId from the store and updates it via setPropertyId. 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 existingOneTimeFee and 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 REVIEWING and CANCELLED statuses, mapping them to the UI-friendly Pending status. This aligns with the PR objectives.


202-212: Status change handler properly re-throws errors.

The handleStatusChange function correctly catches errors, logs them, and re-throws to allow ApplicationCard to display user feedback. This is good error propagation practice.


389-394: Placeholder callback in InviteToApplyModal.

The onSend callback 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 onStatusChange and onMoveIn handlers.

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 setPropertyId in 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.

Comment on lines 9 to +17
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);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

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.

Comment on lines +28 to +33
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 || '');

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, let's find the file and understand its context
find . -type f -name "MoveInRecurringRentSettings.tsx" | head -5

Repository: 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 20

Repository: 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 3

Repository: 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 10

Repository: 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 2

Repository: scalixity-dev/PMS_Frontend

Length of output: 104


🏁 Script executed:

# Search for recurringRent type definitions without type restriction
rg "recurringRent\s*:" --max-count 20

Repository: scalixity-dev/PMS_Frontend

Length of output: 437


🏁 Script executed:

# Look for FormData or form data type definitions
rg "interface.*FormData|type.*FormData" -A 10

Repository: 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.tsx

Repository: 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 -80

Repository: 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 2

Repository: 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 -100

Repository: scalixity-dev/PMS_Frontend

Length of output: 2634


🏁 Script executed:

# Search for RecurringRentData type definition
rg "RecurringRentData|interface.*Recurring" -A 10

Repository: 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.

Suggested change
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.

Comment thread src/pages/Dashboard/features/MoveIn/steps/MoveInTenantSelection.tsx
…n.tsx

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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: Unused onBack prop.

The onBack prop is declared in MoveInTenantSelectionProps but 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 of any and improve status mapping.

The tenant: any type 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 tenants which changes when the memoized array is recreated. While the !selectedTenantId guard 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 refetch from the query hooks to improve user experience.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 24c013d and d6ee586.

📒 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.

Comment on lines +103 to +112
} 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;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

rg -n "email:" --type ts --type tsx -g '!*.test.*' | head -50

Repository: 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 -100

Repository: 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 -30

Repository: 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 -30

Repository: scalixity-dev/PMS_Frontend

Length of output: 97


🏁 Script executed:

rg -n "email:" -g "*.ts" -g "*.tsx" | head -50

Repository: 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 -100

Repository: 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 -30

Repository: 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 3

Repository: 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 -20

Repository: 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 3

Repository: 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-list

Repository: 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 -40

Repository: 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 2

Repository: 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 -100

Repository: 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" -i

Repository: 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 -80

Repository: 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 5

Repository: 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 -150

Repository: 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.tsx

Repository: 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 -50

Repository: 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 service

Repository: 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 -250

Repository: 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 -30

Repository: 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/MoveIn

Repository: 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:

  1. The email:xxx format is a non-standard convention with no backend counterpart. The backend CreateTenantProfileDto expects email as a separate field with an optional userId, not as an ID format.
  2. The email-prefixed string will fail when passed to any tenant API that expects a standard ID (UUID or numeric).
  3. 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.

Comment on lines +269 to +274
{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>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

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.

Suggested change
{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.
@ojshav
ojshav merged commit 8e46b61 into main Jan 13, 2026
1 check was pending
@coderabbitai coderabbitai Bot mentioned this pull request Apr 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant