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
176 lines
6.3 KiB
TypeScript
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;
|