import React, { useState, useEffect } from "react"; import { useNavigate, Link } from "react-router-dom"; import { useAuth } from "../contexts/AuthContext"; import { forumAPI, addressAPI } from "../services/api"; import { uploadFiles } from "../services/uploadService"; import TagInput from "../components/TagInput"; import ForumImageUpload from "../components/ForumImageUpload"; import { Address } from "../types"; const CreateForumPost: React.FC = () => { const { user } = useAuth(); const navigate = useNavigate(); const [isSubmitting, setIsSubmitting] = useState(false); const [error, setError] = useState(null); const [userAddresses, setUserAddresses] = useState([]); const [formData, setFormData] = useState({ title: "", content: "", category: "general_discussion" as | "item_request" | "technical_support" | "community_resources" | "general_discussion", tags: [] as string[], zipCode: user?.zipCode || "", latitude: undefined as number | undefined, longitude: undefined as number | undefined, }); const [imageFiles, setImageFiles] = useState([]); const [imagePreviews, setImagePreviews] = useState([]); useEffect(() => { fetchUserAddresses(); }, []); const fetchUserAddresses = async () => { try { const response = await addressAPI.getAddresses(); setUserAddresses(response.data); } catch (error) { console.error("Error fetching addresses:", error); } }; const categories = [ { value: "item_request", label: "Item Request", description: "Looking to rent a specific item", }, { value: "technical_support", label: "Technical Support", description: "Get help with using the platform", }, { value: "community_resources", label: "Community Resources", description: "Share tips, guides, and resources", }, { value: "general_discussion", label: "General Discussion", description: "Open-ended conversations", }, ]; const handleInputChange = ( e: React.ChangeEvent< HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement > ) => { const { name, value } = e.target; // If category is being changed to item_request and user has addresses, autopopulate location data if (name === "category" && value === "item_request" && userAddresses.length > 0) { // Try to find primary address first, otherwise use first address const primaryAddress = userAddresses.find(addr => addr.isPrimary) || userAddresses[0]; setFormData((prev) => ({ ...prev, [name]: value, zipCode: primaryAddress.zipCode, latitude: primaryAddress.latitude, longitude: primaryAddress.longitude })); } else { setFormData((prev) => ({ ...prev, [name]: value })); } }; const handleTagsChange = (tags: string[]) => { setFormData((prev) => ({ ...prev, tags })); }; const handleImageChange = (e: React.ChangeEvent) => { const files = Array.from(e.target.files || []); const remainingSlots = 5 - imageFiles.length; const filesToAdd = files.slice(0, remainingSlots); setImageFiles((prev) => [...prev, ...filesToAdd]); // Create preview URLs filesToAdd.forEach((file) => { const reader = new FileReader(); reader.onloadend = () => { setImagePreviews((prev) => [...prev, reader.result as string]); }; reader.readAsDataURL(file); }); // Reset input e.target.value = ""; }; const handleRemoveImage = (index: number) => { setImageFiles((prev) => prev.filter((_, i) => i !== index)); setImagePreviews((prev) => prev.filter((_, i) => i !== index)); }; const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); setError(null); // Validation if (!formData.title.trim()) { setError("Title is required"); return; } if (formData.title.length < 10) { setError("Title must be at least 10 characters long"); return; } if (!formData.content.trim()) { setError("Content is required"); return; } if (formData.content.length < 20) { setError("Content must be at least 20 characters long"); return; } if (formData.category === "item_request" && !formData.zipCode.trim()) { setError("Zip code is required for item requests"); return; } try { setIsSubmitting(true); // Upload images to S3 first (if any) let imageFilenames: string[] = []; if (imageFiles.length > 0) { const uploadResults = await uploadFiles("forum", imageFiles); imageFilenames = uploadResults.map((result) => result.key); } // Build the post data const postData: { title: string; content: string; category: string; tags?: string[]; zipCode?: string; latitude?: number; longitude?: number; imageFilenames?: string[]; } = { title: formData.title, content: formData.content, category: formData.category, }; // Add tags if present if (formData.tags.length > 0) { postData.tags = formData.tags; } // Add location data for item requests if (formData.category === 'item_request' && formData.zipCode) { postData.zipCode = formData.zipCode; // If we have coordinates from a saved address, send them to avoid re-geocoding if (formData.latitude !== undefined && formData.longitude !== undefined) { postData.latitude = formData.latitude; postData.longitude = formData.longitude; } } // Add S3 image keys if (imageFilenames.length > 0) { postData.imageFilenames = imageFilenames; } const response = await forumAPI.createPost(postData); navigate(`/forum/${response.data.id}`); } catch (err: any) { setError(err.response?.data?.error || err.message || "Failed to create post"); setIsSubmitting(false); } }; if (!user) { return (
You must be logged in to create a post.
Back to Forum
); } return (
{/* Guidelines Card */}
Community Guidelines
  • Be respectful and courteous to others
  • Stay on topic and relevant to the category
  • No spam, advertising, or self-promotion
  • Search before posting to avoid duplicates
  • Use clear, descriptive titles
  • Provide helpful and constructive feedback

Create New Post

{error && (
{error}
)}
{/* Title */}
{formData.title.length}/200 characters (minimum 10)
{/* Category */}
Choose the category that best fits your post
{/* Location fields for item requests */} {formData.category === "item_request" && (
Your zip code helps notify nearby users who might have the item you're looking for
)} {/* Content */}