Skip to content

feat: enhance service provider management with CRUD operations, docum… - #114

Merged
ojshav merged 2 commits into
mainfrom
ojshav
Dec 23, 2025
Merged

feat: enhance service provider management with CRUD operations, docum…#114
ojshav merged 2 commits into
mainfrom
ojshav

Conversation

@ojshav

@ojshav ojshav commented Dec 23, 2025

Copy link
Copy Markdown
Collaborator

…ent handling, and improved UI components

  • Added new API endpoints for service providers including create, update, delete, and fetch operations.
  • Implemented document upload and retrieval functionality for service providers.
  • Enhanced the AddEditServicePro component with country and state selection, image cropping, and improved form handling.
  • Updated ServicePros and ServiceProsDetail components to display loading states and error handling.
  • Refactored ServiceProCard and ServiceProProfileSection for better user experience and added document display features.

Summary by CodeRabbit

  • New Features

    • Full service‑provider management: create, read, update, delete, archive, and dedicated API endpoints.
    • Document upload/management with per-file validation and download/view actions.
    • Image upload + cropping workflow for profile photos.
    • Location (country/state/city) and phone/fax country‑code selectors.
  • UI/UX Improvements

    • Real-time data fetching for lists and details with loading/error states and retry.
    • Enhanced cards and profile layouts: status indicators, action menus, quick message action, and clearer empty states.

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

…ent handling, and improved UI components

- Added new API endpoints for service providers including create, update, delete, and fetch operations.
- Implemented document upload and retrieval functionality for service providers.
- Enhanced the AddEditServicePro component with country and state selection, image cropping, and improved form handling.
- Updated ServicePros and ServiceProsDetail components to display loading states and error handling.
- Refactored ServiceProCard and ServiceProProfileSection for better user experience and added document display features.
@coderabbitai

coderabbitai Bot commented Dec 23, 2025

Copy link
Copy Markdown
Contributor

Caution

Review failed

The pull request is closed.

Walkthrough

Adds a SERVICE_PROVIDER API group and a new ServiceProviderService (CRUD + documents). Updates ServicePros UI (list, detail, add/edit, card, profile section) to use live backend data, image cropping, country/state/city selectors, document uploads, and loading/error states.

Changes

