Feature: Remplacement de tous les prompts par des modaux + Fix RSS refresh

Modaux :
- Création ConfirmModal pour confirmations avec variants (default/destructive)
- Création InputModal pour saisie de texte/URL/mot de passe
- Création AddUserModal pour création d'utilisateur avec admin checkbox
- Remplacement de tous les prompt() et confirm() par des modaux modernes
- Modaux pour : suppression note, ajout todo, ajout RSS, ajout utilisateur, suppression utilisateur, changement mot de passe, ajout todo dans note

Fix RSS :
- Ajout route POST /api/rss/refresh (alias de /api/rss/fetch)
- Fix uploadImage retour avec result.image || result

UX améliorée :
- Plus de popups navigateur natifs
- Interface cohérente et moderne
- Validation dans les modaux
- Support touche Entrée dans les InputModals
- Auto-focus sur le champ de saisie
This commit is contained in:
Claude committed 2025-11-12 09:24:16 +00:00
1 parent 702d0c2088
commit f8ec63b5ef
6 files changed
+409 -81

No files matched your search

+143 -77
View File
@@ -24,6 +24,9 @@ import SettingsService, { Settings as AppSettings } from "@/services/SettingsSer
import CalendarService, { CalendarEvent } from "@/services/CalendarService";
import { useNavigate } from "react-router-dom";
import { showError, showSuccess } from "@/utils/toast";
import { ConfirmModal } from "@/components/modals/ConfirmModal";
import { InputModal } from "@/components/modals/InputModal";
import { AddUserModal } from "@/components/modals/AddUserModal";
interface UserType {
id: number;
@@ -47,6 +50,15 @@ const Index = () => {
const [showArchived, setShowArchived] = useState(false);
const navigate = useNavigate();
// Modal states
const [deleteNoteModal, setDeleteNoteModal] = useState(false);
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 [addNoteTodoModal, setAddNoteTodoModal] = useState(false);
useEffect(() => {
const userFromAuth = AuthService.getUser();
if (userFromAuth) {
@@ -195,37 +207,32 @@ const Index = () => {
}
};
const handleDeleteNote = async () => {
const confirmDeleteNote = async () => {
if (!openNote?.id) return;
if (window.confirm("Êtes-vous sûr de vouloir supprimer cette note ?")) {
try {
const success = await NotesService.deleteNote(openNote.id);
try {
const success = await NotesService.deleteNote(openNote.id);
if (success) {
const updatedNotes = notes.filter(note => note.id !== openNote.id);
setNotes(updatedNotes);
setOpenNote(null);
showSuccess("Note supprimée");
}
} catch (error) {
showError("Erreur lors de la suppression");
if (success) {
const updatedNotes = notes.filter(note => note.id !== openNote.id);
setNotes(updatedNotes);
setOpenNote(null);
showSuccess("Note supprimée");
}
} catch (error) {
showError("Erreur lors de la suppression");
}
};
const handleAddTodo = async () => {
const text = prompt("Nouvelle tâche:");
if (text) {
try {
const newTodo = await TodosService.createTodo(text);
if (newTodo) {
setTodos([newTodo, ...todos]);
showSuccess("Tâche ajoutée");
}
} catch (error) {
showError("Erreur lors de l'ajout de la tâche");
const confirmAddTodo = async (text: string) => {
try {
const newTodo = await TodosService.createTodo(text);
if (newTodo) {
setTodos([newTodo, ...todos]);
showSuccess("Tâche ajoutée");
}
} catch (error) {
showError("Erreur lors de l'ajout de la tâche");
}
};
@@ -248,19 +255,16 @@ const Index = () => {
}
};
const handleAddRssFeed = async () => {
const url = prompt("URL du flux RSS:");
if (url) {
try {
const newFeed = await RssService.addFeed(url);
if (newFeed) {
setRssFeeds([...rssFeeds, newFeed]);
showSuccess("Flux RSS ajouté");
loadRssArticles();
}
} catch (error) {
showError("Erreur lors de l'ajout du flux");
const confirmAddRssFeed = async (url: string) => {
try {
const newFeed = await RssService.addFeed(url);
if (newFeed) {
setRssFeeds([...rssFeeds, newFeed]);
showSuccess("Flux RSS ajouté");
loadRssArticles();
}
} catch (error) {
showError("Erreur lors de l'ajout du flux");
}
};
@@ -285,13 +289,7 @@ const Index = () => {
}
};
const handleAddUser = async () => {
const username = prompt("Nom d'utilisateur:");
if (!username) return;
const password = prompt("Mot de passe:");
if (!password) return;
const isAdmin = window.confirm("Donner les droits administrateur ?");
const confirmAddUser = async (username: string, password: string, isAdmin: boolean) => {
try {
const newUser = await AdminService.createUser(username, password, isAdmin);
if (newUser) {
@@ -303,31 +301,30 @@ const Index = () => {
}
};
const handleDeleteUser = async (userId: number) => {
if (window.confirm("Êtes-vous sûr de vouloir supprimer cet utilisateur ?")) {
try {
const success = await AdminService.deleteUser(userId);
if (success) {
loadUsers();
showSuccess("Utilisateur supprimé");
}
} catch (error) {
showError("Erreur lors de la suppression");
const confirmDeleteUser = async () => {
if (!deleteUserModal.userId) return;
try {
const success = await AdminService.deleteUser(deleteUserModal.userId);
if (success) {
loadUsers();
showSuccess("Utilisateur supprimé");
}
} catch (error) {
showError("Erreur lors de la suppression");
}
};
const handleChangePassword = async (userId: number) => {
const newPassword = prompt("Nouveau mot de passe:");
if (newPassword) {
try {
const success = await AdminService.updateUserPassword(userId, newPassword);
if (success) {
showSuccess("Mot de passe modifié");
}
} catch (error) {
showError("Erreur lors de la modification");
const confirmChangePassword = async (newPassword: string) => {
if (!changePasswordModal.userId) return;
try {
const success = await AdminService.updateUserPassword(changePasswordModal.userId, newPassword);
if (success) {
showSuccess("Mot de passe modifié");
}
} catch (error) {
showError("Erreur lors de la modification");
}
};
@@ -548,7 +545,7 @@ const Index = () => {
<Button
variant="outline"
size="icon"
onClick={handleDeleteNote}
onClick={() => setDeleteNoteModal(true)}
title="Supprimer"
>
<Trash2 className="h-4 w-4" />
@@ -596,13 +593,7 @@ const Index = () => {
<TabsContent value="todos" className="mt-4 space-y-4">
<Button
onClick={async () => {
const text = prompt("Nouvelle tâche:");
if (text) {
const updatedTodos = [...(openNote.todos || []), { text, completed: false }];
await handleUpdateNote({ todos: updatedTodos });
}
}}
onClick={() => setAddNoteTodoModal(true)}
className="w-full"
>
<Plus className="h-4 w-4 mr-2" />
@@ -780,7 +771,7 @@ const Index = () => {
<CheckSquare className="h-6 w-6" />
Tâches
</CardTitle>
<Button size="lg" variant="outline" onClick={handleAddTodo} className="gap-2">
<Button size="lg" variant="outline" onClick={() => setAddTodoModal(true)} className="gap-2">
<Plus className="h-5 w-5" />
Ajouter
</Button>
@@ -867,7 +858,7 @@ const Index = () => {
<RefreshCw className="h-5 w-5" />
Actualiser
</Button>
<Button size="lg" variant="outline" onClick={handleAddRssFeed} className="gap-2">
<Button size="lg" variant="outline" onClick={() => setAddRssFeedModal(true)} className="gap-2">
<Plus className="h-5 w-5" />
Ajouter
</Button>
@@ -903,7 +894,7 @@ const Index = () => {
<Button
variant="link"
className="mt-2"
onClick={handleAddRssFeed}
onClick={() => setAddRssFeedModal(true)}
>
Ajouter un flux RSS
</Button>
@@ -1008,7 +999,7 @@ const Index = () => {
<TabsContent value="users" className="mt-4 space-y-4">
<Button
className="flex items-center gap-2"
onClick={handleAddUser}
onClick={() => setAddUserModal(true)}
>
<User className="h-4 w-4" />
Ajouter un utilisateur
@@ -1033,7 +1024,7 @@ const Index = () => {
<Button
variant="outline"
size="sm"
onClick={() => handleChangePassword(u.id)}
onClick={() => setChangePasswordModal({open: true, userId: u.id})}
>
<Key className="h-4 w-4 mr-2" />
Mot de passe
@@ -1042,7 +1033,7 @@ const Index = () => {
<Button
variant="destructive"
size="sm"
onClick={() => handleDeleteUser(u.id)}
onClick={() => setDeleteUserModal({open: true, userId: u.id})}
>
Supprimer
</Button>
@@ -1057,7 +1048,7 @@ const Index = () => {
<TabsContent value="rss" className="mt-4 space-y-4">
<Button
className="flex items-center gap-2"
onClick={handleAddRssFeed}
onClick={() => setAddRssFeedModal(true)}
>
<Rss className="h-4 w-4" />
Ajouter un flux RSS
@@ -1228,6 +1219,81 @@ const Index = () => {
</DialogContent>
</Dialog>
{/* Modals */}
<ConfirmModal
open={deleteNoteModal}
onOpenChange={setDeleteNoteModal}
title="Supprimer la note"
description="Êtes-vous sûr de vouloir supprimer cette note ? Cette action est irréversible."
onConfirm={confirmDeleteNote}
confirmText="Supprimer"
cancelText="Annuler"
variant="destructive"
/>
<InputModal
open={addTodoModal}
onOpenChange={setAddTodoModal}
title="Nouvelle tâche"
label="Tâche"
placeholder="Entrez la tâche..."
onConfirm={confirmAddTodo}
confirmText="Ajouter"
/>
<InputModal
open={addRssFeedModal}
onOpenChange={setAddRssFeedModal}
title="Ajouter un flux RSS"
label="URL du flux RSS"
placeholder="https://example.com/feed.xml"
onConfirm={confirmAddRssFeed}
confirmText="Ajouter"
type="url"
/>
<AddUserModal
open={addUserModal}
onOpenChange={setAddUserModal}
onConfirm={confirmAddUser}
/>
<ConfirmModal
open={deleteUserModal.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}
confirmText="Supprimer"
variant="destructive"
/>
<InputModal
open={changePasswordModal.open}
onOpenChange={(open) => setChangePasswordModal({open})}
title="Changer le mot de passe"
label="Nouveau mot de passe"
placeholder="Entrez le nouveau mot de passe..."
onConfirm={confirmChangePassword}
confirmText="Modifier"
type="password"
/>
<InputModal
open={addNoteTodoModal}
onOpenChange={setAddNoteTodoModal}
title="Nouvelle tâche"
label="Tâche"
placeholder="Entrez la tâche..."
onConfirm={async (text) => {
if (openNote) {
const updatedTodos = [...(openNote.todos || []), { text, completed: false }];
await handleUpdateNote({ todos: updatedTodos });
}
}}
confirmText="Ajouter"
/>
<div className="fixed bottom-4 right-4">
<MadeWithDyad />
</div>