[dyad] wrote 5 file(s)

This commit is contained in:
Michael committed 2025-11-06 15:55:54 +01:00
1 parent 1ce71f37c2
commit 209c48ee6b
4 files changed
+90 -53

No files matched your search

+7 -2
View File
@@ -27,8 +27,13 @@ const App = () => {
useEffect(() => { useEffect(() => {
// Vérifier le token au démarrage // Vérifier le token au démarrage
const checkAuth = async () => { const checkAuth = async () => {
const isValid = await AuthService.checkTokenValidity(); try {
setIsLoading(false); await AuthService.checkTokenValidity();
} catch (error) {
console.error("Error checking token validity:", error);
} finally {
setIsLoading(false);
}
}; };
checkAuth(); checkAuth();
+61 -45
View File
@@ -13,36 +13,44 @@ import AdminService from "@/services/AdminService";
import NotesService, { Note } from "@/services/NotesService"; import NotesService, { Note } from "@/services/NotesService";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
interface UserType {
id: number;
username: string;
is_admin: boolean;
}
const Index = () => { const Index = () => {
const [user, setUser] = useState<any>(null); const [user, setUser] = useState<UserType | null>(null);
const [showAdmin, setShowAdmin] = useState(false); const [showAdmin, setShowAdmin] = useState(false);
const [activeTab, setActiveTab] = useState("all"); const [activeTab, setActiveTab] = useState("all");
const [searchQuery, setSearchQuery] = useState(""); const [searchQuery, setSearchQuery] = useState("");
const [notes, setNotes] = useState<Note[]>([]); const [notes, setNotes] = useState<Note[]>([]);
const [currentNote, setCurrentNote] = useState<Note | null>(null); const [currentNote, setCurrentNote] = useState<Note | null>(null);
const [users, setUsers] = useState<any[]>([]); const [users, setUsers] = useState<UserType[]>([]);
const navigate = useNavigate(); const navigate = useNavigate();
useEffect(() => { useEffect(() => {
const user = AuthService.getUser(); const userFromAuth = AuthService.getUser();
if (user) { if (userFromAuth) {
setUser(user); setUser(userFromAuth);
loadNotes(); loadNotes();
} else {
navigate("/login");
} }
}, []); }, [navigate]);
const loadNotes = async () => { const loadNotes = async () => {
const notes = await NotesService.getNotes(); const fetchedNotes = await NotesService.getNotes();
setNotes(notes); setNotes(fetchedNotes || []);
if (notes.length > 0) { if (fetchedNotes && fetchedNotes.length > 0) {
setCurrentNote(notes[0]); setCurrentNote(fetchedNotes[0]);
} }
}; };
const loadUsers = async () => { const loadUsers = async () => {
if (user?.is_admin) { if (user?.is_admin) {
const users = await AdminService.getUsers(); const fetchedUsers = await AdminService.getUsers();
setUsers(users); setUsers(fetchedUsers || []);
} }
}; };
@@ -62,7 +70,13 @@ const Index = () => {
const handleUpdateNote = async (updatedFields: Partial<Note>) => { const handleUpdateNote = async (updatedFields: Partial<Note>) => {
if (!currentNote) return; if (!currentNote) return;
const updatedNote = { ...currentNote, ...updatedFields }; const updatedNote = {
...currentNote,
...updatedFields,
todos: currentNote.todos || [],
images: currentNote.images || []
};
const success = await NotesService.updateNote(updatedNote); const success = await NotesService.updateNote(updatedNote);
if (success) { if (success) {
@@ -137,7 +151,7 @@ const Index = () => {
(activeTab === "all" || (activeTab === "archived" && note.archived)) && (activeTab === "all" || (activeTab === "archived" && note.archived)) &&
(searchQuery === "" || (searchQuery === "" ||
note.title.toLowerCase().includes(searchQuery.toLowerCase()) || note.title.toLowerCase().includes(searchQuery.toLowerCase()) ||
note.content.toLowerCase().includes(searchQuery.toLowerCase())) (note.content && note.content.toLowerCase().includes(searchQuery.toLowerCase())))
); );
return ( return (
@@ -165,33 +179,35 @@ const Index = () => {
</div> </div>
<div className="flex items-center gap-4"> <div className="flex items-center gap-4">
<DropdownMenu> {user && (
<DropdownMenuTrigger asChild> <DropdownMenu>
<Button variant="ghost" className="flex items-center gap-2"> <DropdownMenuTrigger asChild>
<Avatar className="h-8 w-8"> <Button variant="ghost" className="flex items-center gap-2">
<AvatarFallback className="bg-primary/10 text-primary"> <Avatar className="h-8 w-8">
{user?.username?.charAt(0).toUpperCase() || "U"} <AvatarFallback className="bg-primary/10 text-primary">
</AvatarFallback> {user.username?.charAt(0).toUpperCase() || "U"}
</Avatar> </AvatarFallback>
<span className="font-medium">{user?.username}</span> </Avatar>
<ChevronDown className="h-4 w-4 text-muted-foreground" /> <span className="font-medium">{user.username}</span>
</Button> <ChevronDown className="h-4 w-4 text-muted-foreground" />
</DropdownMenuTrigger> </Button>
<DropdownMenuContent align="end" className="w-56"> </DropdownMenuTrigger>
<DropdownMenuLabel>Mon compte</DropdownMenuLabel> <DropdownMenuContent align="end" className="w-56">
<DropdownMenuSeparator /> <DropdownMenuLabel>Mon compte</DropdownMenuLabel>
{user?.is_admin && ( <DropdownMenuSeparator />
<DropdownMenuItem onClick={() => setShowAdmin(true)}> {user.is_admin && (
<Settings className="mr-2 h-4 w-4" /> <DropdownMenuItem onClick={() => setShowAdmin(true)}>
<span>Administration</span> <Settings className="mr-2 h-4 w-4" />
<span>Administration</span>
</DropdownMenuItem>
)}
<DropdownMenuItem onClick={handleLogout}>
<LogOut className="mr-2 h-4 w-4" />
<span>Déconnexion</span>
</DropdownMenuItem> </DropdownMenuItem>
)} </DropdownMenuContent>
<DropdownMenuItem onClick={handleLogout}> </DropdownMenu>
<LogOut className="mr-2 h-4 w-4" /> )}
<span>Déconnexion</span>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div> </div>
</div> </div>
</div> </div>
@@ -259,7 +275,7 @@ const Index = () => {
type="text" type="text"
placeholder="Titre de la note" placeholder="Titre de la note"
className="text-xl font-semibold border-none shadow-none text-2xl h-auto px-0 focus-visible:ring-0" className="text-xl font-semibold border-none shadow-none text-2xl h-auto px-0 focus-visible:ring-0"
value={currentNote.title} value={currentNote.title || ""}
onChange={(e) => handleUpdateNote({ title: e.target.value })} onChange={(e) => handleUpdateNote({ title: e.target.value })}
/> />
@@ -295,7 +311,7 @@ const Index = () => {
className="min-h-[500px] outline-none" className="min-h-[500px] outline-none"
contentEditable contentEditable
suppressContentEditableWarning suppressContentEditableWarning
dangerouslySetInnerHTML={{ __html: currentNote.content }} dangerouslySetInnerHTML={{ __html: currentNote.content || "" }}
onBlur={(e) => handleUpdateNote({ content: e.currentTarget.innerHTML })} onBlur={(e) => handleUpdateNote({ content: e.currentTarget.innerHTML })}
/> />
</CardContent> </CardContent>
@@ -331,7 +347,7 @@ const Index = () => {
type="checkbox" type="checkbox"
checked={todo.completed} checked={todo.completed}
onChange={() => { onChange={() => {
const updatedTodos = [...currentNote.todos]; const updatedTodos = [...(currentNote.todos || [])];
updatedTodos[index].completed = !updatedTodos[index].completed; updatedTodos[index].completed = !updatedTodos[index].completed;
handleUpdateNote({ todos: updatedTodos }); handleUpdateNote({ todos: updatedTodos });
}} }}
@@ -345,7 +361,7 @@ const Index = () => {
size="icon" size="icon"
className="ml-auto h-6 w-6" className="ml-auto h-6 w-6"
onClick={() => { onClick={() => {
const updatedTodos = [...currentNote.todos]; const updatedTodos = [...(currentNote.todos || [])];
updatedTodos.splice(index, 1); updatedTodos.splice(index, 1);
handleUpdateNote({ todos: updatedTodos }); handleUpdateNote({ todos: updatedTodos });
}} }}
@@ -412,7 +428,7 @@ const Index = () => {
if (image.id && currentNote.id) { if (image.id && currentNote.id) {
const success = await NotesService.deleteImage(currentNote.id, image.id); const success = await NotesService.deleteImage(currentNote.id, image.id);
if (success) { if (success) {
const updatedImages = currentNote.images.filter((_, i) => i !== index); const updatedImages = (currentNote.images || []).filter((_, i) => i !== index);
handleUpdateNote({ images: updatedImages }); handleUpdateNote({ images: updatedImages });
} }
} }
+3 -3
View File
@@ -30,7 +30,7 @@ class AuthService {
getHeaders(): Record<string, string> { getHeaders(): Record<string, string> {
return { return {
"Authorization": `Bearer ${this.token}`, "Authorization": `Bearer ${this.token || ""}`,
"Content-Type": "application/json" "Content-Type": "application/json"
}; };
} }
@@ -51,8 +51,8 @@ class AuthService {
this.token = data.token; this.token = data.token;
this.user = data.user; this.user = data.user;
localStorage.setItem("token", this.token); localStorage.setItem("token", this.token || "");
localStorage.setItem("user", JSON.stringify(this.user)); localStorage.setItem("user", JSON.stringify(this.user || {}));
showSuccess("Connexion réussie"); showSuccess("Connexion réussie");
return true; return true;
+19 -3
View File
@@ -35,7 +35,14 @@ class NotesService {
throw new Error("Erreur lors de la récupération des notes"); throw new Error("Erreur lors de la récupération des notes");
} }
return await response.json(); const notes = await response.json();
// Ensure todos and images are always arrays
return notes.map((note: Note) => ({
...note,
todos: note.todos || [],
images: note.images || []
}));
} catch (error) { } catch (error) {
showError(error instanceof Error ? error.message : "Erreur serveur"); showError(error instanceof Error ? error.message : "Erreur serveur");
return []; return [];
@@ -55,7 +62,12 @@ class NotesService {
} }
showSuccess("Note créée avec succès"); showSuccess("Note créée avec succès");
return await response.json(); const note = await response.json();
return {
...note,
todos: note.todos || [],
images: note.images || []
};
} catch (error) { } catch (error) {
showError(error instanceof Error ? error.message : "Erreur serveur"); showError(error instanceof Error ? error.message : "Erreur serveur");
return null; return null;
@@ -69,7 +81,11 @@ class NotesService {
const response = await fetch(`/api/notes/${note.id}`, { const response = await fetch(`/api/notes/${note.id}`, {
method: "PUT", method: "PUT",
headers: AuthService.getHeaders(), headers: AuthService.getHeaders(),
body: JSON.stringify(note) body: JSON.stringify({
...note,
todos: note.todos || [],
images: note.images || []
})
}); });
if (!response.ok) { if (!response.ok) {