Cohort / File(s) Summary
API Configuration
src/config/api.config.ts
Added SERVICE_PROVIDER endpoint group with routes: GET_ALL, GET_ONE, GET_BY_CATEGORY, CREATE, UPDATE, DELETE, UPLOAD_DOCUMENT, GET_DOCUMENTS, DELETE_DOCUMENT.
Service Layer
src/services/service-provider.service.ts
New ServiceProviderService with methods: create, getAll, getOne, getByCategory, update, delete, getDocuments. Adds CreateServiceProviderDto, UpdateServiceProviderDto, BackendServiceProvider, BackendServiceProviderDocument, and a singleton serviceProviderService. Uniform fetch/error handling throughout.
ServicePros List
src/pages/Dashboard/features/ServicePros/ServicePros.tsx
Replaced mock data with API-driven fetch (serviceProviderService.getAll), added loading/error states, mapping to ServiceProCardData, and formatting helpers (initials, phone, category).
ServicePros Detail
src/pages/Dashboard/features/ServicePros/ServiceProsDetail.tsx
Fetches provider by ID and documents, adds loading/error UI, delete/archive flows, disabled/loading actions, and maps backend shape to UI-friendly structure.
Add/Edit Form
src/pages/Dashboard/features/ServicePros/AddEditServicePro.tsx
Major form updates: country/state/city selectors, phone/fax country-code dropdowns, image upload + crop modal, document upload (per-file validation, size limits), validation, create/update via CreateServiceProviderDto, document upload post-create/update, loading/error handling and navigation.
Card Component
src/pages/Dashboard/features/ServicePros/components/ServiceProCard.tsx
UI restructuring: menu refactor, status indicator on avatar, phone display with icon, "View Profile" and new "Quick Message" button (stub), layout and style adjustments.
Profile Section
src/pages/Dashboard/features/ServicePros/components/ServiceProProfileSection.tsx
Component props extended to accept documents and isLoadingDocuments; renders attachments grid with view/download actions, loading spinner, and "No attachments" fallback.
Minor UI Update
src/pages/Dashboard/features/Tenants/Tenants.tsx
Removed visible label and chevron icon inside sort-toggle button; behavior unchanged.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant UI as ServicePros UI (List / Detail / Form)
    participant Svc as serviceProviderService (client)
    participant API as Backend API
    participant DB as Database

    rect rgb(230,240,250)
    Note over User,UI: List view flow
    User->>UI: Navigate to Service Pros list
    UI->>Svc: getAll(isActive=true)
    Svc->>API: GET /api/service-provider
    API->>DB: Query providers
    DB-->>API: Records
    API-->>Svc: JSON providers
    Svc-->>UI: BackendServiceProvider[]
    UI->>UI: Transform -> ServiceProCardData
    UI-->>User: Render cards
    end

    rect rgb(240,250,240)
    Note over User,UI: Create / Update flow with uploads
    User->>UI: Submit Add/Edit form (+profile image/documents)
    UI->>UI: Validate, optionally crop image
    UI->>Svc: create/update(CreateServiceProviderDto)
    Svc->>API: POST/PUT /api/service-provider
    API->>DB: Insert/Update provider
    DB-->>API: Provider record (id)
    API-->>Svc: BackendServiceProvider
    Svc-->>UI: Success (provider id)
    UI->>Svc: upload documents (per file) using UPLOAD_DOCUMENT
    loop per file
      Svc->>API: POST /api/service-provider/{id}/documents
      API->>DB: Store document metadata
      DB-->>API: Acknowledged
      API-->>Svc: Stored document
    end
    Svc-->>UI: All documents uploaded
    UI-->>User: Navigate to list / show success
    end

    rect rgb(250,240,240)
    Note over User,UI: Delete flow
    User->>UI: Trigger delete on Detail
    UI->>Svc: delete(id)
    Svc->>API: DELETE /api/service-provider/{id}
    API->>DB: Delete/archive
    DB-->>API: Acknowledged
    API-->>Svc: { message }
    Svc-->>UI: Success
    UI-->>User: Confirm and navigate away
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately reflects the main changes: adding CRUD operations for service provider management, document handling, and UI enhancements across multiple components.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

📜 Recent review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2950e00 and 0ec5dc3.

📒 Files selected for processing (1)
  • src/pages/Dashboard/features/ServicePros/AddEditServicePro.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: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
src/pages/Dashboard/features/Tenants/Tenants.tsx (1)

136-141: Sort button appears empty — likely unintended change.

The button retains its onClick handler and styling but renders no visible content. This makes the sort functionality inaccessible to users. Was this intentional, or should the label/icon be restored?

src/pages/Dashboard/features/ServicePros/components/ServiceProCard.tsx (1)

44-48: Menu actions for "Send connection" and "Delete" are empty stubs.

These menu items have action: () => { } which does nothing when clicked. This could confuse users who expect the actions to work.

Would you like me to help implement these handlers or add TODO comments to track this work?

src/pages/Dashboard/features/ServicePros/ServicePros.tsx (1)

182-187: Sort button is empty — same issue as Tenants.tsx.

The button has onClick but no visible content, making the sort functionality inaccessible.

🧹 Nitpick comments (10)
src/pages/Dashboard/features/ServicePros/ServiceProsDetail.tsx (3)

9-40: Consider extracting shared helper functions to reduce duplication.

These helper functions (getInitials, formatPhoneNumber, formatCategory, formatAddress) are duplicated in ServicePros.tsx. Consider extracting them to a shared utility module (e.g., utils/service-provider.utils.ts) to maintain DRY principles.


82-88: Avoid any types for better type safety.

Using any for servicePro and documents loses the benefits of TypeScript. Consider defining explicit types:

🔎 Suggested type improvements
+interface TransformedServicePro {
+    id: string;
+    initials: string;
+    name: string;
+    phone: string;
+    email: string;
+    outstanding: number;
+    deposits: number;
+    credits: number;
+    image?: string;
+    personalInfo: { /* ... fields */ };
+    forwardingAddress: string;
+}
+
-const [servicePro, setServicePro] = useState<any>(null);
+const [servicePro, setServicePro] = useState<TransformedServicePro | null>(null);
-const [documents, setDocuments] = useState<any[]>([]);
+const [documents, setDocuments] = useState<BackendServiceProviderDocument[]>([]);

