|
|
|
|
@@ -4,8 +4,6 @@ import {
|
|
|
|
|
Euro,
|
|
|
|
|
Maximize2,
|
|
|
|
|
DoorOpen,
|
|
|
|
|
Bus,
|
|
|
|
|
Car,
|
|
|
|
|
Calendar,
|
|
|
|
|
Sofa,
|
|
|
|
|
ParkingCircle,
|
|
|
|
|
@@ -92,17 +90,17 @@ const formatFrDate = (value) => {
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const normalizeFeedback = (noteP, noteA, commentP, commentA) => ({
|
|
|
|
|
NOTE_P: typeof noteP === "number" ? noteP : parseFloat(noteP) || 0,
|
|
|
|
|
const normalizeFeedback = (noteV, noteA, commentV, commentA) => ({
|
|
|
|
|
NOTE_V: typeof noteV === "number" ? noteV : parseFloat(noteV) || 0,
|
|
|
|
|
NOTE_A: typeof noteA === "number" ? noteA : parseFloat(noteA) || 0,
|
|
|
|
|
COMMENTAIRE_P: commentP?.trim() ? commentP.trim() : null,
|
|
|
|
|
COMMENTAIRE_V: commentV?.trim() ? commentV.trim() : null,
|
|
|
|
|
COMMENTAIRE_A: commentA?.trim() ? commentA.trim() : null,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
export default function AppartCard({ annonce, onDelete, onUpdated }) {
|
|
|
|
|
const [noteP, setNoteP] = useState(annonce.NOTE_P || 0);
|
|
|
|
|
const [noteV, setNoteV] = useState(annonce.NOTE_V || 0);
|
|
|
|
|
const [noteA, setNoteA] = useState(annonce.NOTE_A || 0);
|
|
|
|
|
const [commentP, setCommentP] = useState(annonce.COMMENTAIRE_P || "");
|
|
|
|
|
const [commentV, setCommentV] = useState(annonce.COMMENTAIRE_V || "");
|
|
|
|
|
const [commentA, setCommentA] = useState(annonce.COMMENTAIRE_A || "");
|
|
|
|
|
const [saving, setSaving] = useState(false);
|
|
|
|
|
const [pendingSave, setPendingSave] = useState(false);
|
|
|
|
|
@@ -110,20 +108,20 @@ export default function AppartCard({ annonce, onDelete, onUpdated }) {
|
|
|
|
|
const [deleting, setDeleting] = useState(false);
|
|
|
|
|
|
|
|
|
|
const lastSavedRef = useRef(
|
|
|
|
|
normalizeFeedback(noteP, noteA, commentP, commentA)
|
|
|
|
|
normalizeFeedback(noteV, noteA, commentV, commentA)
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const hasPaulinFeedback = (noteP ?? 0) > 0 || !!commentP?.trim();
|
|
|
|
|
const hasAgatheFeedback = (noteA ?? 0) > 0 || !!commentA?.trim();
|
|
|
|
|
const hasVincentFeedback = (noteV ?? 0) > 0 || !!commentV?.trim();
|
|
|
|
|
const hasAntoineFeedback = (noteA ?? 0) > 0 || !!commentA?.trim();
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
const normalized = normalizeFeedback(noteP, noteA, commentP, commentA);
|
|
|
|
|
const normalized = normalizeFeedback(noteV, noteA, commentV, commentA);
|
|
|
|
|
const lastSaved = lastSavedRef.current;
|
|
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
normalized.NOTE_P === lastSaved.NOTE_P &&
|
|
|
|
|
normalized.NOTE_V === lastSaved.NOTE_V &&
|
|
|
|
|
normalized.NOTE_A === lastSaved.NOTE_A &&
|
|
|
|
|
normalized.COMMENTAIRE_P === lastSaved.COMMENTAIRE_P &&
|
|
|
|
|
normalized.COMMENTAIRE_V === lastSaved.COMMENTAIRE_V &&
|
|
|
|
|
normalized.COMMENTAIRE_A === lastSaved.COMMENTAIRE_A
|
|
|
|
|
) {
|
|
|
|
|
return;
|
|
|
|
|
@@ -147,14 +145,14 @@ export default function AppartCard({ annonce, onDelete, onUpdated }) {
|
|
|
|
|
}, 700);
|
|
|
|
|
|
|
|
|
|
return () => clearTimeout(timeout);
|
|
|
|
|
}, [annonce.id, commentA, commentP, noteA, noteP, onUpdated]);
|
|
|
|
|
}, [annonce.id, commentA, commentV, noteA, noteV, onUpdated]);
|
|
|
|
|
|
|
|
|
|
const handleSave = async () => {
|
|
|
|
|
setSaving(true);
|
|
|
|
|
await updateAnnonce(annonce.id, {
|
|
|
|
|
NOTE_P: noteP,
|
|
|
|
|
NOTE_V: noteV,
|
|
|
|
|
NOTE_A: noteA,
|
|
|
|
|
COMMENTAIRE_P: commentP || null,
|
|
|
|
|
COMMENTAIRE_V: commentV || null,
|
|
|
|
|
COMMENTAIRE_A: commentA || null,
|
|
|
|
|
});
|
|
|
|
|
onUpdated?.();
|
|
|
|
|
@@ -168,7 +166,7 @@ export default function AppartCard({ annonce, onDelete, onUpdated }) {
|
|
|
|
|
setDeleting(false);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const avgNote = noteP || noteA ? ((noteP + noteA) / (noteP && noteA ? 2 : 1)).toFixed(1) : null;
|
|
|
|
|
const avgNote = noteV || noteA ? ((noteV + noteA) / (noteV && noteA ? 2 : 1)).toFixed(1) : null;
|
|
|
|
|
|
|
|
|
|
const dateDispo = formatFrDate(annonce.DATE_DISPO);
|
|
|
|
|
const dateAjout = formatFrDate(annonce.createdAt);
|
|
|
|
|
@@ -228,11 +226,12 @@ export default function AppartCard({ annonce, onDelete, onUpdated }) {
|
|
|
|
|
<div className="flex flex-wrap gap-x-4 gap-y-1.5">
|
|
|
|
|
<InfoChip icon={Maximize2} value={`${annonce.SURFACE ?? 0} m2`} />
|
|
|
|
|
<InfoChip icon={DoorOpen} value={`${annonce.NB_PIECE ?? 0} piece${(annonce.NB_PIECE ?? 0) > 1 ? "s" : ""}`} />
|
|
|
|
|
{annonce.DISTANCE_V > 0 && (
|
|
|
|
|
<InfoChip icon={Bus} label="Vatel" value={`${annonce.DISTANCE_V} min`} />
|
|
|
|
|
)}
|
|
|
|
|
{annonce.DISTANCE_S > 0 && (
|
|
|
|
|
<InfoChip icon={Car} label="Sup de Vinci" value={`${annonce.DISTANCE_S} min`} />
|
|
|
|
|
{annonce.ARRONDISSEMENT && (
|
|
|
|
|
<InfoChip
|
|
|
|
|
icon={MapPin}
|
|
|
|
|
label="Arrond."
|
|
|
|
|
value={`${annonce.ARRONDISSEMENT}${annonce.ARRONDISSEMENT === 1 ? "er" : "e"}`}
|
|
|
|
|
/>
|
|
|
|
|
)}
|
|
|
|
|
{dateDispo && <InfoChip icon={Calendar} label="Dispo" value={dateDispo} />}
|
|
|
|
|
{dateAjout && <InfoChip icon={Calendar} label="Ajout" value={dateAjout} />}
|
|
|
|
|
@@ -254,14 +253,14 @@ export default function AppartCard({ annonce, onDelete, onUpdated }) {
|
|
|
|
|
<span className="flex items-center gap-1.5">
|
|
|
|
|
<MessageSquare size={14} />
|
|
|
|
|
Notes et commentaires
|
|
|
|
|
{(hasPaulinFeedback || hasAgatheFeedback) && (
|
|
|
|
|
{(hasVincentFeedback || hasAntoineFeedback) && (
|
|
|
|
|
<span className="flex items-center gap-1">
|
|
|
|
|
{hasPaulinFeedback && (
|
|
|
|
|
{hasVincentFeedback && (
|
|
|
|
|
<span className="w-4 h-4 rounded-full bg-warm-500 text-white text-[9px] font-semibold flex items-center justify-center">
|
|
|
|
|
P
|
|
|
|
|
V
|
|
|
|
|
</span>
|
|
|
|
|
)}
|
|
|
|
|
{hasAgatheFeedback && (
|
|
|
|
|
{hasAntoineFeedback && (
|
|
|
|
|
<span className="w-4 h-4 rounded-full bg-pink-500 text-white text-[9px] font-semibold flex items-center justify-center">
|
|
|
|
|
A
|
|
|
|
|
</span>
|
|
|
|
|
@@ -280,35 +279,35 @@ export default function AppartCard({ annonce, onDelete, onUpdated }) {
|
|
|
|
|
|
|
|
|
|
{expanded && (
|
|
|
|
|
<div className="px-5 pb-4 space-y-3">
|
|
|
|
|
{/* Paulin */}
|
|
|
|
|
{/* Vincent */}
|
|
|
|
|
<div className="space-y-1.5">
|
|
|
|
|
<div className="flex items-center justify-between">
|
|
|
|
|
<span className="text-xs font-semibold text-warm-600 uppercase tracking-wide">
|
|
|
|
|
Paulin
|
|
|
|
|
Vincent
|
|
|
|
|
</span>
|
|
|
|
|
<StarRating value={noteP} onChange={setNoteP} />
|
|
|
|
|
<StarRating value={noteV} onChange={setNoteV} />
|
|
|
|
|
</div>
|
|
|
|
|
<textarea
|
|
|
|
|
value={commentP}
|
|
|
|
|
onChange={(e) => setCommentP(e.target.value)}
|
|
|
|
|
placeholder="L'avis de Paulin..."
|
|
|
|
|
value={commentV}
|
|
|
|
|
onChange={(e) => setCommentV(e.target.value)}
|
|
|
|
|
placeholder="L'avis de Vincent..."
|
|
|
|
|
rows={2}
|
|
|
|
|
className="w-full px-3 py-2 text-sm border border-sand-200 rounded-xl resize-none focus:outline-none focus:ring-2 focus:ring-warm-200 placeholder:text-sand-300 text-sand-800 bg-sand-50/50"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* Agathe */}
|
|
|
|
|
{/* Antoine */}
|
|
|
|
|
<div className="space-y-1.5">
|
|
|
|
|
<div className="flex items-center justify-between">
|
|
|
|
|
<span className="text-xs font-semibold text-pink-500 uppercase tracking-wide">
|
|
|
|
|
Agathe
|
|
|
|
|
Antoine
|
|
|
|
|
</span>
|
|
|
|
|
<StarRating value={noteA} onChange={setNoteA} />
|
|
|
|
|
</div>
|
|
|
|
|
<textarea
|
|
|
|
|
value={commentA}
|
|
|
|
|
onChange={(e) => setCommentA(e.target.value)}
|
|
|
|
|
placeholder="L'avis d'Agathe..."
|
|
|
|
|
placeholder="L'avis d'Antoine..."
|
|
|
|
|
rows={2}
|
|
|
|
|
className="w-full px-3 py-2 text-sm border border-sand-200 rounded-xl resize-none focus:outline-none focus:ring-2 focus:ring-pink-200 placeholder:text-sand-300 text-sand-800 bg-sand-50/50"
|
|
|
|
|
/>
|
|
|
|
|
|