From 9d039d20c4561f2aebf9ea57620e8e63cfd7d500 Mon Sep 17 00:00:00 2001 From: Michael SCHAL Date: Sun, 23 Nov 2025 13:52:41 +0100 Subject: [PATCH 1/2] feat: Implement new React frontend with authentication, routing, theming, and add comprehensive calendar timezone management and database migration scripts. --- src/pages/Index.tsx | 632 ++++++++++++++-------------- src/pages/mobile/NoteDetailPage.tsx | 10 +- src/services/NotesService.ts | 44 +- 3 files changed, 360 insertions(+), 326 deletions(-) diff --git a/src/pages/Index.tsx b/src/pages/Index.tsx index a23c1b2..1478d98 100644 --- a/src/pages/Index.tsx +++ b/src/pages/Index.tsx @@ -142,13 +142,13 @@ const Index = () => { const [addTodoModal, setAddTodoModal] = useState(false); const [addRssFeedModal, setAddRssFeedModal] = useState(false); const [addUserModal, setAddUserModal] = useState(false); - const [deleteUserModal, setDeleteUserModal] = useState<{open: boolean, userId?: number}>({open: false}); - const [changePasswordModal, setChangePasswordModal] = useState<{open: boolean, userId?: number}>({open: false}); + const [deleteUserModal, setDeleteUserModal] = useState<{ open: boolean, userId?: number }>({ open: false }); + const [changePasswordModal, setChangePasswordModal] = useState<{ open: boolean, userId?: number }>({ open: false }); const [addNoteTodoModal, setAddNoteTodoModal] = useState(false); const [addTagModal, setAddTagModal] = useState(false); const [selectedImage, setSelectedImage] = useState(null); const [addEventModal, setAddEventModal] = useState(false); - const [editEventModal, setEditEventModal] = useState<{open: boolean, event?: CalendarEvent}>({open: false}); + const [editEventModal, setEditEventModal] = useState<{ open: boolean, event?: CalendarEvent }>({ open: false }); // Pagination states const [notesPage, setNotesPage] = useState(0); @@ -796,8 +796,8 @@ const Index = () => { const filteredNotes = notes.filter(note => (showArchived ? note.archived : !note.archived) && (searchQuery === "" || - (note.title && note.title.toLowerCase().includes(searchQuery.toLowerCase())) || - (note.content && note.content.toLowerCase().includes(searchQuery.toLowerCase()))) + (note.title && note.title.toLowerCase().includes(searchQuery.toLowerCase())) || + (note.content && note.content.toLowerCase().includes(searchQuery.toLowerCase()))) ); // Pagination for notes @@ -995,10 +995,9 @@ const Index = () => { return (
{ -
+
+ { + const updatedTodos = [...(openNote.todos || [])]; + updatedTodos[index] = { ...todo, completed: !todo.completed }; + await handleUpdateNote({ todos: updatedTodos }); + }} + /> + + {todo.text} + + +
))}
@@ -1451,54 +1459,54 @@ const Index = () => {
{openNote.files.map((file: any, index: number) => ( -
- -
-

{file.original_name || file.filename}

- {file.size && ( -

- {(file.size / 1024).toFixed(2)} KB -

- )} -
-
- - + + }} + > + + +
-
))}
@@ -1555,228 +1563,228 @@ const Index = () => { {/* Right Column: Todos & RSS Boxes side by side */}
- {/* Todos Box */} - - -
- - - Tâches - - -
-
- - - - Actives ({todos.filter(t => !t.completed).length}) - Complétées ({todos.filter(t => t.completed).length}) - - - -
- {activeTodos.length > 0 ? ( - paginatedActiveTodos.map(todo => ( -
- todo.id && handleToggleTodo(todo.id)} - className="h-4 w-4" - /> - - {todo.text} - - - -
- )) - ) : ( -

Aucune tâche active

- )} -
- {totalActiveTodosPages > 1 && ( -
- - - {todosActivePage + 1}/{totalActiveTodosPages} - - -
- )} -
- - -
- {completedTodos.length > 0 ? ( - paginatedCompletedTodos.map(todo => ( -
- todo.id && handleToggleTodo(todo.id)} - className="h-4 w-4" - /> - - {todo.text} - - {todo.priority && ( - - )} - -
- )) - ) : ( -

Aucune tâche complétée

- )} -
- {totalCompletedTodosPages > 1 && ( -
- - - {todosCompletedPage + 1}/{totalCompletedTodosPages} - - -
- )} -
-
-
-
- - {/* RSS Box */} - - -
- - - Flux RSS - -
- -
-
-
- -
- {rssArticles.length > 0 ? ( - paginatedRssArticles.map(article => ( -
article.link && window.open(article.link, '_blank')} - > -
-
-

{article.title}

- {article.description && ( -

- {article.description.replace(/<[^>]*>/g, '')} -

- )} -
- -
-
- )) - ) : ( -

- Aucun article -
- -

- )} -
- {totalRssPages > 1 && ( -
- - - {rssPage + 1}/{totalRssPages} - - -
- )} -
-
+ + + + + Actives ({todos.filter(t => !t.completed).length}) + Complétées ({todos.filter(t => t.completed).length}) + + +
+ {activeTodos.length > 0 ? ( + paginatedActiveTodos.map(todo => ( +
+ todo.id && handleToggleTodo(todo.id)} + className="h-4 w-4" + /> + + {todo.text} + + + +
+ )) + ) : ( +

Aucune tâche active

+ )} +
+ {totalActiveTodosPages > 1 && ( +
+ + + {todosActivePage + 1}/{totalActiveTodosPages} + + +
+ )} +
+ + +
+ {completedTodos.length > 0 ? ( + paginatedCompletedTodos.map(todo => ( +
+ todo.id && handleToggleTodo(todo.id)} + className="h-4 w-4" + /> + + {todo.text} + + {todo.priority && ( + + )} + +
+ )) + ) : ( +

Aucune tâche complétée

+ )} +
+ {totalCompletedTodosPages > 1 && ( +
+ + + {todosCompletedPage + 1}/{totalCompletedTodosPages} + + +
+ )} +
+
+
+ + + {/* RSS Box */} + + +
+ + + Flux RSS + +
+ + +
+
+
+ +
+ {rssArticles.length > 0 ? ( + paginatedRssArticles.map(article => ( +
article.link && window.open(article.link, '_blank')} + > +
+
+

{article.title}

+ {article.description && ( +

+ {article.description.replace(/<[^>]*>/g, '')} +

+ )} +
+ +
+
+ )) + ) : ( +

+ Aucun article +
+ +

+ )} +
+ {totalRssPages > 1 && ( +
+ + + {rssPage + 1}/{totalRssPages} + + +
+ )} +
+
+ +
- {/* Admin Modal */} @@ -1825,7 +1833,7 @@ const Index = () => { @@ -2116,7 +2124,7 @@ const Index = () => { setDeleteUserModal({open})} + onOpenChange={(open) => setDeleteUserModal({ open })} title="Supprimer l'utilisateur" description="Êtes-vous sûr de vouloir supprimer cet utilisateur ? Cette action est irréversible." onConfirm={confirmDeleteUser} @@ -2126,7 +2134,7 @@ const Index = () => { setChangePasswordModal({open})} + onOpenChange={(open) => setChangePasswordModal({ open })} title="Changer le mot de passe" label="Nouveau mot de passe" placeholder="Entrez le nouveau mot de passe..." @@ -2225,7 +2233,7 @@ const Index = () => { name="startDateTime" type="datetime-local" required - defaultValue={toLocalDateTimeString(new Date(Date.now() + 60*60*1000))} + defaultValue={toLocalDateTimeString(new Date(Date.now() + 60 * 60 * 1000))} />
@@ -2235,7 +2243,7 @@ const Index = () => { name="endDateTime" type="datetime-local" required - defaultValue={toLocalDateTimeString(new Date(Date.now() + 2*60*60*1000))} + defaultValue={toLocalDateTimeString(new Date(Date.now() + 2 * 60 * 60 * 1000))} />
@@ -2337,7 +2345,7 @@ const Index = () => {
{/* Edit Event Modal */} - setEditEventModal({open, event: undefined})}> + setEditEventModal({ open, event: undefined })}> Modifier le rendez-vous @@ -2373,7 +2381,7 @@ const Index = () => { await CalendarService.updateEvent(editEventModal.event.id, eventData); showSuccess('Événement mis à jour avec succès'); - setEditEventModal({open: false, event: undefined}); + setEditEventModal({ open: false, event: undefined }); await loadCalendarEvents(); } catch (error: any) { showError(error.response?.data?.error || 'Erreur lors de la mise à jour de l\'événement'); @@ -2504,7 +2512,7 @@ const Index = () => {
- - updateNote({ archived: !note.archived })}> + { + 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"} diff --git a/src/services/NotesService.ts b/src/services/NotesService.ts index aa383d9..c4d338a 100644 --- a/src/services/NotesService.ts +++ b/src/services/NotesService.ts @@ -41,13 +41,13 @@ class NotesService { const response = await fetch("/api/notes", { headers: AuthService.getHeaders() }); - + if (!response.ok) { throw new Error("Erreur lors de la récupération des notes"); } - + const notes = await response.json(); - + // Ensure todos and images are always arrays return notes.map((note: Note) => ({ ...note, @@ -59,7 +59,7 @@ class NotesService { return []; } } - + async createNote(title: string, content: string = ""): Promise { try { const response = await fetch("/api/notes", { @@ -67,11 +67,11 @@ class NotesService { headers: AuthService.getHeaders(), body: JSON.stringify({ title, content, todos: [] }) }); - + if (!response.ok) { throw new Error("Erreur lors de la création de la note"); } - + showSuccess("Note créée avec succès"); const note = await response.json(); return { @@ -84,7 +84,7 @@ class NotesService { return null; } } - + async updateNote(note: Note): Promise { if (!note.id) return null; @@ -118,18 +118,18 @@ class NotesService { return null; } } - + async deleteNote(noteId: number): Promise { try { const response = await fetch(`/api/notes/${noteId}`, { method: "DELETE", headers: AuthService.getHeaders() }); - + if (!response.ok) { throw new Error("Erreur lors de la suppression de la note"); } - + showSuccess("Note supprimée avec succès"); return true; } catch (error) { @@ -137,7 +137,7 @@ class NotesService { return false; } } - + async uploadImage(noteId: number, file: File): Promise { const formData = new FormData(); formData.append("image", file); @@ -162,7 +162,7 @@ class NotesService { return null; } } - + async deleteImage(noteId: number, imageId: number): Promise { try { const response = await fetch(`/api/notes/${noteId}/images/${imageId}`, { @@ -199,6 +199,26 @@ class NotesService { return false; } } + + async archiveNote(noteId: number, archived: boolean): Promise { + try { + const response = await fetch(`/api/notes/${noteId}/archive`, { + method: "PUT", + headers: AuthService.getHeaders(), + body: JSON.stringify({ archived }) + }); + + if (!response.ok) { + throw new Error("Erreur lors de l'archivage de la note"); + } + + showSuccess(archived ? "Note archivée" : "Note désarchivée"); + return true; + } catch (error) { + showError(error instanceof Error ? error.message : "Erreur serveur"); + return false; + } + } } export default new NotesService(); \ No newline at end of file From 623cb2f4f8e5c49707bf44c6b86bf637ff622811 Mon Sep 17 00:00:00 2001 From: Michael SCHAL Date: Sun, 23 Nov 2025 14:02:54 +0100 Subject: [PATCH 2/2] arichivage --- src/pages/Index.tsx | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/src/pages/Index.tsx b/src/pages/Index.tsx index 1478d98..3516ee1 100644 --- a/src/pages/Index.tsx +++ b/src/pages/Index.tsx @@ -996,7 +996,7 @@ const Index = () => {
@@ -1150,6 +1150,27 @@ const Index = () => { )} + {/* Archive icon */} + +

{note.title || "Sans titre"}

@@ -2670,8 +2691,8 @@ const Index = () => { >
{msg.content}