343 lines
12 KiB
TypeScript
343 lines
12 KiB
TypeScript
import React, { useState } from "react";
|
|
import { useNavigate } from "react-router-dom";
|
|
import { useAuth } from "../contexts/AuthContext";
|
|
import { itemRequestAPI } from "../services/api";
|
|
import AddressAutocomplete from "../components/AddressAutocomplete";
|
|
|
|
const CreateItemRequest: React.FC = () => {
|
|
const navigate = useNavigate();
|
|
const { user } = useAuth();
|
|
const [loading, setLoading] = useState(false);
|
|
const [error, setError] = useState<string | null>(null);
|
|
|
|
const [formData, setFormData] = useState({
|
|
title: "",
|
|
description: "",
|
|
address1: "",
|
|
address2: "",
|
|
city: "",
|
|
state: "",
|
|
zipCode: "",
|
|
country: "US",
|
|
latitude: undefined as number | undefined,
|
|
longitude: undefined as number | undefined,
|
|
maxPricePerHour: "",
|
|
maxPricePerDay: "",
|
|
preferredStartDate: "",
|
|
preferredEndDate: "",
|
|
isFlexibleDates: true,
|
|
});
|
|
|
|
const handleChange = (
|
|
e: React.ChangeEvent<
|
|
HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement
|
|
>
|
|
) => {
|
|
const { name, value, type } = e.target;
|
|
if (type === "checkbox") {
|
|
const checked = (e.target as HTMLInputElement).checked;
|
|
setFormData((prev) => ({ ...prev, [name]: checked }));
|
|
} else {
|
|
setFormData((prev) => ({ ...prev, [name]: value }));
|
|
}
|
|
};
|
|
|
|
const handleAddressChange = (value: string, lat?: number, lon?: number) => {
|
|
setFormData((prev) => ({
|
|
...prev,
|
|
address1: value,
|
|
latitude: lat,
|
|
longitude: lon,
|
|
city: prev.city,
|
|
state: prev.state,
|
|
zipCode: prev.zipCode,
|
|
country: prev.country,
|
|
}));
|
|
};
|
|
|
|
const handleSubmit = async (e: React.FormEvent) => {
|
|
e.preventDefault();
|
|
if (!user) return;
|
|
|
|
setLoading(true);
|
|
setError(null);
|
|
|
|
try {
|
|
const requestData = {
|
|
...formData,
|
|
maxPricePerHour: formData.maxPricePerHour
|
|
? parseFloat(formData.maxPricePerHour)
|
|
: null,
|
|
maxPricePerDay: formData.maxPricePerDay
|
|
? parseFloat(formData.maxPricePerDay)
|
|
: null,
|
|
preferredStartDate: formData.preferredStartDate || null,
|
|
preferredEndDate: formData.preferredEndDate || null,
|
|
};
|
|
|
|
await itemRequestAPI.createItemRequest(requestData);
|
|
navigate("/my-requests");
|
|
} catch (err: any) {
|
|
setError(err.response?.data?.error || "Failed to create item request");
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
if (!user) {
|
|
return (
|
|
<div className="container mt-5">
|
|
<div className="alert alert-warning" role="alert">
|
|
Please log in to create item requests.
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="container mt-4">
|
|
<div className="row justify-content-center">
|
|
<div className="col-md-8">
|
|
<div className="card">
|
|
<div className="card-header">
|
|
<h2 className="mb-0">Request an Item</h2>
|
|
<p className="text-muted mb-0">
|
|
Can't find what you need? Request it and let others know!
|
|
</p>
|
|
</div>
|
|
<div className="card-body">
|
|
{error && (
|
|
<div className="alert alert-danger" role="alert">
|
|
{error}
|
|
</div>
|
|
)}
|
|
|
|
<form onSubmit={handleSubmit}>
|
|
<div className="mb-3">
|
|
<label htmlFor="title" className="form-label">
|
|
What are you looking for? *
|
|
</label>
|
|
<input
|
|
type="text"
|
|
className="form-control"
|
|
id="title"
|
|
name="title"
|
|
value={formData.title}
|
|
onChange={handleChange}
|
|
placeholder="e.g., Power drill, Camera lens, Camping tent"
|
|
required
|
|
/>
|
|
</div>
|
|
|
|
<div className="mb-3">
|
|
<label htmlFor="description" className="form-label">
|
|
Description *
|
|
</label>
|
|
<textarea
|
|
className="form-control"
|
|
id="description"
|
|
name="description"
|
|
rows={4}
|
|
value={formData.description}
|
|
onChange={handleChange}
|
|
placeholder="Describe what you need it for, any specific requirements, condition preferences, etc."
|
|
required
|
|
/>
|
|
</div>
|
|
|
|
<div className="row mb-3">
|
|
<div className="col-md-6">
|
|
<label htmlFor="maxPricePerDay" className="form-label">
|
|
Max Price per Day
|
|
</label>
|
|
<div className="input-group">
|
|
<span className="input-group-text">$</span>
|
|
<input
|
|
type="number"
|
|
className="form-control"
|
|
id="maxPricePerDay"
|
|
name="maxPricePerDay"
|
|
value={formData.maxPricePerDay}
|
|
onChange={handleChange}
|
|
step="0.01"
|
|
min="0"
|
|
placeholder="0.00"
|
|
/>
|
|
</div>
|
|
</div>
|
|
<div className="col-md-6">
|
|
<label htmlFor="maxPricePerHour" className="form-label">
|
|
Max Price per Hour
|
|
</label>
|
|
<div className="input-group">
|
|
<span className="input-group-text">$</span>
|
|
<input
|
|
type="number"
|
|
className="form-control"
|
|
id="maxPricePerHour"
|
|
name="maxPricePerHour"
|
|
value={formData.maxPricePerHour}
|
|
onChange={handleChange}
|
|
step="0.01"
|
|
min="0"
|
|
placeholder="0.00"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="mb-3">
|
|
<label className="form-label">Address</label>
|
|
<AddressAutocomplete
|
|
value={formData.address1}
|
|
onChange={handleAddressChange}
|
|
placeholder="Enter your address or area"
|
|
/>
|
|
</div>
|
|
|
|
<div className="row mb-3">
|
|
<div className="col-md-6">
|
|
<label htmlFor="address2" className="form-label">
|
|
Apartment, suite, etc.
|
|
</label>
|
|
<input
|
|
type="text"
|
|
className="form-control"
|
|
id="address2"
|
|
name="address2"
|
|
value={formData.address2}
|
|
onChange={handleChange}
|
|
placeholder="Apt 2B, Suite 100, etc."
|
|
/>
|
|
</div>
|
|
<div className="col-md-6">
|
|
<label htmlFor="city" className="form-label">
|
|
City
|
|
</label>
|
|
<input
|
|
type="text"
|
|
className="form-control"
|
|
id="city"
|
|
name="city"
|
|
value={formData.city}
|
|
onChange={handleChange}
|
|
placeholder="City"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="row mb-3">
|
|
<div className="col-md-6">
|
|
<label htmlFor="state" className="form-label">
|
|
State
|
|
</label>
|
|
<input
|
|
type="text"
|
|
className="form-control"
|
|
id="state"
|
|
name="state"
|
|
value={formData.state}
|
|
onChange={handleChange}
|
|
placeholder="State"
|
|
/>
|
|
</div>
|
|
<div className="col-md-6">
|
|
<label htmlFor="zipCode" className="form-label">
|
|
ZIP Code
|
|
</label>
|
|
<input
|
|
type="text"
|
|
className="form-control"
|
|
id="zipCode"
|
|
name="zipCode"
|
|
value={formData.zipCode}
|
|
onChange={handleChange}
|
|
placeholder="12345"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="mb-3">
|
|
<div className="form-check">
|
|
<input
|
|
className="form-check-input"
|
|
type="checkbox"
|
|
id="isFlexibleDates"
|
|
name="isFlexibleDates"
|
|
checked={formData.isFlexibleDates}
|
|
onChange={handleChange}
|
|
/>
|
|
<label
|
|
className="form-check-label"
|
|
htmlFor="isFlexibleDates"
|
|
>
|
|
I'm flexible with dates
|
|
</label>
|
|
</div>
|
|
</div>
|
|
|
|
{!formData.isFlexibleDates && (
|
|
<div className="row mb-3">
|
|
<div className="col-md-6">
|
|
<label
|
|
htmlFor="preferredStartDate"
|
|
className="form-label"
|
|
>
|
|
Preferred Start Date
|
|
</label>
|
|
<input
|
|
type="date"
|
|
className="form-control"
|
|
id="preferredStartDate"
|
|
name="preferredStartDate"
|
|
value={formData.preferredStartDate}
|
|
onChange={handleChange}
|
|
min={new Date().toLocaleDateString()}
|
|
/>
|
|
</div>
|
|
<div className="col-md-6">
|
|
<label htmlFor="preferredEndDate" className="form-label">
|
|
Preferred End Date
|
|
</label>
|
|
<input
|
|
type="date"
|
|
className="form-control"
|
|
id="preferredEndDate"
|
|
name="preferredEndDate"
|
|
value={formData.preferredEndDate}
|
|
onChange={handleChange}
|
|
min={
|
|
formData.preferredStartDate ||
|
|
new Date().toLocaleDateString()
|
|
}
|
|
/>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
<div className="d-grid gap-2">
|
|
<button
|
|
type="submit"
|
|
className="btn btn-primary"
|
|
disabled={loading}
|
|
>
|
|
{loading ? "Creating Request..." : "Create Request"}
|
|
</button>
|
|
<button
|
|
type="button"
|
|
className="btn btn-secondary"
|
|
onClick={() => navigate(-1)}
|
|
>
|
|
Cancel
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default CreateItemRequest;
|