date selection
This commit is contained in:
@@ -435,7 +435,7 @@ const CreateItem: React.FC = () => {
|
||||
onPeriodsChange={(periods) =>
|
||||
setFormData(prev => ({ ...prev, unavailablePeriods: periods }))
|
||||
}
|
||||
priceType={priceType}
|
||||
mode="owner"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -546,7 +546,7 @@ const EditItem: React.FC = () => {
|
||||
const userPeriods = periods.filter(p => !p.isAcceptedRental);
|
||||
setFormData(prev => ({ ...prev, unavailablePeriods: userPeriods }));
|
||||
}}
|
||||
priceType={priceType}
|
||||
mode="owner"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -18,6 +18,9 @@ const ItemDetail: React.FC = () => {
|
||||
|
||||
useEffect(() => {
|
||||
fetchItem();
|
||||
}, [id]);
|
||||
|
||||
useEffect(() => {
|
||||
if (user) {
|
||||
checkIfAlreadyRenting();
|
||||
}
|
||||
@@ -207,7 +210,7 @@ const ItemDetail: React.FC = () => {
|
||||
|
||||
<ItemReviews itemId={item.id} />
|
||||
|
||||
<div className="d-flex gap-2">
|
||||
<div className="d-flex gap-2 mb-5">
|
||||
{isOwner ? (
|
||||
<button className="btn btn-primary" onClick={handleEdit}>
|
||||
Edit Listing
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Link, useNavigate } from 'react-router-dom';
|
||||
import { Link, useNavigate, useLocation } from 'react-router-dom';
|
||||
import { useAuth } from '../contexts/AuthContext';
|
||||
|
||||
const Login: React.FC = () => {
|
||||
@@ -9,6 +9,9 @@ const Login: React.FC = () => {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const { login } = useAuth();
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
|
||||
const from = location.state?.from?.pathname || '/';
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
@@ -17,7 +20,7 @@ const Login: React.FC = () => {
|
||||
|
||||
try {
|
||||
await login(email, password);
|
||||
navigate('/');
|
||||
navigate(from, { replace: true });
|
||||
} catch (err: any) {
|
||||
setError(err.response?.data?.error || 'Failed to login');
|
||||
} finally {
|
||||
@@ -75,7 +78,7 @@ const Login: React.FC = () => {
|
||||
<div className="text-center mt-3">
|
||||
<p className="mb-0">
|
||||
Don't have an account?{' '}
|
||||
<Link to="/register" className="text-decoration-none">
|
||||
<Link to="/register" state={{ from: location.state?.from }} className="text-decoration-none">
|
||||
Sign up
|
||||
</Link>
|
||||
</p>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Link, useNavigate } from 'react-router-dom';
|
||||
import { Link, useNavigate, useLocation } from 'react-router-dom';
|
||||
import { useAuth } from '../contexts/AuthContext';
|
||||
|
||||
const Register: React.FC = () => {
|
||||
@@ -15,6 +15,9 @@ const Register: React.FC = () => {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const { register } = useAuth();
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
|
||||
const from = location.state?.from?.pathname || '/';
|
||||
|
||||
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setFormData({
|
||||
@@ -30,7 +33,7 @@ const Register: React.FC = () => {
|
||||
|
||||
try {
|
||||
await register(formData);
|
||||
navigate('/');
|
||||
navigate(from, { replace: true });
|
||||
} catch (err: any) {
|
||||
setError(err.response?.data?.error || 'Failed to create account');
|
||||
} finally {
|
||||
|
||||
@@ -22,6 +22,13 @@ const RentItem: React.FC = () => {
|
||||
cardCVC: '',
|
||||
cardName: ''
|
||||
});
|
||||
|
||||
const [manualSelection, setManualSelection] = useState({
|
||||
startDate: '',
|
||||
startTime: '09:00',
|
||||
endDate: '',
|
||||
endTime: '17:00'
|
||||
});
|
||||
|
||||
const [selectedPeriods, setSelectedPeriods] = useState<Array<{
|
||||
id: string;
|
||||
@@ -41,6 +48,48 @@ const RentItem: React.FC = () => {
|
||||
useEffect(() => {
|
||||
calculateTotal();
|
||||
}, [selectedPeriods, item]);
|
||||
|
||||
useEffect(() => {
|
||||
// Sync manual selection with selected periods
|
||||
if (selectedPeriods.length > 0) {
|
||||
const period = selectedPeriods[0];
|
||||
// Extract hours from the Date objects if startTime/endTime not provided
|
||||
let startTimeStr = period.startTime;
|
||||
let endTimeStr = period.endTime;
|
||||
|
||||
if (!startTimeStr) {
|
||||
const startHour = period.startDate.getHours();
|
||||
startTimeStr = `${startHour.toString().padStart(2, '0')}:00`;
|
||||
}
|
||||
|
||||
if (!endTimeStr) {
|
||||
const endHour = period.endDate.getHours();
|
||||
// If the end hour is 23:59:59, show it as 00:00 of the next day
|
||||
if (endHour === 23 && period.endDate.getMinutes() === 59) {
|
||||
endTimeStr = '00:00';
|
||||
// Adjust the end date to show the next day
|
||||
const adjustedEndDate = new Date(period.endDate);
|
||||
adjustedEndDate.setDate(adjustedEndDate.getDate() + 1);
|
||||
setManualSelection({
|
||||
startDate: period.startDate.toISOString().split('T')[0],
|
||||
startTime: startTimeStr,
|
||||
endDate: adjustedEndDate.toISOString().split('T')[0],
|
||||
endTime: endTimeStr
|
||||
});
|
||||
return;
|
||||
} else {
|
||||
endTimeStr = `${endHour.toString().padStart(2, '0')}:00`;
|
||||
}
|
||||
}
|
||||
|
||||
setManualSelection({
|
||||
startDate: period.startDate.toISOString().split('T')[0],
|
||||
startTime: startTimeStr,
|
||||
endDate: period.endDate.toISOString().split('T')[0],
|
||||
endTime: endTimeStr
|
||||
});
|
||||
}
|
||||
}, [selectedPeriods]);
|
||||
|
||||
const fetchItem = async () => {
|
||||
try {
|
||||
@@ -176,6 +225,59 @@ const RentItem: React.FC = () => {
|
||||
setFormData(prev => ({ ...prev, [name]: value }));
|
||||
}
|
||||
};
|
||||
|
||||
const handleManualSelectionChange = (e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement>) => {
|
||||
const updatedSelection = {
|
||||
...manualSelection,
|
||||
[e.target.name]: e.target.value
|
||||
};
|
||||
setManualSelection(updatedSelection);
|
||||
|
||||
// Automatically apply selection if both dates are set
|
||||
if (updatedSelection.startDate && updatedSelection.endDate) {
|
||||
// Create dates with time set to midnight to avoid timezone issues
|
||||
const start = new Date(updatedSelection.startDate + 'T00:00:00');
|
||||
const end = new Date(updatedSelection.endDate + 'T00:00:00');
|
||||
|
||||
// Add time for both hourly and daily rentals
|
||||
const [startHour, startMin] = updatedSelection.startTime.split(':').map(Number);
|
||||
const [endHour, endMin] = updatedSelection.endTime.split(':').map(Number);
|
||||
start.setHours(startHour, startMin, 0, 0);
|
||||
end.setHours(endHour, endMin, 0, 0);
|
||||
|
||||
// Note: We keep the times as selected by the user
|
||||
// The calendar will interpret 00:00 correctly
|
||||
|
||||
// Validate dates
|
||||
if (end < start) {
|
||||
setError('End date/time must be after start date/time');
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if dates are available
|
||||
const unavailable = item?.unavailablePeriods?.some(period => {
|
||||
const periodStart = new Date(period.startDate);
|
||||
const periodEnd = new Date(period.endDate);
|
||||
return (start >= periodStart && start <= periodEnd) ||
|
||||
(end >= periodStart && end <= periodEnd) ||
|
||||
(start <= periodStart && end >= periodEnd);
|
||||
});
|
||||
|
||||
if (unavailable) {
|
||||
setError('Selected dates include unavailable periods');
|
||||
return;
|
||||
}
|
||||
|
||||
setError(null);
|
||||
setSelectedPeriods([{
|
||||
id: Date.now().toString(),
|
||||
startDate: start,
|
||||
endDate: end,
|
||||
startTime: updatedSelection.startTime,
|
||||
endTime: updatedSelection.endTime
|
||||
}]);
|
||||
}
|
||||
};
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
@@ -223,42 +325,117 @@ const RentItem: React.FC = () => {
|
||||
<h5 className="card-title">Select Rental Period</h5>
|
||||
|
||||
<AvailabilityCalendar
|
||||
unavailablePeriods={[
|
||||
...(item.unavailablePeriods || []),
|
||||
...selectedPeriods.map(p => ({ ...p, isRentalSelection: true }))
|
||||
]}
|
||||
onPeriodsChange={(periods) => {
|
||||
// Only handle rental selections
|
||||
const rentalSelections = periods.filter(p => p.isRentalSelection);
|
||||
setSelectedPeriods(rentalSelections.map(p => {
|
||||
const { isRentalSelection, ...rest } = p;
|
||||
return rest;
|
||||
}));
|
||||
unavailablePeriods={item.unavailablePeriods || []}
|
||||
onPeriodsChange={() => {}} // Read-only for renters
|
||||
mode="renter"
|
||||
selectedRentalPeriod={selectedPeriods.length > 0 ? {
|
||||
id: selectedPeriods[0].id,
|
||||
startDate: selectedPeriods[0].startDate,
|
||||
endDate: selectedPeriods[0].endDate,
|
||||
startTime: selectedPeriods[0].startTime,
|
||||
endTime: selectedPeriods[0].endTime,
|
||||
isRentalSelection: true
|
||||
} : undefined}
|
||||
onRentalSelect={(period) => {
|
||||
// Update selected periods
|
||||
setSelectedPeriods([{
|
||||
id: Date.now().toString(),
|
||||
startDate: period.startDate,
|
||||
endDate: period.endDate,
|
||||
startTime: period.startTime,
|
||||
endTime: period.endTime
|
||||
}]);
|
||||
|
||||
// Update manual selection to match calendar
|
||||
setManualSelection({
|
||||
startDate: period.startDate.toISOString().split('T')[0],
|
||||
startTime: period.startTime || '09:00',
|
||||
endDate: period.endDate.toISOString().split('T')[0],
|
||||
endTime: period.endTime || '17:00'
|
||||
});
|
||||
}}
|
||||
priceType={showHourlyOptions ? "hour" : "day"}
|
||||
isRentalMode={true}
|
||||
/>
|
||||
|
||||
<div className="mt-4">
|
||||
<div className="row g-3">
|
||||
<div className="col-md-3">
|
||||
<label htmlFor="startDate" className="form-label">Start Date</label>
|
||||
<input
|
||||
type="date"
|
||||
className="form-control"
|
||||
id="startDate"
|
||||
name="startDate"
|
||||
value={manualSelection.startDate}
|
||||
onChange={handleManualSelectionChange}
|
||||
min={new Date().toISOString().split('T')[0]}
|
||||
/>
|
||||
</div>
|
||||
<div className="col-md-3">
|
||||
<label htmlFor="startTime" className="form-label">Start Time</label>
|
||||
<select
|
||||
className="form-select"
|
||||
id="startTime"
|
||||
name="startTime"
|
||||
value={manualSelection.startTime}
|
||||
onChange={handleManualSelectionChange}
|
||||
>
|
||||
{Array.from({ length: 24 }, (_, i) => {
|
||||
const hour = i === 0 ? 12 : i > 12 ? i - 12 : i;
|
||||
const period = i < 12 ? 'AM' : 'PM';
|
||||
return (
|
||||
<option key={i} value={`${i.toString().padStart(2, '0')}:00`}>
|
||||
{hour}:00 {period}
|
||||
</option>
|
||||
);
|
||||
})}
|
||||
</select>
|
||||
</div>
|
||||
<div className="col-md-3">
|
||||
<label htmlFor="endDate" className="form-label">End Date</label>
|
||||
<input
|
||||
type="date"
|
||||
className="form-control"
|
||||
id="endDate"
|
||||
name="endDate"
|
||||
value={manualSelection.endDate}
|
||||
onChange={handleManualSelectionChange}
|
||||
min={manualSelection.startDate || new Date().toISOString().split('T')[0]}
|
||||
/>
|
||||
</div>
|
||||
<div className="col-md-3">
|
||||
<label htmlFor="endTime" className="form-label">End Time</label>
|
||||
<select
|
||||
className="form-select"
|
||||
id="endTime"
|
||||
name="endTime"
|
||||
value={manualSelection.endTime}
|
||||
onChange={handleManualSelectionChange}
|
||||
>
|
||||
{Array.from({ length: 24 }, (_, i) => {
|
||||
const hour = i === 0 ? 12 : i > 12 ? i - 12 : i;
|
||||
const period = i < 12 ? 'AM' : 'PM';
|
||||
return (
|
||||
<option key={i} value={`${i.toString().padStart(2, '0')}:00`}>
|
||||
{hour}:00 {period}
|
||||
</option>
|
||||
);
|
||||
})}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{rentalDuration.days < minDays && rentalDuration.days > 0 && !showHourlyOptions && (
|
||||
<div className="alert alert-warning mt-3">
|
||||
Minimum rental period is {minDays} days
|
||||
</div>
|
||||
)}
|
||||
|
||||
{selectedPeriods.length === 0 && (
|
||||
<div className="alert alert-info mt-3">
|
||||
Please select your rental dates on the calendar above
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="card mb-4">
|
||||
<div className="card-body">
|
||||
<h5 className="card-title">Delivery Options</h5>
|
||||
|
||||
<div className="mb-3">
|
||||
<label htmlFor="deliveryMethod" className="form-label">Delivery Method *</label>
|
||||
<div className="my-3">
|
||||
<select
|
||||
className="form-select"
|
||||
id="deliveryMethod"
|
||||
|
||||
Reference in New Issue
Block a user