millieux de tout brisrr

This commit is contained in:
Your Name 2025-12-10 17:59:44 -05:00
parent d2bde41aa4
commit 73b48dc728
3 changed files with 256 additions and 103 deletions

View File

@ -1,5 +1,55 @@
for f in $(find src -type f \( -name "*.js" -o -name "*.jsx" -o -name "*.css" \) ! -name "maplibre.css"); do #!/data/data/com.termux/files/usr/bin/bash
echo "================== FILE: $f ==================" set -euo pipefail
cat "$f"
echo "" # Nombre de parties voulues
done | termux-clipboard-set PARTS=3
# Fichier temporaire qui contient tout le code
TMPFILE=$(mktemp)
cleanup() { rm -f "$TMPFILE" "${TMPFILE}"_part_* 2>/dev/null || true; }
trap cleanup EXIT
# 1) Construire le gros fichier avec tous les .js/.jsx/.css
find src -type f \( -name "*.js" -o -name "*.jsx" -o -name "*.css" \) ! -name "maplibre.css" -print0 |
while IFS= read -r -d '' f; do
printf '================== FILE: %s ==================\n' "$f" >> "$TMPFILE"
cat "$f" >> "$TMPFILE"
printf '\n' >> "$TMPFILE"
done
# 2) Découper en exactement PARTS morceaux (ou moins si très petit)
# -n l/3 = découpe en 3 blocs de lignes à peu près égaux
split -n l/"$PARTS" -d --additional-suffix=".txt" "$TMPFILE" "${TMPFILE}_part_"
# 3) Envoyer chaque morceau dans le presse-papier, ENTER pour le suivant
parts=( "${TMPFILE}_part_"*.txt )
total=${#parts[@]}
if [ "$total" -eq 0 ]; then
echo "Aucun contenu trouvé."
exit 0
fi
echo "📦 Total: $total partie(s) à envoyer dans ChatGPT."
idx=1
for p in "${parts[@]}"; do
# On ajoute une bannière claire DEBUT/FIN pour chaque partie
{
echo "===== SOCIOWIRE FRONTEND PART $idx / $total (BEGIN) ====="
cat "$p"
echo
echo "===== END OF SOCIOWIRE FRONTEND PART $idx / $total ====="
} | termux-clipboard-set
echo "➡️ Partie $idx / $total copiée dans le presse-papier."
if [ "$idx" -lt "$total" ]; then
echo "Colle cette partie dans ChatGPT, envoie le message,"
echo "puis reviens ici et appuie sur ENTER pour la suivante (ou 'q' + ENTER pour quitter)…"
read -r ans || true
[ "$ans" = "q" ] || [ "$ans" = "Q" ] && break
fi
idx=$((idx+1))
done
echo "✅ Terminé."

View File

@ -1,105 +1,153 @@
import React, { createContext, useContext, useEffect, useState } from "react"; import React, { createContext, useContext, useEffect, useState } from "react";
import keycloak from "./keycloak";
// CONFIG KEYCLOAK
const KC_BASE = "https://web.sociowire.com:8443";
const KC_REALM = "sociowire";
const KC_CLIENT_ID = "sociowire-frontend";
const TOKEN_KEY = "sociowire:token";
const AuthContext = createContext(null); const AuthContext = createContext(null);
function parseJwt(token) {
try {
const [, payload] = token.split(".");
const json = atob(payload.replace(/-/g, "+").replace(/_/g, "/"));
return JSON.parse(json);
} catch {
return {};
}
}
export function AuthProvider({ children }) { export function AuthProvider({ children }) {
const [kc, setKc] = useState(null); const [token, setToken] = useState(null);
const [loading, setLoading] = useState(true);
const [authenticated, setAuthenticated] = useState(false); const [authenticated, setAuthenticated] = useState(false);
const [username, setUsername] = useState(""); const [username, setUsername] = useState("");
const [loading, setLoading] = useState(true);
const [lastError, setLastError] = useState(""); const [lastError, setLastError] = useState("");
// charger token depuis localStorage au démarrage
useEffect(() => { useEffect(() => {
let isMounted = true;
async function init() {
try { try {
console.log("[AUTH] init keycloak…"); const saved = localStorage.getItem(TOKEN_KEY);
const ok = await keycloak.init({ if (saved) {
onLoad: "check-sso", // ne force pas le login au chargement const data = JSON.parse(saved);
checkLoginIframe: false, if (data && data.access_token) {
}); const now = Date.now() / 1000;
const payload = parseJwt(data.access_token);
if (!isMounted) return; if (!payload.exp || payload.exp > now) {
setToken(data.access_token);
console.log("[AUTH] init result:", ok); setAuthenticated(true);
window.kc = keycloak; // debug global
setKc(keycloak);
setAuthenticated(ok);
if (ok && keycloak.tokenParsed) {
const p = keycloak.tokenParsed;
const name = const name =
p.preferred_username || payload.preferred_username ||
p.email || payload.email ||
p.name || payload.name ||
(p.sub ? String(p.sub) : ""); (payload.sub ? String(payload.sub) : "");
setUsername(name || ""); setUsername(name || "");
} else {
setUsername("");
} }
} catch (err) {
console.error("[AUTH] keycloak init error", err);
if (isMounted) {
setLastError(String(err));
} }
}
} catch (e) {
console.warn("[AUTH] load token error", e);
} finally { } finally {
if (isMounted) setLoading(false); setLoading(false);
} }
}
init();
// refresh token simplifié
const interval = setInterval(() => {
if (!keycloak || !keycloak.authenticated) return;
keycloak
.updateToken(60)
.then((refreshed) => {
if (refreshed) {
console.log("[AUTH] token refreshed");
}
})
.catch((err) => {
console.warn("[AUTH] token refresh error", err);
});
}, 30000);
return () => {
isMounted = false;
clearInterval(interval);
};
}, []); }, []);
const login = () => { async function login(user, pass) {
keycloak.login({ setLastError("");
redirectUri: window.location.href, if (!user || !pass) {
}); setLastError("Username & password required.");
}; return;
}
setLoading(true);
try {
const params = new URLSearchParams();
params.set("grant_type", "password");
params.set("client_id", KC_CLIENT_ID);
params.set("username", user);
params.set("password", pass);
const logout = () => { const res = await fetch(
keycloak.logout({ `${KC_BASE}/realms/${KC_REALM}/protocol/openid-connect/token`,
redirectUri: window.location.origin + "/", {
}); method: "POST",
}; headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
body: params.toString(),
}
);
return ( if (!res.ok) {
<AuthContext.Provider const text = await res.text();
value={{ console.error("[AUTH] login failed", res.status, text);
keycloak: kc, setLastError("Invalid credentials or auth error.");
setAuthenticated(false);
setToken(null);
setUsername("");
try {
localStorage.removeItem(TOKEN_KEY);
} catch {}
return;
}
const data = await res.json();
const accessToken = data.access_token;
if (!accessToken) {
setLastError("No access token returned.");
return;
}
const payload = parseJwt(accessToken);
const name =
payload.preferred_username ||
payload.email ||
payload.name ||
(payload.sub ? String(payload.sub) : "");
setToken(accessToken);
setAuthenticated(true);
setUsername(name || "");
setLastError("");
try {
localStorage.setItem(TOKEN_KEY, JSON.stringify(data));
} catch (e) {
console.warn("[AUTH] save token error", e);
}
} catch (err) {
console.error("[AUTH] login error", err);
setLastError("Network/auth error.");
setAuthenticated(false);
setToken(null);
setUsername("");
} finally {
setLoading(false);
}
}
function logout() {
setToken(null);
setAuthenticated(false);
setUsername("");
setLastError("");
try {
localStorage.removeItem(TOKEN_KEY);
} catch {}
}
const value = {
loading, loading,
authenticated, authenticated,
username, username,
token,
lastError,
login, login,
logout, logout,
lastError, };
}}
> return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
{children}
</AuthContext.Provider>
);
} }
export function useAuth() { export function useAuth() {
@ -109,3 +157,4 @@ export function useAuth() {
} }
return ctx; return ctx;
} }
// modif 1

