You'll add more details later, like bed types.
{counters.map((item) => (
-
-
{item.label}
-
+
+
{item.label}
+
updateCount(item.key, -1)}
- className="w-10 h-10 rounded-full border-2 border-black flex items-center justify-center hover:bg-gray-100"
+ disabled={!data[item.key]}
+ className="w-12 h-12 rounded-full border-2 border-black flex items-center justify-center hover:bg-gray-100 disabled:opacity-30 disabled:hover:bg-transparent transition-colors"
>
- -
+ -
- {data[item.key] || 0}
+ {data[item.key] || 0}
updateCount(item.key, 1)}
- className="w-10 h-10 rounded-full border-2 border-black flex items-center justify-center hover:bg-gray-100"
+ className="w-12 h-12 rounded-full border-2 border-black flex items-center justify-center hover:bg-gray-100 transition-colors"
>
- +
+ +
))}
-
-
- Back
-
- onNext()} className={buttonStyle}>
- Next
-
-
)
}
-/* Property Photos */
-const PropertyPhotos = ({ onNext, onBack }: StepProps) => {
- return (
-
-
Add some photos of your house
-
-
-
-
Drag your photos here
-
Choose at least 5 photos
-
-
Upload from your device
-
-
-
- Back
-
- onNext()} className={buttonStyle}>
- Next
-
-
-
- )
-}
-
-/* Property Amenities */
-const PropertyAmenities = ({ onNext, onBack, data, updateData }: StepProps) => {
+const PropertyAmenities = ({ data, updateData }: StepProps) => {
const amenities = [
{ id: 'wifi', label: 'Wi-Fi', icon: WifiHigh },
- { id: 'kitchen', label: 'Kitchen', icon: House },
- { id: 'parking', label: 'Free parking', icon: Farm },
- { id: 'pool', label: 'Pool', icon: Buildings },
+ { id: 'kitchen', label: 'Kitchen', icon: CookingPot },
+ { id: 'parking', label: 'Free parking', icon: Car },
+ { id: 'pool', label: 'Pool', icon: SwimmingPool },
+ { id: 'ac', label: 'Air conditioning', icon: Wind }, // Using House as placeholder for AC if needed
+ { id: 'tv', label: 'TV', icon: TelevisionSimple },
];
const toggleAmenity = (id: string) => {
@@ -265,159 +308,184 @@ const PropertyAmenities = ({ onNext, onBack, data, updateData }: StepProps) => {
};
return (
-
+
What does your place offer?
+
You can add more amenities after you publish.
{amenities.map((amenity) => (
toggleAmenity(amenity.id)}
- className={`${optionCardStyle((data.amenities || []).includes(amenity.id))} items-start w-full`}
+ className={`${optionCardStyle((data.amenities || []).includes(amenity.id))} items-start w-full text-left`}
>
-
+
{amenity.label}
))}
-
-
- Back
-
- onNext()} className={buttonStyle}>
- Next
-
-
)
}
-/* Property Description */
-const PropertyDescription = ({ onNext, onBack, data, updateData }: StepProps) => {
+const PropertyDescription = ({ data, updateData }: StepProps) => {
return (
-
+
How would you describe your place?
-
-
-
-
- Back
-
-
onNext()} className={buttonStyle}>
- Next
-
+
Short and sweet works best.
+
+
+ Title
+ updateData('title', e.target.value)}
+ />
+
+
+ Description
+
)
}
-/* Property Price */
-const PropertyPrice = ({ onNext, onBack, data, updateData }: StepProps) => {
+const PropertyPhotos = ({ data, updateData }: StepProps) => {
+ const fileInputRef = useRef
(null);
+
+ const handleFileChange = (e: React.ChangeEvent) => {
+ if (e.target.files) {
+ const newPhotos = Array.from(e.target.files).map(file => URL.createObjectURL(file));
+ const currentPhotos = data.photos || [];
+ updateData('photos', [...currentPhotos, ...newPhotos]);
+ }
+ };
+
+ const removePhoto = (index: number) => {
+ const currentPhotos = data.photos || [];
+ const updated = currentPhotos.filter((_, i) => i !== index);
+ updateData('photos', updated);
+ };
+
return (
-
+
+
Add some photos of your house
+
You'll need 5 photos to get started. You can add more or make changes later.
+
+
+
fileInputRef.current?.click()}
+ className="border-2 border-dashed border-gray-300 rounded-lg p-12 flex flex-col items-center justify-center gap-4 cursor-pointer hover:bg-gray-50 transition-colors"
+ >
+
+ Upload photos
+ JPG, PNG up to 10MB
+
+
+
+ {data.photos && data.photos.length > 0 && (
+
+ {data.photos.map((photo, index) => (
+
+
+
removePhoto(index)}
+ className="absolute top-2 right-2 bg-white rounded-full p-1 border-2 border-black hover:bg-red-50 transition-colors z-10"
+ >
+
+
+ {index === 0 && (
+
Cover Photo
+ )}
+
+ ))}
+
+ )}
+
+ {(!data.photos || data.photos.length < 5) && (
+
+
+ Please upload at least {5 - (data.photos?.length || 0)} more photos
+
+ )}
+
+
+ )
+}
+
+const PropertyPrice = ({ data, updateData }: StepProps) => {
+ return (
+
Now, set your price
-
+
You can change it anytime.
+
-
+ ₨
updateData('price', e.target.value)}
/>
-
per night
-
-
-
- Back
-
-
onNext()} className={buttonStyle}>
- Next
-
+
per night
)
}
-/* Property Instant Approval */
-const PropertyInstantApproval = ({ onNext, onBack, data, updateData }: StepProps) => {
+const PropertyInstantApproval = ({ data, updateData }: StepProps) => {
return (
-
+
Decide how you'll confirm reservations
updateData('instantBook', true)}
- className={`${optionCardStyle(data.instantBook === true)} w-full`}
+ className={`${optionCardStyle(data.instantBook === true)} w-full flex-row items-start text-left`}
>
-
-
-
- Use Instant Book
- Guests can book automatically.
-
+
+
+ Use Instant Book
+ Guests can book automatically. No need to approve reservations.
updateData('instantBook', false)}
- className={`${optionCardStyle(data.instantBook === false)} w-full`}
+ className={`${optionCardStyle(data.instantBook === false)} w-full flex-row items-start text-left`}
>
-
-
-
- Approve manually
- You approve or decline booking requests.
-
+
+
+ Approve manually
+ You approve or decline booking requests. You will be notified of each booking request.
-
-
- Back
-
- onNext()} className={buttonStyle}>
- Next
-
-
)
}
-/* Availability Calendar */
-const AvailabilityCalendar = ({ onNext, onBack }: StepProps) => {
- return (
-
-
When is your place available?
-
-
-
Calendar Component Placeholder
-
-
-
- Back
-
- onNext()} className={buttonStyle}>
- Next
-
-
-
- )
-}
-
-/* Safety Details */
-const SafetyDetails = ({ onNext, onBack, data, updateData }: StepProps) => {
+const SafetyDetails = ({ data, updateData }: StepProps) => {
const safetyItems = [
{ id: 'camera', label: 'Security camera(s)', icon: Camera },
- { id: 'weapons', label: 'Weapons', icon: ShieldCheck },
+ { id: 'weapons', label: 'Dangerous weapons', icon: ShieldCheck },
{ id: 'animals', label: 'Dangerous animals', icon: Farm },
];
@@ -430,7 +498,7 @@ const SafetyDetails = ({ onNext, onBack, data, updateData }: StepProps) => {
};
return (
-
+
Does your place have any of these?
{safetyItems.map((item) => (
@@ -444,27 +512,27 @@ const SafetyDetails = ({ onNext, onBack, data, updateData }: StepProps) => {
{item.label}
-
+
+ {(data.safety || []).includes(item.id) &&
}
+
))}
-
-
- Back
-
- { }} className={buttonStyle}>
- Finish
-
-
)
}
+/* --- Main List Component --- */
+
function List() {
const [step, setStep] = useState(0);
- const [direction, setDirection] = useState(1); // 1 for next, -1 for back
- const [formData, setFormData] = useState({});
- const containerRef = useRef(null);
+ const [direction, setDirection] = useState(1);
+ const [formData, setFormData] = useState({
+ guests: 1,
+ bedrooms: 1,
+ beds: 1,
+ bathrooms: 1,
+ });
const contentRef = useRef(null);
const steps = [
@@ -472,22 +540,29 @@ function List() {
PropertyPlaceType,
PropertyLocation,
PropertyCapacity,
- PropertyPhotos,
PropertyAmenities,
PropertyDescription,
+ PropertyPhotos,
PropertyPrice,
PropertyInstantApproval,
- AvailabilityCalendar,
SafetyDetails
];
const CurrentStepComponent = steps[step];
+ const totalSteps = steps.length;
+ const progress = ((step + 1) / totalSteps) * 100;
const updateData = (key: keyof FormData, value: FormValue) => {
setFormData(prev => ({ ...prev, [key]: value }));
};
const handleNext = () => {
+ // Validation for photos
+ if (step === 6 && (!formData.photos || formData.photos.length < 5)) {
+ alert("Please upload at least 5 photos to continue.");
+ return;
+ }
+
if (step < steps.length - 1) {
setDirection(1);
setStep(prev => prev + 1);
@@ -503,81 +578,88 @@ function List() {
useGSAP(() => {
if (!contentRef.current) return;
-
- // Animate the content sliding in
gsap.fromTo(contentRef.current,
- {
- x: direction * 50,
- opacity: 0
- },
- {
- x: 0,
- opacity: 1,
- duration: 0.4,
- ease: "power2.out"
- }
+ { x: direction * 20, opacity: 0 },
+ { x: 0, opacity: 1, duration: 0.4, ease: "power2.out" }
);
}, [step]);
return (
-
+
{/* Left Panel - Form */}
-
- {/* Progress Bar - Top */}
-
-
-
-
- {step + 1}
-
-
Step {step + 1} of {steps.length}
+
+ {/* Header */}
+
+
+
+ {step + 1}
+
+
+ Step {step + 1} of {totalSteps}
+ {
+ step === 0 ? "Basics" :
+ step < 4 ? "Details" :
+ step < 7 ? "Description" : "Finish"
+ }
-
Exit
-
-
Save & Exit
+
+
+ {/* Progress Bar */}
+
+
+ {/* Scrollable Content */}
+
- {/* Content */}
-
-
+ {/* Fixed Footer Navigation */}
+
+
+ Back
+
+
+ {step === totalSteps - 1 ? 'Finish' : 'Next'}
+
- {/* Right Panel - Visual */}
-
- {/* Gradient Background */}
-
-
+ {/* Background Pattern */}
+
- {/* Decorative Content */}
-
-
-
- Open your door to hosting
-
-
- Earn money, reach millions of travelers, and find your freedom. It's easy to get started.
-
+
+
+
What guests will see
+
As you update your listing, see how it looks in search results.
+