condition checks in rental history in profile
This commit is contained in:
@@ -9,10 +9,10 @@ interface ConditionCheckViewerModalProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const checkTypeLabels: Record<string, string> = {
|
const checkTypeLabels: Record<string, string> = {
|
||||||
pre_rental_owner: "Pre-Rental Check (Owner)",
|
pre_rental_owner: "Pre-Rental Condition (Owner)",
|
||||||
rental_start_renter: "Rental Start Check (Renter)",
|
rental_start_renter: "Rental Start Condition (Renter)",
|
||||||
rental_end_renter: "Rental End Check (Renter)",
|
rental_end_renter: "Rental End Condition (Renter)",
|
||||||
post_rental_owner: "Post-Rental Check (Owner)",
|
post_rental_owner: "Post-Rental Condition (Owner)",
|
||||||
};
|
};
|
||||||
|
|
||||||
const ConditionCheckViewerModal: React.FC<ConditionCheckViewerModalProps> = ({
|
const ConditionCheckViewerModal: React.FC<ConditionCheckViewerModalProps> = ({
|
||||||
@@ -65,10 +65,13 @@ const ConditionCheckViewerModal: React.FC<ConditionCheckViewerModalProps> = ({
|
|||||||
? `${conditionCheck.submittedByUser.firstName} ${conditionCheck.submittedByUser.lastName}`
|
? `${conditionCheck.submittedByUser.firstName} ${conditionCheck.submittedByUser.lastName}`
|
||||||
: "Unknown";
|
: "Unknown";
|
||||||
|
|
||||||
const submittedDate = new Date(conditionCheck.submittedAt).toLocaleString(undefined, {
|
const submittedDate = new Date(conditionCheck.submittedAt).toLocaleString(
|
||||||
|
undefined,
|
||||||
|
{
|
||||||
dateStyle: "short",
|
dateStyle: "short",
|
||||||
timeStyle: "short",
|
timeStyle: "short",
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
// Filter to only valid string keys for display
|
// Filter to only valid string keys for display
|
||||||
const validImageKeys = (conditionCheck.imageFilenames || []).filter(
|
const validImageKeys = (conditionCheck.imageFilenames || []).filter(
|
||||||
@@ -76,7 +79,9 @@ const ConditionCheckViewerModal: React.FC<ConditionCheckViewerModalProps> = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const currentImageKey = validImageKeys[selectedImage];
|
const currentImageKey = validImageKeys[selectedImage];
|
||||||
const currentImageUrl = currentImageKey ? imageUrls.get(currentImageKey) : undefined;
|
const currentImageUrl = currentImageKey
|
||||||
|
? imageUrls.get(currentImageKey)
|
||||||
|
: undefined;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@@ -219,7 +224,11 @@ const ConditionCheckViewerModal: React.FC<ConditionCheckViewerModalProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="modal-footer">
|
<div className="modal-footer">
|
||||||
<button type="button" className="btn btn-secondary" onClick={onHide}>
|
<button
|
||||||
|
type="button"
|
||||||
|
className="btn btn-secondary"
|
||||||
|
onClick={onHide}
|
||||||
|
>
|
||||||
Close
|
Close
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
import React, { useState, useEffect, useCallback } from "react";
|
import React, { useState, useEffect, useCallback } from "react";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { useAuth } from "../contexts/AuthContext";
|
import { useAuth } from "../contexts/AuthContext";
|
||||||
import { userAPI, itemAPI, rentalAPI, addressAPI } from "../services/api";
|
import { userAPI, itemAPI, rentalAPI, addressAPI, conditionCheckAPI } from "../services/api";
|
||||||
import { User, Item, Rental, Address } from "../types";
|
import { User, Item, Rental, Address, ConditionCheck } from "../types";
|
||||||
import { uploadFile, getPublicImageUrl } from "../services/uploadService";
|
import { uploadFile, getPublicImageUrl } from "../services/uploadService";
|
||||||
import AvailabilitySettings from "../components/AvailabilitySettings";
|
import AvailabilitySettings from "../components/AvailabilitySettings";
|
||||||
import ReviewItemModal from "../components/ReviewModal";
|
import ReviewItemModal from "../components/ReviewModal";
|
||||||
import ReviewRenterModal from "../components/ReviewRenterModal";
|
import ReviewRenterModal from "../components/ReviewRenterModal";
|
||||||
import ReviewDetailsModal from "../components/ReviewDetailsModal";
|
import ReviewDetailsModal from "../components/ReviewDetailsModal";
|
||||||
|
import ConditionCheckViewerModal from "../components/ConditionCheckViewerModal";
|
||||||
import Avatar from "../components/Avatar";
|
import Avatar from "../components/Avatar";
|
||||||
import {
|
import {
|
||||||
geocodingService,
|
geocodingService,
|
||||||
@@ -114,6 +115,11 @@ const Profile: React.FC = () => {
|
|||||||
"renter" | "owner"
|
"renter" | "owner"
|
||||||
>("renter");
|
>("renter");
|
||||||
|
|
||||||
|
// Condition check state
|
||||||
|
const [conditionChecks, setConditionChecks] = useState<ConditionCheck[]>([]);
|
||||||
|
const [showConditionCheckViewer, setShowConditionCheckViewer] = useState(false);
|
||||||
|
const [selectedConditionCheck, setSelectedConditionCheck] = useState<ConditionCheck | null>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchProfile();
|
fetchProfile();
|
||||||
fetchStats();
|
fetchStats();
|
||||||
@@ -239,6 +245,63 @@ const Profile: React.FC = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const fetchConditionChecks = async (renterRentals: Rental[], ownerRentals: Rental[]) => {
|
||||||
|
try {
|
||||||
|
const allRentals = [...renterRentals, ...ownerRentals];
|
||||||
|
// Remove duplicates (a rental could appear in both if user is both renter and owner somehow)
|
||||||
|
const uniqueRentals = allRentals.filter(
|
||||||
|
(rental, index, self) => self.findIndex((r) => r.id === rental.id) === index
|
||||||
|
);
|
||||||
|
|
||||||
|
const allChecks: ConditionCheck[] = [];
|
||||||
|
for (const rental of uniqueRentals) {
|
||||||
|
try {
|
||||||
|
const response = await conditionCheckAPI.getConditionChecks(rental.id);
|
||||||
|
if (response.data.conditionChecks) {
|
||||||
|
allChecks.push(...response.data.conditionChecks);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
// Skip rentals with no condition checks
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setConditionChecks(allChecks);
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Failed to fetch condition checks:", err);
|
||||||
|
setConditionChecks([]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Fetch condition checks when rental history is loaded
|
||||||
|
useEffect(() => {
|
||||||
|
if (pastRenterRentals.length > 0 || pastOwnerRentals.length > 0) {
|
||||||
|
fetchConditionChecks(pastRenterRentals, pastOwnerRentals);
|
||||||
|
}
|
||||||
|
}, [pastRenterRentals, pastOwnerRentals]);
|
||||||
|
|
||||||
|
const getCompletedChecksForRental = (rentalId: string): ConditionCheck[] => {
|
||||||
|
return conditionChecks.filter((check) => check.rentalId === rentalId);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleViewConditionCheck = (check: ConditionCheck) => {
|
||||||
|
setSelectedConditionCheck(check);
|
||||||
|
setShowConditionCheckViewer(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getConditionCheckLabel = (checkType: string): string => {
|
||||||
|
switch (checkType) {
|
||||||
|
case "pre_rental_owner":
|
||||||
|
return "Pre-Rental Condition";
|
||||||
|
case "rental_start_renter":
|
||||||
|
return "Rental Start Condition";
|
||||||
|
case "rental_end_renter":
|
||||||
|
return "Rental End Condition";
|
||||||
|
case "post_rental_owner":
|
||||||
|
return "Post-Rental Condition";
|
||||||
|
default:
|
||||||
|
return "Condition Check";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handleReviewClick = (rental: Rental) => {
|
const handleReviewClick = (rental: Rental) => {
|
||||||
setSelectedRental(rental);
|
setSelectedRental(rental);
|
||||||
setShowReviewModal(true);
|
setShowReviewModal(true);
|
||||||
@@ -1274,6 +1337,24 @@ const Profile: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Condition Checks */}
|
||||||
|
{getCompletedChecksForRental(rental.id).length > 0 && (
|
||||||
|
<div className="mb-2">
|
||||||
|
{getCompletedChecksForRental(rental.id).map((check) => (
|
||||||
|
<button
|
||||||
|
key={`${rental.id}-${check.checkType}`}
|
||||||
|
className="btn btn-link text-success small p-0 text-decoration-none d-block"
|
||||||
|
onClick={() => handleViewConditionCheck(check)}
|
||||||
|
>
|
||||||
|
{getConditionCheckLabel(check.checkType)}
|
||||||
|
<small className="text-muted ms-2">
|
||||||
|
{new Date(check.createdAt).toLocaleDateString()}
|
||||||
|
</small>
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
<div className="d-flex gap-2 mt-3">
|
<div className="d-flex gap-2 mt-3">
|
||||||
{rental.status === "completed" &&
|
{rental.status === "completed" &&
|
||||||
!rental.itemRating &&
|
!rental.itemRating &&
|
||||||
@@ -1400,6 +1481,24 @@ const Profile: React.FC = () => {
|
|||||||
<strong>Total:</strong> ${rental.totalAmount}
|
<strong>Total:</strong> ${rental.totalAmount}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
{/* Condition Checks */}
|
||||||
|
{getCompletedChecksForRental(rental.id).length > 0 && (
|
||||||
|
<div className="mb-2">
|
||||||
|
{getCompletedChecksForRental(rental.id).map((check) => (
|
||||||
|
<button
|
||||||
|
key={`${rental.id}-${check.checkType}`}
|
||||||
|
className="btn btn-link text-success small p-0 text-decoration-none d-block"
|
||||||
|
onClick={() => handleViewConditionCheck(check)}
|
||||||
|
>
|
||||||
|
{getConditionCheckLabel(check.checkType)}
|
||||||
|
<small className="text-muted ms-2">
|
||||||
|
{new Date(check.createdAt).toLocaleDateString()}
|
||||||
|
</small>
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
<div className="d-flex gap-2 mt-3">
|
<div className="d-flex gap-2 mt-3">
|
||||||
{rental.status === "completed" &&
|
{rental.status === "completed" &&
|
||||||
!rental.renterRating &&
|
!rental.renterRating &&
|
||||||
@@ -1615,6 +1714,16 @@ const Profile: React.FC = () => {
|
|||||||
userType={reviewDetailsUserType}
|
userType={reviewDetailsUserType}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Condition Check Viewer Modal */}
|
||||||
|
<ConditionCheckViewerModal
|
||||||
|
show={showConditionCheckViewer}
|
||||||
|
onHide={() => {
|
||||||
|
setShowConditionCheckViewer(false);
|
||||||
|
setSelectedConditionCheck(null);
|
||||||
|
}}
|
||||||
|
conditionCheck={selectedConditionCheck}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user