View File

@ -1,4 +1,4 @@
import React from "react"; import React, { useState } from "react";
import { useAuth } from "../../auth/AuthContext"; import { useAuth } from "../../auth/AuthContext";
const THEME_LABELS = { const THEME_LABELS = {
@ -8,12 +8,27 @@ const THEME_LABELS = {
}; };
export default function TopBar({ theme = "dark", onChangeTheme }) { export default function TopBar({ theme = "dark", onChangeTheme }) {
const { loading, authenticated, username, login, logout, lastError } = useAuth(); const {
loading,
authenticated,
username,
login,
logout,
lastError,
} = useAuth();
const [user, setUser] = useState("");
const [pass, setPass] = useState("");
let authLabel = "AUTH: anon"; let authLabel = "AUTH: anon";
if (loading) authLabel = "AUTH: loading…"; if (loading) authLabel = "AUTH: loading…";
else if (authenticated) authLabel = `AUTH: user=${username || "?"}`; else if (authenticated) authLabel = `AUTH: user=${username || "?"}`;
const handleSubmit = (e) => {
e.preventDefault();
login(user.trim(), pass);
};
return ( return (
<header className="topbar"> <header className="topbar">
<div className="logo-circle">SW</div> <div className="logo-circle">SW</div>
@ -25,7 +40,7 @@ export default function TopBar({ theme = "dark", onChangeTheme }) {
{authLabel} {authLabel}
{lastError && ( {lastError && (
<span style={{ color: "#f97373", marginLeft: "0.3rem" }}> <span style={{ color: "#f97373", marginLeft: "0.3rem" }}>
(auth err) ({lastError})
</span> </span>
)} )}
</div> </div>
@ -48,11 +63,7 @@ export default function TopBar({ theme = "dark", onChangeTheme }) {
))} ))}
</div> </div>
{loading ? ( {authenticated ? (
<button className="btn-primary" disabled>
</button>
) : authenticated ? (
<div style={{ display: "flex", alignItems: "center", gap: "0.4rem" }}> <div style={{ display: "flex", alignItems: "center", gap: "0.4rem" }}>
<span style={{ fontSize: "0.8rem" }}>{username}</span> <span style={{ fontSize: "0.8rem" }}>{username}</span>
<button className="btn-primary" onClick={logout}> <button className="btn-primary" onClick={logout}>
@ -60,11 +71,54 @@ export default function TopBar({ theme = "dark", onChangeTheme }) {
</button> </button>
</div> </div>
) : ( ) : (
<button className="btn-primary" onClick={login}> <form
Login onSubmit={handleSubmit}
style={{
display: "flex",
alignItems: "center",
gap: "0.25rem",
}}
>
<input
type="text"
placeholder="user"
value={user}
onChange={(e) => setUser(e.target.value)}
style={{
fontSize: "0.7rem",
padding: "0.15rem 0.4rem",
borderRadius: 999,
border: "1px solid #4b5563",
background: "#020617",
color: "#e5e7eb",
}}
/>
<input
type="password"
placeholder="pass"
value={pass}
onChange={(e) => setPass(e.target.value)}
style={{
fontSize: "0.7rem",
padding: "0.15rem 0.4rem",
borderRadius: 999,
border: "1px solid #4b5563",
background: "#020617",
color: "#e5e7eb",
}}
/>
<button
className="btn-primary"
type="submit"
disabled={loading}
style={{ fontSize: "0.7rem" }}
>
{loading ? "..." : "Login"}
</button> </button>
</form>
)} )}
</div> </div>
</header> </header>
); );
} }
// modif 2