-
-
+ {currentNote ? (
+
+
+
handleUpdateNote({ title: e.target.value })}
+ />
+
+
+
+
+
+
-
-
-
+ )}
@@ -225,28 +464,63 @@ const Index = () => {
-
+
Ajouter un utilisateur
-
-
-
-
- AD
-
-
-
admin
-
Administrateur
-
-
-
- Modifier
-
-
-
+ {users.length > 0 ? (
+ users.map(user => (
+
+
+
+
+ {user.username.charAt(0).toUpperCase()}
+
+
+
{user.username}
+
+ {user.is_admin ? "Administrateur" : "Utilisateur"}
+
+
+
+
+ {user.id !== 1 && (
+ <>
+ handleChangePassword(user.id)}
+ >
+ Mot de passe
+
+ handleToggleAdmin(user.id, user.is_admin)}
+ >
+ {user.is_admin ? "Retirer admin" : "Rendre admin"}
+
+ handleDeleteUser(user.id)}
+ >
+ Supprimer
+
+ >
+ )}
+
+
+
+ ))
+ ) : (
+
Chargement des utilisateurs...
+ )}
diff --git a/src/pages/Login.tsx b/src/pages/Login.tsx
index 362209d..0dc95cf 100644
--- a/src/pages/Login.tsx
+++ b/src/pages/Login.tsx
@@ -2,40 +2,26 @@ import { useState } from "react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Card, CardContent, CardHeader, CardTitle, CardDescription, CardFooter } from "@/components/ui/card";
-import { showError, showSuccess } from "@/utils/toast";
import { MadeWithDyad } from "@/components/made-with-dyad";
+import AuthService from "@/services/AuthService";
+import { useNavigate } from "react-router-dom";
const Login = () => {
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const [isLoading, setIsLoading] = useState(false);
+ const navigate = useNavigate();
const handleLogin = async (e: React.FormEvent) => {
e.preventDefault();
setIsLoading(true);
- try {
- const response = await fetch("/api/auth/login", {
- method: "POST",
- headers: { "Content-Type": "application/json" },
- body: JSON.stringify({ username, password }),
- });
-
- const data = await response.json();
-
- if (!response.ok) {
- throw new Error(data.message || "Erreur de connexion");
- }
-
- localStorage.setItem("token", data.token);
- localStorage.setItem("user", JSON.stringify(data.user));
- showSuccess("Connexion réussie");
- window.location.href = "/";
- } catch (error) {
- showError(error instanceof Error ? error.message : "Erreur de connexion");
- } finally {
- setIsLoading(false);
+ const success = await AuthService.login(username, password);
+ if (success) {
+ navigate("/");
}
+
+ setIsLoading(false);
};
return (
diff --git a/src/services/AdminService.ts b/src/services/AdminService.ts
new file mode 100644
index 0000000..d66cfe4
--- /dev/null
+++ b/src/services/AdminService.ts
@@ -0,0 +1,110 @@
+import AuthService from "./AuthService";
+import { showError, showSuccess } from "@/utils/toast";
+
+interface User {
+ id: number;
+ username: string;
+ is_admin: boolean;
+ created_at?: string;
+}
+
+class AdminService {
+ async getUsers(): Promise
{
+ try {
+ const response = await fetch("/api/users", {
+ headers: AuthService.getHeaders()
+ });
+
+ if (!response.ok) {
+ throw new Error("Erreur lors de la récupération des utilisateurs");
+ }
+
+ return await response.json();
+ } catch (error) {
+ showError(error instanceof Error ? error.message : "Erreur serveur");
+ return [];
+ }
+ }
+
+ async createUser(username: string, password: string, isAdmin: boolean): Promise {
+ try {
+ const response = await fetch("/api/users", {
+ method: "POST",
+ headers: AuthService.getHeaders(),
+ body: JSON.stringify({ username, password, is_admin: isAdmin })
+ });
+
+ if (!response.ok) {
+ const error = await response.json();
+ throw new Error(error.message || "Erreur lors de la création de l'utilisateur");
+ }
+
+ showSuccess("Utilisateur créé avec succès");
+ return await response.json();
+ } catch (error) {
+ showError(error instanceof Error ? error.message : "Erreur serveur");
+ return null;
+ }
+ }
+
+ async updateUserPassword(userId: number, password: string): Promise {
+ try {
+ const response = await fetch(`/api/users/${userId}`, {
+ method: "PUT",
+ headers: AuthService.getHeaders(),
+ body: JSON.stringify({ password })
+ });
+
+ if (!response.ok) {
+ throw new Error("Erreur lors de la modification du mot de passe");
+ }
+
+ showSuccess("Mot de passe modifié avec succès");
+ return true;
+ } catch (error) {
+ showError(error instanceof Error ? error.message : "Erreur serveur");
+ return false;
+ }
+ }
+
+ async toggleAdminStatus(userId: number, currentStatus: boolean): Promise {
+ try {
+ const response = await fetch(`/api/users/${userId}`, {
+ method: "PUT",
+ headers: AuthService.getHeaders(),
+ body: JSON.stringify({ is_admin: !currentStatus })
+ });
+
+ if (!response.ok) {
+ throw new Error("Erreur lors de la modification des droits");
+ }
+
+ showSuccess("Droits modifiés avec succès");
+ return true;
+ } catch (error) {
+ showError(error instanceof Error ? error.message : "Erreur serveur");
+ return false;
+ }
+ }
+
+ async deleteUser(userId: number): Promise {
+ try {
+ const response = await fetch(`/api/users/${userId}`, {
+ method: "DELETE",
+ headers: AuthService.getHeaders()
+ });
+
+ if (!response.ok) {
+ throw new Error("Erreur lors de la suppression de l'utilisateur");
+ }
+
+ showSuccess("Utilisateur supprimé avec succès");
+ return true;
+ } catch (error) {
+ showError(error instanceof Error ? error.message : "Erreur serveur");
+ return false;
+ }
+ }
+}
+
+export default new AdminService();
\ No newline at end of file
diff --git a/src/services/AuthService.ts b/src/services/AuthService.ts
new file mode 100644
index 0000000..adc52bf
--- /dev/null
+++ b/src/services/AuthService.ts
@@ -0,0 +1,94 @@
+import { showError, showSuccess } from "@/utils/toast";
+
+interface User {
+ id: number;
+ username: string;
+ is_admin: boolean;
+}
+
+class AuthService {
+ private token: string | null;
+ private user: User | null;
+
+ constructor() {
+ this.token = localStorage.getItem("token");
+ const userStr = localStorage.getItem("user");
+ this.user = userStr ? JSON.parse(userStr) : null;
+ }
+
+ isAuthenticated(): boolean {
+ return !!this.token;
+ }
+
+ getUser(): User | null {
+ return this.user;
+ }
+
+ getToken(): string | null {
+ return this.token;
+ }
+
+ getHeaders(): Record {
+ return {
+ "Authorization": `Bearer ${this.token}`,
+ "Content-Type": "application/json"
+ };
+ }
+
+ async login(username: string, password: string): Promise {
+ try {
+ const response = await fetch("/api/auth/login", {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({ username, password })
+ });
+
+ const data = await response.json();
+
+ if (!response.ok) {
+ throw new Error(data.message || "Erreur de connexion");
+ }
+
+ this.token = data.token;
+ this.user = data.user;
+ localStorage.setItem("token", this.token);
+ localStorage.setItem("user", JSON.stringify(this.user));
+
+ showSuccess("Connexion réussie");
+ return true;
+ } catch (error) {
+ showError(error instanceof Error ? error.message : "Erreur de connexion");
+ return false;
+ }
+ }
+
+ logout(): void {
+ localStorage.removeItem("token");
+ localStorage.removeItem("user");
+ this.token = null;
+ this.user = null;
+ showSuccess("Déconnexion réussie");
+ }
+
+ async checkTokenValidity(): Promise {
+ if (!this.token) return false;
+
+ try {
+ const response = await fetch("/api/auth/me", {
+ headers: this.getHeaders()
+ });
+
+ if (!response.ok) {
+ this.logout();
+ return false;
+ }
+
+ return true;
+ } catch (error) {
+ this.logout();
+ return false;
+ }
+ }
+}
+
+export default new AuthService();
\ No newline at end of file
diff --git a/src/services/NotesService.ts b/src/services/NotesService.ts
new file mode 100644
index 0000000..fbde5a6
--- /dev/null
+++ b/src/services/NotesService.ts
@@ -0,0 +1,148 @@
+import AuthService from "./AuthService";
+import { showError, showSuccess } from "@/utils/toast";
+
+interface Todo {
+ id?: number;
+ text: string;
+ completed: boolean;
+ position?: number;
+}
+
+interface Image {
+ id?: number;
+ filename: string;
+}
+
+export interface Note {
+ id?: number;
+ title: string;
+ content: string;
+ archived?: boolean;
+ created_at?: string;
+ updated_at?: string;
+ todos: Todo[];
+ images: Image[];
+}
+
+class NotesService {
+ async getNotes(): Promise {
+ try {
+ const response = await fetch("/api/notes", {
+ headers: AuthService.getHeaders()
+ });
+
+ if (!response.ok) {
+ throw new Error("Erreur lors de la récupération des notes");
+ }
+
+ return await response.json();
+ } catch (error) {
+ showError(error instanceof Error ? error.message : "Erreur serveur");
+ return [];
+ }
+ }
+
+ async createNote(title: string, content: string = ""): Promise {
+ try {
+ const response = await fetch("/api/notes", {
+ method: "POST",
+ 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");
+ return await response.json();
+ } catch (error) {
+ showError(error instanceof Error ? error.message : "Erreur serveur");
+ return null;
+ }
+ }
+
+ async updateNote(note: Note): Promise {
+ if (!note.id) return false;
+
+ try {
+ const response = await fetch(`/api/notes/${note.id}`, {
+ method: "PUT",
+ headers: AuthService.getHeaders(),
+ body: JSON.stringify(note)
+ });
+
+ if (!response.ok) {
+ throw new Error("Erreur lors de la mise à jour de la note");
+ }
+
+ return true;
+ } catch (error) {
+ showError(error instanceof Error ? error.message : "Erreur serveur");
+ return false;
+ }
+ }
+
+ 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) {
+ showError(error instanceof Error ? error.message : "Erreur serveur");
+ return false;
+ }
+ }
+
+ async uploadImage(noteId: number, file: File): Promise {
+ const formData = new FormData();
+ formData.append("image", file);
+
+ try {
+ const response = await fetch(`/api/notes/${noteId}/images`, {
+ method: "POST",
+ headers: {
+ "Authorization": AuthService.getHeaders().Authorization
+ },
+ body: formData
+ });
+
+ if (!response.ok) {
+ throw new Error("Erreur lors de l'upload de l'image");
+ }
+
+ return await response.json();
+ } catch (error) {
+ showError(error instanceof Error ? error.message : "Erreur serveur");
+ return null;
+ }
+ }
+
+ async deleteImage(noteId: number, imageId: number): Promise {
+ try {
+ const response = await fetch(`/api/notes/${noteId}/images/${imageId}`, {
+ method: "DELETE",
+ headers: AuthService.getHeaders()
+ });
+
+ if (!response.ok) {
+ throw new Error("Erreur lors de la suppression de l'image");
+ }
+
+ return true;
+ } catch (error) {
+ showError(error instanceof Error ? error.message : "Erreur serveur");
+ return false;
+ }
+ }
+}
+
+export default new NotesService();
\ No newline at end of file