carnet panel
This commit is contained in:
parent
c9d483223d
commit
2fc389a091
@ -214,6 +214,39 @@ export default function CarnetPage() {
|
||||
}
|
||||
};
|
||||
|
||||
const handleDeleteNote = async (note: Note) => {
|
||||
try {
|
||||
const response = await fetch(`/api/nextcloud/files`, {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
id: note.id,
|
||||
folder: selectedFolder
|
||||
}),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to delete note');
|
||||
}
|
||||
|
||||
// Refresh the notes list
|
||||
const notesResponse = await fetch(`/api/nextcloud/files?folder=${selectedFolder}`);
|
||||
if (notesResponse.ok) {
|
||||
const updatedNotes = await notesResponse.json();
|
||||
setNotes(updatedNotes);
|
||||
}
|
||||
|
||||
// If the deleted note was selected, clear the selection
|
||||
if (selectedNote?.id === note.id) {
|
||||
setSelectedNote(null);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error deleting note:', error);
|
||||
}
|
||||
};
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="flex h-screen items-center justify-center">
|
||||
@ -259,6 +292,7 @@ export default function CarnetPage() {
|
||||
onNoteSelect={handleNoteSelect}
|
||||
currentFolder={selectedFolder}
|
||||
onNewNote={handleNewNote}
|
||||
onDeleteNote={handleDeleteNote}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import { Image, FileText, Link, List, Plus } from 'lucide-react';
|
||||
|
||||
interface Note {
|
||||
@ -25,6 +25,7 @@ export const Editor: React.FC<EditorProps> = ({ note, onSave, currentFolder = 'N
|
||||
const [content, setContent] = useState(note?.content || '');
|
||||
const [isSaving, setIsSaving] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const saveTimeout = useRef<NodeJS.Timeout>();
|
||||
|
||||
useEffect(() => {
|
||||
const fetchNoteContent = async () => {
|
||||
@ -60,10 +61,21 @@ export const Editor: React.FC<EditorProps> = ({ note, onSave, currentFolder = 'N
|
||||
|
||||
const handleTitleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setTitle(e.target.value);
|
||||
debouncedSave();
|
||||
};
|
||||
|
||||
const handleContentChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
|
||||
setContent(e.target.value);
|
||||
debouncedSave();
|
||||
};
|
||||
|
||||
const debouncedSave = () => {
|
||||
if (saveTimeout.current) {
|
||||
clearTimeout(saveTimeout.current);
|
||||
}
|
||||
saveTimeout.current = setTimeout(() => {
|
||||
handleSave();
|
||||
}, 1000); // Save after 1 second of inactivity
|
||||
};
|
||||
|
||||
const handleSave = async () => {
|
||||
@ -99,7 +111,6 @@ export const Editor: React.FC<EditorProps> = ({ note, onSave, currentFolder = 'N
|
||||
onRefresh?.();
|
||||
} catch (error) {
|
||||
console.error('Error saving note:', error);
|
||||
// TODO: Show error message to user
|
||||
} finally {
|
||||
setIsSaving(false);
|
||||
}
|
||||
@ -134,28 +145,6 @@ export const Editor: React.FC<EditorProps> = ({ note, onSave, currentFolder = 'N
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Toolbar */}
|
||||
<div className="px-4 py-2 border-b border-carnet-border">
|
||||
<div className="flex space-x-1">
|
||||
<button className="p-1.5 rounded hover:bg-carnet-hover">
|
||||
<List className="h-4 w-4 text-carnet-text-muted" />
|
||||
</button>
|
||||
<button className="p-1.5 rounded hover:bg-carnet-hover">
|
||||
<Link className="h-4 w-4 text-carnet-text-muted" />
|
||||
</button>
|
||||
<button className="p-1.5 rounded hover:bg-carnet-hover">
|
||||
<Image className="h-4 w-4 text-carnet-text-muted" />
|
||||
</button>
|
||||
<button
|
||||
className={`p-1.5 rounded hover:bg-carnet-hover ${isSaving ? 'opacity-50 cursor-not-allowed' : ''}`}
|
||||
onClick={handleSave}
|
||||
disabled={isSaving}
|
||||
>
|
||||
<FileText className="h-4 w-4 text-carnet-text-muted" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Editor Area */}
|
||||
<div className="flex-1 p-4">
|
||||
{isLoading ? (
|
||||
@ -171,6 +160,11 @@ export const Editor: React.FC<EditorProps> = ({ note, onSave, currentFolder = 'N
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
{isSaving && (
|
||||
<div className="absolute bottom-4 right-4 text-sm text-carnet-text-muted">
|
||||
Enregistrement...
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@ -21,6 +21,7 @@ interface NotesViewProps {
|
||||
currentFolder?: string;
|
||||
onNewNote?: () => void;
|
||||
loading?: boolean;
|
||||
onDeleteNote?: (note: Note) => void;
|
||||
}
|
||||
|
||||
export const NotesView: React.FC<NotesViewProps> = ({
|
||||
@ -28,9 +29,11 @@ export const NotesView: React.FC<NotesViewProps> = ({
|
||||
onNoteSelect,
|
||||
currentFolder = 'Notes',
|
||||
onNewNote,
|
||||
loading = false
|
||||
loading = false,
|
||||
onDeleteNote
|
||||
}) => {
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const [hoveredNote, setHoveredNote] = useState<string | null>(null);
|
||||
|
||||
const formatDate = (dateString: string) => {
|
||||
return format(new Date(dateString), 'EEEE d MMM yyyy', { locale: fr });
|
||||
@ -125,32 +128,49 @@ export const NotesView: React.FC<NotesViewProps> = ({
|
||||
{sortNotes(notes).map((note) => (
|
||||
<li
|
||||
key={note.id}
|
||||
onClick={() => onNoteSelect?.(note)}
|
||||
className="p-4 hover:bg-carnet-hover cursor-pointer"
|
||||
onMouseEnter={() => setHoveredNote(note.id)}
|
||||
onMouseLeave={() => setHoveredNote(null)}
|
||||
className="p-4 hover:bg-carnet-hover cursor-pointer group"
|
||||
>
|
||||
<div className="flex items-center space-x-2">
|
||||
<Icon className="h-4 w-4 text-carnet-text-muted" />
|
||||
<div className="flex-1">
|
||||
{currentFolder === 'Diary' || currentFolder === 'Health' ? (
|
||||
<>
|
||||
<div className="text-sm font-medium text-carnet-text-primary">
|
||||
{formatDate(note.lastModified)}
|
||||
</div>
|
||||
<div className="text-sm text-carnet-text-muted">
|
||||
{formatNoteTitle(note)}
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<div className="text-sm font-medium text-carnet-text-primary">
|
||||
{note.title}
|
||||
</div>
|
||||
<div className="text-xs text-carnet-text-muted">
|
||||
{formatDate(note.lastModified)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
<div className="flex items-center justify-between">
|
||||
<div
|
||||
className="flex items-center space-x-2 flex-1"
|
||||
onClick={() => onNoteSelect?.(note)}
|
||||
>
|
||||
<Icon className="h-4 w-4 text-carnet-text-muted" />
|
||||
<div className="flex-1">
|
||||
{currentFolder === 'Diary' || currentFolder === 'Health' ? (
|
||||
<>
|
||||
<div className="text-sm font-medium text-carnet-text-primary">
|
||||
{formatDate(note.lastModified)}
|
||||
</div>
|
||||
<div className="text-sm text-carnet-text-muted">
|
||||
{formatNoteTitle(note)}
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<div className="text-sm font-medium text-carnet-text-primary">
|
||||
{note.title}
|
||||
</div>
|
||||
<div className="text-xs text-carnet-text-muted">
|
||||
{formatDate(note.lastModified)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{currentFolder === 'Notes' && hoveredNote === note.id && (
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onDeleteNote?.(note);
|
||||
}}
|
||||
className="p-1 text-carnet-text-muted hover:text-red-500 opacity-0 group-hover:opacity-100 transition-opacity"
|
||||
>
|
||||
<X className="h-4 w-4" />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user