update frontend
This commit is contained in:
parent
73b48dc728
commit
c130ad9fe6
|
|
@ -106,3 +106,26 @@ export async function createPost(payload, token) {
|
|||
|
||||
return res.json().catch(() => ({}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Crée un nouvel utilisateur (backend doit exposer POST /api/register)
|
||||
* payload: { username, email, password }
|
||||
*/
|
||||
export async function registerUser(payload) {
|
||||
const res = await fetch(`${API_BASE}/register`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
credentials: "include",
|
||||
body: JSON.stringify(payload),
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const text = await res.text().catch(() => "");
|
||||
console.error("registerUser failed", res.status, text);
|
||||
throw new Error(text || "Failed to register user");
|
||||
}
|
||||
|
||||
return res.json().catch(() => ({}));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -157,4 +157,3 @@ export function useAuth() {
|
|||
}
|
||||
return ctx;
|
||||
}
|
||||
// modif 1
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
import React, { useState } from "react";
|
||||
import { useAuth } from "../../auth/AuthContext";
|
||||
import { registerUser } from "../../api/client";
|
||||
|
||||
const THEME_LABELS = {
|
||||
dark: "Dark",
|
||||
|
|
@ -17,18 +18,52 @@ export default function TopBar({ theme = "dark", onChangeTheme }) {
|
|||
lastError,
|
||||
} = useAuth();
|
||||
|
||||
const [mode, setMode] = useState("login"); // "login" | "register"
|
||||
|
||||
// login form
|
||||
const [user, setUser] = useState("");
|
||||
const [pass, setPass] = useState("");
|
||||
|
||||
// signup form
|
||||
const [regUser, setRegUser] = useState("");
|
||||
const [regEmail, setRegEmail] = useState("");
|
||||
const [regPass, setRegPass] = useState("");
|
||||
const [signupError, setSignupError] = useState("");
|
||||
const [signupLoading, setSignupLoading] = useState(false);
|
||||
|
||||
let authLabel = "AUTH: anon";
|
||||
if (loading) authLabel = "AUTH: loading…";
|
||||
else if (authenticated) authLabel = `AUTH: user=${username || "?"}`;
|
||||
|
||||
const handleSubmit = (e) => {
|
||||
const handleLoginSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
login(user.trim(), pass);
|
||||
};
|
||||
|
||||
const handleRegisterSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
setSignupError("");
|
||||
if (!regUser.trim() || !regEmail.trim() || !regPass) {
|
||||
setSignupError("All fields required.");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
setSignupLoading(true);
|
||||
await registerUser({
|
||||
username: regUser.trim(),
|
||||
email: regEmail.trim(),
|
||||
password: regPass,
|
||||
});
|
||||
// auto login après création
|
||||
login(regUser.trim(), regPass);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
setSignupError(err.message || "Register error.");
|
||||
} finally {
|
||||
setSignupLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<header className="topbar">
|
||||
<div className="logo-circle">SW</div>
|
||||
|
|
@ -43,6 +78,11 @@ export default function TopBar({ theme = "dark", onChangeTheme }) {
|
|||
({lastError})
|
||||
</span>
|
||||
)}
|
||||
{signupError && (
|
||||
<span style={{ color: "#f97373", marginLeft: "0.3rem" }}>
|
||||
({signupError})
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -71,8 +111,59 @@ export default function TopBar({ theme = "dark", onChangeTheme }) {
|
|||
</button>
|
||||
</div>
|
||||
) : (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "flex-end",
|
||||
gap: "0.25rem",
|
||||
minWidth: "230px",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
gap: "0.25rem",
|
||||
fontSize: "0.65rem",
|
||||
}}
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setMode("login")}
|
||||
style={{
|
||||
padding: "0.15rem 0.45rem",
|
||||
borderRadius: 999,
|
||||
border: "1px solid #4b5563",
|
||||
background:
|
||||
mode === "login" ? "#1e293b" : "transparent",
|
||||
color: "#e5e7eb",
|
||||
cursor: "pointer",
|
||||
fontSize: "0.65rem",
|
||||
}}
|
||||
>
|
||||
Login
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setMode("register")}
|
||||
style={{
|
||||
padding: "0.15rem 0.45rem",
|
||||
borderRadius: 999,
|
||||
border: "1px solid #4b5563",
|
||||
background:
|
||||
mode === "register" ? "#1e293b" : "transparent",
|
||||
color: "#e5e7eb",
|
||||
cursor: "pointer",
|
||||
fontSize: "0.65rem",
|
||||
}}
|
||||
>
|
||||
Sign up
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{mode === "login" ? (
|
||||
<form
|
||||
onSubmit={handleSubmit}
|
||||
onSubmit={handleLoginSubmit}
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
|
|
@ -116,9 +207,73 @@ export default function TopBar({ theme = "dark", onChangeTheme }) {
|
|||
{loading ? "..." : "Login"}
|
||||
</button>
|
||||
</form>
|
||||
) : (
|
||||
<form
|
||||
onSubmit={handleRegisterSubmit}
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: "0.25rem",
|
||||
}}
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="user"
|
||||
value={regUser}
|
||||
onChange={(e) => setRegUser(e.target.value)}
|
||||
style={{
|
||||
fontSize: "0.7rem",
|
||||
padding: "0.15rem 0.4rem",
|
||||
borderRadius: 999,
|
||||
border: "1px solid #4b5563",
|
||||
background: "#020617",
|
||||
color: "#e5e7eb",
|
||||
minWidth: "70px",
|
||||
}}
|
||||
/>
|
||||
<input
|
||||
type="email"
|
||||
placeholder="email"
|
||||
value={regEmail}
|
||||
onChange={(e) => setRegEmail(e.target.value)}
|
||||
style={{
|
||||
fontSize: "0.7rem",
|
||||
padding: "0.15rem 0.4rem",
|
||||
borderRadius: 999,
|
||||
border: "1px solid #4b5563",
|
||||
background: "#020617",
|
||||
color: "#e5e7eb",
|
||||
minWidth: "90px",
|
||||
}}
|
||||
/>
|
||||
<input
|
||||
type="password"
|
||||
placeholder="pass"
|
||||
value={regPass}
|
||||
onChange={(e) => setRegPass(e.target.value)}
|
||||
style={{
|
||||
fontSize: "0.7rem",
|
||||
padding: "0.15rem 0.4rem",
|
||||
borderRadius: 999,
|
||||
border: "1px solid #4b5563",
|
||||
background: "#020617",
|
||||
color: "#e5e7eb",
|
||||
minWidth: "70px",
|
||||
}}
|
||||
/>
|
||||
<button
|
||||
className="btn-primary"
|
||||
type="submit"
|
||||
disabled={signupLoading}
|
||||
style={{ fontSize: "0.7rem" }}
|
||||
>
|
||||
{signupLoading ? "..." : "Create"}
|
||||
</button>
|
||||
</form>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
// modif 2
|
||||
|
|
@ -17,7 +17,7 @@ import PostList from "../Posts/PostList";
|
|||
import { useAuth } from "../../auth/AuthContext";
|
||||
|
||||
export default function MapView({ theme = "dark" }) {
|
||||
const { authenticated, username } = useAuth();
|
||||
const { authenticated, username, token } = useAuth();
|
||||
|
||||
const {
|
||||
containerRef,
|
||||
|
|
@ -179,7 +179,8 @@ export default function MapView({ theme = "dark" }) {
|
|||
setIsSaving(true);
|
||||
|
||||
// On récupère le post retourné par le backend
|
||||
const newPost = await createPost({
|
||||
const newPost = await createPost(
|
||||
{
|
||||
title: draftTitle.trim(),
|
||||
snippet: draftBody.trim() || draftTitle.trim(),
|
||||
category: draftCategory.toUpperCase(),
|
||||
|
|
@ -187,9 +188,10 @@ export default function MapView({ theme = "dark" }) {
|
|||
lat,
|
||||
lon: lng,
|
||||
author: authorName,
|
||||
});
|
||||
|
||||
// On l’injecte direct dans le moteur de posts,
|
||||
},
|
||||
token
|
||||
);
|
||||
// On l’injecte direct dans le moteur de posts,
|
||||
// en plus du broadcast WebSocket
|
||||
if (newPost && newPost.id) {
|
||||
handleIncomingPost(newPost);
|
||||
|
|
|
|||
Loading…
Reference in New Issue