import { useEffect, useState } from "react"; import { useParams, useNavigate } from "react-router-dom"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Badge } from "@/components/ui/badge"; import { Checkbox } from "@/components/ui/checkbox"; import { Card, CardContent } from "@/components/ui/card"; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from "@/components/ui/dialog"; import { MobileHeader } from "@/components/mobile/MobileHeader"; import { RichTextEditor } from "@/components/RichTextEditor"; import { ArrowLeft, Archive, Trash2, CheckSquare, Image as ImageIcon, Paperclip, Tag as TagIcon, X, MoreVertical, FileDown } from "lucide-react"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import NotesService, { Note } from "@/services/NotesService"; import { showError, showSuccess } from "@/utils/toast"; import { compressImage, formatFileSize } from "@/utils/imageCompression"; import { downloadNoteAsMarkdown } from "@/utils/markdownExport"; export default function NoteDetailPage() { const { id } = useParams(); const navigate = useNavigate(); const [note, setNote] = useState(null); const [addTodoModal, setAddTodoModal] = useState(false); const [todoText, setTodoText] = useState(""); const [deleteModal, setDeleteModal] = useState(false); const [selectedImage, setSelectedImage] = useState(null); useEffect(() => { loadNote(); }, [id]); const loadNote = async () => { if (!id) return; try { const notes = await NotesService.getNotes(); const foundNote = notes.find(n => n.id === parseInt(id)); if (foundNote) { setNote(foundNote); } else { showError("Note introuvable"); navigate("/mobile/notes"); } } catch (error) { showError("Erreur lors du chargement de la note"); } }; const updateNote = async (updates: Partial) => { if (!note) return; try { const updated = { ...note, ...updates }; setNote(updated); await NotesService.updateNote(updated); } catch (error) { showError("Erreur lors de la sauvegarde"); } }; const handleDelete = async () => { if (!note?.id) return; try { await NotesService.deleteNote(note.id); showSuccess("Note supprimée"); navigate("/mobile/notes"); } catch (error) { showError("Erreur lors de la suppression"); } }; const handleAddTodo = async () => { if (!note?.id || !todoText.trim()) return; const newTodo = await NotesService.addTodo(note.id, todoText.trim()); if (newTodo) { const updatedTodos = [...(note.todos || []), newTodo]; setNote({ ...note, todos: updatedTodos }); setTodoText(""); setAddTodoModal(false); } }; const handleToggleTodo = async (index: number) => { if (!note?.todos) return; const todo = note.todos[index]; if (!todo.id) return; const success = await NotesService.toggleTodo(todo.id, !todo.completed); if (success) { const updatedTodos = [...note.todos]; updatedTodos[index] = { ...updatedTodos[index], completed: !updatedTodos[index].completed }; setNote({ ...note, todos: updatedTodos }); } }; const handleDeleteTodo = async (index: number) => { if (!note?.todos) return; const todo = note.todos[index]; if (!todo.id) return; const success = await NotesService.deleteTodo(todo.id); if (success) { const updatedTodos = note.todos.filter((_, i) => i !== index); setNote({ ...note, todos: updatedTodos }); showSuccess("Tâche supprimée"); } }; const handleImageUpload = async (e: React.ChangeEvent) => { if (!e.target.files || !e.target.files[0] || !note?.id) return; const originalFile = e.target.files[0]; const originalSize = formatFileSize(originalFile.size); try { // Compresser l'image avant l'upload const compressedFile = await compressImage(originalFile); const compressedSize = formatFileSize(compressedFile.size); // Afficher un message si l'image a été compressée if (compressedFile.size < originalFile.size) { showSuccess(`Image compressée: ${originalSize} → ${compressedSize}`); } // Vérifier la taille finale (5MB max après compression) const maxSize = 5 * 1024 * 1024; // 5MB if (compressedFile.size > maxSize) { showError("L'image est trop grande même après compression (max 5MB)"); e.target.value = ''; return; } const image = await NotesService.uploadImage(note.id, compressedFile); if (image) { const updatedImages = [...(note.images || []), image]; setNote({ ...note, images: updatedImages }); showSuccess("Image ajoutée"); } else { showError("Erreur lors de l'upload de l'image"); } } catch (error) { showError(error instanceof Error ? error.message : "Erreur lors de l'upload"); } e.target.value = ''; }; const handleDeleteImage = async (imageId: number, index: number) => { if (!note?.id) return; try { await NotesService.deleteImage(note.id, imageId); const updatedImages = (note.images || []).filter((_, i) => i !== index); setNote({ ...note, images: updatedImages }); showSuccess("Image supprimée"); } catch (error) { showError("Erreur lors de la suppression"); } }; if (!note) { return (
); } return (
{/* Fixed Header */}

{note.title || "Sans titre"}

{ if (note) { downloadNoteAsMarkdown(note); showSuccess("Note exportée en Markdown"); } }}> Exporter en Markdown { if (note?.id) { const newArchived = !note.archived; const success = await NotesService.archiveNote(note.id, newArchived); if (success) { setNote({ ...note, archived: newArchived }); } } }}> {note.archived ? "Désarchiver" : "Archiver"} setDeleteModal(true)} className="text-red-600"> Supprimer
{/* Scrollable Content */}
{/* Title */} updateNote({ title: e.target.value })} /> {/* Action Buttons */}
{/* Todos */} {note.todos && note.todos.length > 0 && ( {note.todos.map((todo, index) => (
handleToggleTodo(index)} /> {todo.text}
))}
)} {/* Images */} {note.images && note.images.length > 0 && (
{note.images.map((image, index) => (
{image.original_name} setSelectedImage(`/uploads/images/${image.filename}`)} />
))}
)} {/* Rich Text Editor */}
updateNote({ content })} />
{/* Add Todo Modal */} Ajouter une tâche setTodoText(e.target.value)} onKeyDown={(e) => e.key === 'Enter' && handleAddTodo()} /> {/* Delete Confirmation */} Supprimer la note ?

Cette action est irréversible.

{/* Image Preview */} setSelectedImage(null)}> Preview
); }