Initial commit: CSV Vie Locale
This commit is contained in:
@@ -0,0 +1,136 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import api from "../api";
|
||||
import { useAuth } from "../contexts/AuthContext";
|
||||
|
||||
export default function HelpRequests() {
|
||||
const { user } = useAuth();
|
||||
const [requests, setRequests] = useState([]);
|
||||
const [showForm, setShowForm] = useState(false);
|
||||
const [form, setForm] = useState({ title: "", description: "", category: "autre", urgent: false, image: "" });
|
||||
const [editingId, setEditingId] = useState(null);
|
||||
const [uploading, setUploading] = useState(false);
|
||||
|
||||
const load = () => {
|
||||
api.get("/help").then((r) => setRequests(r.data)).catch(() => {});
|
||||
};
|
||||
|
||||
useEffect(() => { load(); }, []);
|
||||
|
||||
const handleDelete = async (id) => {
|
||||
if (!confirm("Supprimer cette demande ?")) return;
|
||||
try {
|
||||
await api.delete("/help/" + id);
|
||||
load();
|
||||
} catch (err) { alert(err.response?.data?.error || "Erreur"); }
|
||||
};
|
||||
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
try {
|
||||
if (editingId) {
|
||||
await api.put("/help/" + editingId, form);
|
||||
} else {
|
||||
await api.post("/help", form);
|
||||
}
|
||||
setShowForm(false);
|
||||
setEditingId(null);
|
||||
setForm({ title: "", description: "", category: "autre", urgent: false, image: "" });
|
||||
setEditingId(null);
|
||||
load();
|
||||
} catch (err) { alert(err.response?.data?.error || "Erreur"); }
|
||||
};
|
||||
|
||||
const handleEdit = (r) => {
|
||||
setForm({ title: r.title, description: r.description, category: r.category, urgent: r.urgent === 1, image: r.image || "" });
|
||||
setEditingId(r.id);
|
||||
setShowForm(true);
|
||||
};
|
||||
|
||||
const handleImageUpload = async (e) => {
|
||||
const file = e.target.files[0];
|
||||
if (!file) return;
|
||||
setUploading(true);
|
||||
const fd = new FormData();
|
||||
fd.append("file", file);
|
||||
try {
|
||||
const res = await api.post("/upload", fd, { headers: { "Content-Type": "multipart/form-data" } });
|
||||
setForm({ ...form, image: res.data.url });
|
||||
} catch (err) { alert("Erreur upload"); }
|
||||
setUploading(false);
|
||||
};
|
||||
|
||||
const categoryLabels = { course: "Courses", bricolage: "Bricolage", jardinage: "Jardinage", transport: "Transport", numerique: "Numérique", autre: "Autre" };
|
||||
|
||||
return (
|
||||
<div className="max-w-4xl mx-auto px-4 py-8">
|
||||
<div className="flex justify-between items-center mb-6">
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold">Entraide</h1>
|
||||
<p className="text-sm text-gray-500">Demander ou proposer de l'aide aux habitants</p>
|
||||
</div>
|
||||
{user && (
|
||||
<button onClick={() => { setShowForm(!showForm); setEditingId(null); setForm({ title: "", description: "", category: "autre", urgent: false, image: "" }); }} className="bg-csv-600 text-white px-4 py-2 rounded-lg text-sm hover:bg-csv-700">
|
||||
{showForm ? "Annuler" : "Demander de l'aide"}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{showForm && (
|
||||
<form onSubmit={handleSubmit} className="border rounded-lg p-4 mb-6 space-y-3 bg-white">
|
||||
<input placeholder="Titre *" required value={form.title} onChange={(e) => setForm({ ...form, title: e.target.value })}
|
||||
className="w-full border rounded px-3 py-2 text-sm outline-none focus:ring-2 focus:ring-csv-500" />
|
||||
<textarea placeholder="Description *" required rows={3} value={form.description} onChange={(e) => setForm({ ...form, description: e.target.value })}
|
||||
className="w-full border rounded px-3 py-2 text-sm outline-none focus:ring-2 focus:ring-csv-500" />
|
||||
<div className="flex gap-3 items-center">
|
||||
<select value={form.category} onChange={(e) => setForm({ ...form, category: e.target.value })}
|
||||
className="border rounded px-3 py-2 text-sm outline-none">
|
||||
{Object.entries(categoryLabels).map(([k, v]) => <option key={k} value={k}>{v}</option>)}
|
||||
</select>
|
||||
<label className="flex items-center gap-2 text-sm">
|
||||
<input type="checkbox" checked={form.urgent} onChange={(e) => setForm({ ...form, urgent: e.target.checked })} />
|
||||
Urgent
|
||||
</label>
|
||||
</div>
|
||||
{form.image && <img src={form.image} alt="" className="h-20 rounded" />}
|
||||
<div>
|
||||
<input type="file" accept="image/*" onChange={handleImageUpload} className="text-sm" />
|
||||
{uploading && <span className="text-xs text-gray-400 ml-2">Upload...</span>}
|
||||
</div>
|
||||
<button type="submit" className="bg-csv-600 text-white px-4 py-2 rounded text-sm hover:bg-csv-700">
|
||||
{editingId ? "Mettre a jour" : "Publier"}
|
||||
</button>
|
||||
</form>
|
||||
)}
|
||||
|
||||
{requests.length === 0 ? (
|
||||
<p className="text-gray-400 text-center py-8">Aucune demande d'aide pour le moment.</p>
|
||||
) : (
|
||||
<div className="space-y-3">
|
||||
{requests.map((r) => (
|
||||
<div key={r.id} className={"border rounded-lg p-4 bg-white " + (r.urgent ? "border-red-300" : "")}>
|
||||
{r.image && <img src={r.image} alt={r.title} className="w-full h-36 object-cover rounded mb-2" />}
|
||||
<div className="flex items-start gap-3">
|
||||
{r.urgent && <span className="bg-red-500 text-white text-xs px-2 py-0.5 rounded-full mt-0.5">Urgent</span>}
|
||||
<div className="flex-1">
|
||||
<h3 className="font-semibold">{r.title}</h3>
|
||||
<p className="text-sm text-gray-600 mt-1">{r.description}</p>
|
||||
<div className="flex gap-3 mt-2 text-xs text-gray-400">
|
||||
<span className="bg-gray-100 px-2 py-0.5 rounded">{categoryLabels[r.category] || r.category}</span>
|
||||
<span>par {r.author_name}</span>
|
||||
{r.author_phone && <span>📞 {r.author_phone}</span>}
|
||||
{user && r.author_id === user.id && (
|
||||
<>
|
||||
<button onClick={() => handleEdit(r)} className="text-blue-500 hover:text-blue-700 ml-auto mr-2">Modifier</button>
|
||||
<button onClick={() => handleDelete(r.id)} className="text-red-500 hover:text-red-700">Supprimer</button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user