tududi/app/frontend/components/Note/NoteDetails.tsx
Chris Veleris dfcb97a355 Move to React
Add .gitignore

Removed node_modules from previous commit

Fix task modes

Fix task modes

Fix task modes

Remove node_modules

Update basic task modal

Add notes functionality

Improve UI

Setup views

Add scopes

Fix projects layout

Restructure

Fix rest of the UI issues

Cleanup old views

Add .env to .gitignore
2024-10-25 21:03:43 +03:00

176 lines
6.3 KiB
TypeScript

import React, { useEffect, useState } from 'react';
import { useParams, Link, useNavigate } from 'react-router-dom';
import { PencilSquareIcon, TrashIcon, TagIcon } from '@heroicons/react/24/solid';
import { useDataContext } from '../../contexts/DataContext'; // Import the DataContext
import ConfirmDialog from '../Shared/ConfirmDialog';
import NoteModal from './NoteModal';
import { Note } from '../../entities/Note'; // Adjust path as necessary
const NoteDetails: React.FC = () => {
const { id } = useParams<{ id: string }>();
const { notes, deleteNote, isLoading, isError } = useDataContext(); // Get notes and deleteNote from context
const [note, setNote] = useState<Note | null>(null);
const [isNoteModalOpen, setIsNoteModalOpen] = useState(false); // State for the modal
const [isConfirmDialogOpen, setIsConfirmDialogOpen] = useState<boolean>(false);
const [noteToDelete, setNoteToDelete] = useState<Note | null>(null);
const navigate = useNavigate();
useEffect(() => {
// Find the note with the matching ID from the context
const foundNote = notes.find((n) => n.id === Number(id));
setNote(foundNote || null);
}, [id, notes]);
const handleDeleteNote = async () => {
if (!noteToDelete) return;
try {
await deleteNote(noteToDelete.id);
navigate('/notes'); // Navigate back to the notes list after deletion
} catch (err) {
console.error('Error deleting note:', err);
}
};
const handleSaveNote = (updatedNote: Note) => {
setNote(updatedNote); // Update the note after saving
setIsNoteModalOpen(false); // Close modal after saving
};
const handleEditNote = () => {
setIsNoteModalOpen(true); // Open the modal when editing
};
const handleOpenConfirmDialog = (note: Note) => {
setNoteToDelete(note);
setIsConfirmDialogOpen(true);
};
if (isLoading) {
return (
<div className="flex items-center justify-center h-screen bg-gray-100 dark:bg-gray-900">
<div className="text-xl font-semibold text-gray-700 dark:text-gray-200">
Loading note details...
</div>
</div>
);
}
if (isError || !note) {
return (
<div className="flex items-center justify-center h-screen bg-gray-100 dark:bg-gray-900">
<div className="text-red-500 text-lg">
{isError ? 'Error loading note details.' : 'Note not found.'}
</div>
</div>
);
}
return (
<div className="flex justify-center px-4">
<div className="w-full max-w-4xl">
{/* Header Section with Title and Action Buttons */}
<div className="flex items-center justify-between mb-4">
<div className="flex items-center">
<i className="bi bi-journal-text text-xl mr-2"></i>
<h2 className="text-2xl font-light text-gray-900 dark:text-gray-100">
{note.title}
</h2>
</div>
{/* Action Buttons */}
<div className="flex space-x-2">
<button
onClick={handleEditNote}
className="text-gray-500 hover:text-blue-700 dark:hover:text-blue-300 focus:outline-none"
aria-label={`Edit ${note.title}`}
title={`Edit ${note.title}`}
>
<PencilSquareIcon className="h-5 w-5" />
</button>
<button
onClick={() => handleOpenConfirmDialog(note)}
className="text-gray-500 hover:text-red-700 dark:hover:text-red-300 focus:outline-none"
aria-label={`Delete ${note.title}`}
title={`Delete ${note.title}`}
>
<TrashIcon className="h-5 w-5" />
</button>
</div>
</div>
{/* Card with Tags and Metadata */}
<div className="bg-white dark:bg-gray-900 shadow-md rounded-lg p-4 mb-6">
{/* Note Tags */}
{note.tags && note.tags.length > 0 && (
<div className="mb-4">
<div className="mt-2 flex flex-wrap space-x-2">
{note.tags.map((tag) => (
<button
key={tag.id}
onClick={() => navigate(`/tasks?tag=${tag.name}`)}
className="flex items-center space-x-1 px-2 py-1 bg-gray-100 dark:bg-gray-700 rounded-lg cursor-pointer hover:bg-gray-200 dark:hover:bg-gray-600"
>
<TagIcon className="h-4 w-4 text-gray-500 dark:text-gray-300" />
<span className="text-xs text-gray-700 dark:text-gray-300">{tag.name}</span>
</button>
))}
</div>
</div>
)}
{/* Note Metadata */}
<div className="text-sm text-gray-500 dark:text-gray-400">
<p>Created on: {new Date(note.created_at || '').toLocaleDateString()}</p>
<p>Last updated: {new Date(note.updated_at || '').toLocaleDateString()}</p>
</div>
{/* Note Project */}
{note.project && (
<div className="mt-4">
<h3 className="text-lg font-semibold text-gray-900 dark:text-white">Project</h3>
<Link
to={`/project/${note.project.id}`}
className="text-blue-600 dark:text-blue-400 hover:underline"
>
{note.project.name}
</Link>
</div>
)}
</div>
{/* Note Content */}
<div className="mb-6">
<p className="text-gray-700 dark:text-gray-300 whitespace-pre-line">
{note.content}
</p>
</div>
{/* NoteModal for editing */}
{isNoteModalOpen && (
<NoteModal
isOpen={isNoteModalOpen}
onClose={() => setIsNoteModalOpen(false)}
onSave={handleSaveNote}
note={note} // Pass the current note to the modal for editing
/>
)}
{/* ConfirmDialog */}
{isConfirmDialogOpen && noteToDelete && (
<ConfirmDialog
title="Delete Note"
message={`Are you sure you want to delete the note "${noteToDelete.title}"?`}
onConfirm={handleDeleteNote}
onCancel={() => {
setIsConfirmDialogOpen(false);
setNoteToDelete(null);
}}
/>
)}
</div>
</div>
);
};
export default NoteDetails;