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) {
|
if (isLoading) {
|
||||||
return (
|
return (
|
||||||
<div className="flex h-screen items-center justify-center">
|
<div className="flex h-screen items-center justify-center">
|
||||||
@ -259,6 +292,7 @@ export default function CarnetPage() {
|
|||||||
onNoteSelect={handleNoteSelect}
|
onNoteSelect={handleNoteSelect}
|
||||||
currentFolder={selectedFolder}
|
currentFolder={selectedFolder}
|
||||||
onNewNote={handleNewNote}
|
onNewNote={handleNewNote}
|
||||||
|
onDeleteNote={handleDeleteNote}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect, useRef } from 'react';
|
||||||
import { Image, FileText, Link, List, Plus } from 'lucide-react';
|
import { Image, FileText, Link, List, Plus } from 'lucide-react';
|
||||||
|
|
||||||
interface Note {
|
interface Note {
|
||||||
@ -25,6 +25,7 @@ export const Editor: React.FC<EditorProps> = ({ note, onSave, currentFolder = 'N
|
|||||||
const [content, setContent] = useState(note?.content || '');
|
const [content, setContent] = useState(note?.content || '');
|
||||||
const [isSaving, setIsSaving] = useState(false);
|
const [isSaving, setIsSaving] = useState(false);
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
const saveTimeout = useRef<NodeJS.Timeout>();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchNoteContent = async () => {
|
const fetchNoteContent = async () => {
|
||||||
@ -60,10 +61,21 @@ export const Editor: React.FC<EditorProps> = ({ note, onSave, currentFolder = 'N
|
|||||||
|
|
||||||
const handleTitleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
const handleTitleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
setTitle(e.target.value);
|
setTitle(e.target.value);
|
||||||
|
debouncedSave();
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleContentChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
|
const handleContentChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
|
||||||
setContent(e.target.value);
|
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 () => {
|
const handleSave = async () => {
|
||||||
@ -99,7 +111,6 @@ export const Editor: React.FC<EditorProps> = ({ note, onSave, currentFolder = 'N
|
|||||||
onRefresh?.();
|
onRefresh?.();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error saving note:', error);
|
console.error('Error saving note:', error);
|
||||||
// TODO: Show error message to user
|
|
||||||
} finally {
|
} finally {
|
||||||
setIsSaving(false);
|
setIsSaving(false);
|
||||||
}
|
}
|
||||||
@ -134,28 +145,6 @@ export const Editor: React.FC<EditorProps> = ({ note, onSave, currentFolder = 'N
|
|||||||
/>
|
/>
|
||||||
</div>
|
</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 */}
|
{/* Editor Area */}
|
||||||
<div className="flex-1 p-4">
|
<div className="flex-1 p-4">
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
@ -171,6 +160,11 @@ export const Editor: React.FC<EditorProps> = ({ note, onSave, currentFolder = 'N
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
{isSaving && (
|
||||||
|
<div className="absolute bottom-4 right-4 text-sm text-carnet-text-muted">
|
||||||
|
Enregistrement...
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@ -21,6 +21,7 @@ interface NotesViewProps {
|
|||||||
currentFolder?: string;
|
currentFolder?: string;
|
||||||
onNewNote?: () => void;
|
onNewNote?: () => void;
|
||||||
loading?: boolean;
|
loading?: boolean;
|
||||||
|
onDeleteNote?: (note: Note) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const NotesView: React.FC<NotesViewProps> = ({
|
export const NotesView: React.FC<NotesViewProps> = ({
|
||||||
@ -28,9 +29,11 @@ export const NotesView: React.FC<NotesViewProps> = ({
|
|||||||
onNoteSelect,
|
onNoteSelect,
|
||||||
currentFolder = 'Notes',
|
currentFolder = 'Notes',
|
||||||
onNewNote,
|
onNewNote,
|
||||||
loading = false
|
loading = false,
|
||||||
|
onDeleteNote
|
||||||
}) => {
|
}) => {
|
||||||
const [searchQuery, setSearchQuery] = useState('');
|
const [searchQuery, setSearchQuery] = useState('');
|
||||||
|
const [hoveredNote, setHoveredNote] = useState<string | null>(null);
|
||||||
|
|
||||||
const formatDate = (dateString: string) => {
|
const formatDate = (dateString: string) => {
|
||||||
return format(new Date(dateString), 'EEEE d MMM yyyy', { locale: fr });
|
return format(new Date(dateString), 'EEEE d MMM yyyy', { locale: fr });
|
||||||
@ -125,10 +128,15 @@ export const NotesView: React.FC<NotesViewProps> = ({
|
|||||||
{sortNotes(notes).map((note) => (
|
{sortNotes(notes).map((note) => (
|
||||||
<li
|
<li
|
||||||
key={note.id}
|
key={note.id}
|
||||||
onClick={() => onNoteSelect?.(note)}
|
onMouseEnter={() => setHoveredNote(note.id)}
|
||||||
className="p-4 hover:bg-carnet-hover cursor-pointer"
|
onMouseLeave={() => setHoveredNote(null)}
|
||||||
|
className="p-4 hover:bg-carnet-hover cursor-pointer group"
|
||||||
|
>
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div
|
||||||
|
className="flex items-center space-x-2 flex-1"
|
||||||
|
onClick={() => onNoteSelect?.(note)}
|
||||||
>
|
>
|
||||||
<div className="flex items-center space-x-2">
|
|
||||||
<Icon className="h-4 w-4 text-carnet-text-muted" />
|
<Icon className="h-4 w-4 text-carnet-text-muted" />
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
{currentFolder === 'Diary' || currentFolder === 'Health' ? (
|
{currentFolder === 'Diary' || currentFolder === 'Health' ? (
|
||||||
@ -152,6 +160,18 @@ export const NotesView: React.FC<NotesViewProps> = ({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</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>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user