150-165: Archive handler updates local state without backend confirmation.

After calling serviceProviderService.update(), the code optimistically sets local state without verifying the response reflects isActive: false. Consider using the returned data to update state:

🔎 Suggested improvement
 const handleArchive = async () => {
     if (!id || !servicePro) return;
     
     setIsArchiving(true);
     try {
-        await serviceProviderService.update(id, { isActive: false });
-        setServicePro({ ...servicePro, isActive: false });
+        const updated = await serviceProviderService.update(id, { isActive: false });
+        // Re-transform the updated data to ensure consistency
+        setServicePro(transformServiceProvider(updated));
         setIsArchiveModalOpen(false);
     } catch (err) {
src/pages/Dashboard/features/ServicePros/components/ServiceProCard.tsx (1)

105-107: Status indicator is hardcoded to green.

The green dot always renders regardless of the service provider's actual status. Consider passing an isActive prop and conditionally styling:

🔎 Suggested approach
 interface ServiceProCardProps {
     id: string | number;
     initials: string;
     name: string;
     phone: string;
     category: string;
     bgColor?: string;
     image?: string;
+    isActive?: boolean;
 }
-<div className="absolute bottom-0 right-0 w-4 h-4 bg-green-500 border-2 border-white rounded-full"></div>
+<div className={`absolute bottom-0 right-0 w-4 h-4 ${isActive !== false ? 'bg-green-500' : 'bg-gray-400'} border-2 border-white rounded-full`}></div>
src/pages/Dashboard/features/ServicePros/components/ServiceProProfileSection.tsx (1)

4-7: Type inconsistency: servicePro.id is number but documents use string IDs.

The BackendServiceProvider interface from the service layer uses id: string. Consider aligning the servicePro.id type:

 servicePro: {
-    id: number;
+    id: string;
     name: string;
src/pages/Dashboard/features/ServicePros/ServicePros.tsx (1)

26-47: Duplicate helper functions across files.

getInitials, formatPhoneNumber, and formatCategory are duplicated in ServiceProsDetail.tsx. Extract these to a shared utility module.

src/services/service-provider.service.ts (3)

62-104: Consider extracting the error handling logic to reduce duplication.

The error parsing pattern is repeated identically in every method. Extract to a helper:

🔎 Suggested refactor
private async handleResponse<T>(response: Response, defaultError: string): Promise<T> {
    if (!response.ok) {
        let errorMessage = defaultError;
        try {
            const errorData = await response.json();
            if (Array.isArray(errorData.message)) {
                errorMessage = errorData.message.join('. ');
            } else if (errorData.message) {
                errorMessage = errorData.message;
            } else if (errorData.error) {
                errorMessage = errorData.error;
            }
            console.error(`${defaultError}:`, { status: response.status, statusText: response.statusText, errorData });
        } catch (parseError) {
            errorMessage = `${defaultError}: ${response.statusText}`;
            console.error('Failed to parse error response:', parseError);
        }
        throw new Error(errorMessage);
    }
    return response.json();
}

114-120: GET requests don't require Content-Type: application/json header.

The Content-Type header is only meaningful for requests with a body. While harmless, removing it for GET requests aligns with HTTP semantics.


325-368: Missing uploadDocument and deleteDocument methods.

The API endpoints for UPLOAD_DOCUMENT and DELETE_DOCUMENT are defined in api.config.ts, but corresponding service methods are not implemented. The AddEditServicePro.tsx component implements uploadDocument inline.

Would you like me to generate the missing service methods to centralize document operations?

src/pages/Dashboard/features/ServicePros/AddEditServicePro.tsx (1)

127-156: UseEffects that reset child fields may cause unintended state resets.

These effects reset state/city whenever dependencies change, including on initial mount or when formData is set during edit mode fetch. This could clear valid data loaded from the backend.

Consider using a ref to track whether it's the initial load:

🔎 Suggested approach
const isInitialMount = useRef(true);

useEffect(() => {
    if (formData.address.country) {
        setStates(State.getStatesOfCountry(formData.address.country));
        // Only reset if not initial mount
        if (!isInitialMount.current) {
            setFormData(prev => ({
                ...prev,
                address: { ...prev.address, state: '', city: '' }
            }));
        }
    } else {
        setStates([]);
    }
}, [formData.address.country]);

// Set isInitialMount.current = false after initial data load in fetchServiceProvider
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5fe4ba3 and 2950e00.

📒 Files selected for processing (8)
  • src/config/api.config.ts
  • src/pages/Dashboard/features/ServicePros/AddEditServicePro.tsx
  • src/pages/Dashboard/features/ServicePros/ServicePros.tsx
  • src/pages/Dashboard/features/ServicePros/ServiceProsDetail.tsx
  • src/pages/Dashboard/features/ServicePros/components/ServiceProCard.tsx
  • src/pages/Dashboard/features/ServicePros/components/ServiceProProfileSection.tsx
  • src/pages/Dashboard/features/Tenants/Tenants.tsx
  • src/services/service-provider.service.ts
🧰 Additional context used
🧬 Code graph analysis (5)
src/pages/Dashboard/features/ServicePros/components/ServiceProCard.tsx (1)
src/pages/basewebsite/resources/sections/resourceIcons.tsx (1)
  • Phone (465-486)
src/pages/Dashboard/features/ServicePros/ServiceProsDetail.tsx (1)
src/services/service-provider.service.ts (2)
  • BackendServiceProvider (36-60)
  • serviceProviderService (368-368)
src/pages/Dashboard/features/ServicePros/ServicePros.tsx (1)
src/services/service-provider.service.ts (2)
  • serviceProviderService (368-368)
  • BackendServiceProvider (36-60)
src/services/service-provider.service.ts (1)
src/config/api.config.ts (1)
  • API_ENDPOINTS (4-107)
src/pages/Dashboard/features/ServicePros/AddEditServicePro.tsx (2)
src/services/service-provider.service.ts (2)
  • serviceProviderService (368-368)
  • CreateServiceProviderDto (4-23)
src/config/api.config.ts (1)
  • API_ENDPOINTS (4-107)
🔇 Additional comments (8)
src/config/api.config.ts (1)

96-106: LGTM!

The new SERVICE_PROVIDER endpoints follow the established pattern consistently with other entities (TENANT, PROPERTY, etc.). URL structure and parameterization are correct.

src/pages/Dashboard/features/ServicePros/ServiceProsDetail.tsx (1)

91-127: Good implementation of data fetching with graceful document error handling.

The approach of not failing the entire page when documents fail to load (lines 111-114) is a solid resilience pattern.

src/pages/Dashboard/features/ServicePros/components/ServiceProProfileSection.tsx (2)

114-121: The download attribute may not trigger downloads for cross-origin URLs.

If doc.fileUrl points to an external domain (e.g., S3), browsers may ignore the download attribute due to CORS restrictions. Consider adding a backend proxy endpoint or informing users via tooltip.


76-133: Good implementation of conditional rendering for document states.

The loading spinner, document grid, and empty state placeholder are well-structured. The document cards have clean layout with accessible actions.

src/pages/Dashboard/features/ServicePros/ServicePros.tsx (1)

49-78: Good data fetching implementation with proper error handling.

The fetch logic properly handles loading states, transforms data, and provides meaningful error messages.

src/pages/Dashboard/features/ServicePros/AddEditServicePro.tsx (3)

490-506: Good form validation with clear required field checking.

The validation covers all essential fields and scrolls to top on error for visibility.


1174-1185: Good implementation of image crop modal integration.

The modal is conditionally rendered with proper props and handlers for cancel/complete flows.


240-306: No action needed—the dependency is correctly memoized.

The phoneCountryCodes variable is wrapped in useMemo with an empty dependency array [], which means it returns the same reference on every render. Including it in the useEffect dependency array is the correct pattern and will not cause unnecessary re-fetches. The memoization effectively prevents the stale closure concern.

Likely an incorrect or invalid review comment.

Comment thread src/pages/Dashboard/features/ServicePros/AddEditServicePro.tsx
…ponent

- Introduced rollback mechanism for new service providers if document upload fails.
- Improved error messaging to inform users about the status of service provider creation and document upload.
- Added logic to handle cleanup of service provider in case of upload failure, ensuring better user experience.
@ojshav
ojshav merged commit 0676a8d into main Dec 23, 2025
1 check was pending
@coderabbitai coderabbitai Bot mentioned this pull request Mar 17, 2026
@coderabbitai coderabbitai Bot mentioned this pull request Apr 16, 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