diff --git a/.env b/.env index 88ae8ef..5fc3483 100644 --- a/.env +++ b/.env @@ -1,2 +1,2 @@ -VITE_API_ENDPOINT=http://localhost:5254 -VITE_BASE= +VITE_API_ENDPOINT=https://iqball.maxou.dev/api/dotnet-master +#VITE_API_ENDPOINT=http://localhost:5254 diff --git a/.eslintrc.cjs b/.eslintrc.cjs index e2c5b87..da35694 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -5,6 +5,8 @@ module.exports = { "eslint:recommended", "plugin:@typescript-eslint/recommended", "plugin:react-hooks/recommended", + "plugin:react/recommended", + "plugin:react/jsx-runtime", ], ignorePatterns: ["dist", ".eslintrc.cjs"], parser: "@typescript-eslint/parser", diff --git a/.gitignore b/.gitignore index 265f50c..780376a 100644 --- a/.gitignore +++ b/.gitignore @@ -14,7 +14,6 @@ dist-ssr # Editor directories and files .vscode/* -!.vscode/extensions.json .idea .DS_Store *.suo diff --git a/ci/.drone.yml b/ci/.drone.yml index 0652133..c6f2421 100644 --- a/ci/.drone.yml +++ b/ci/.drone.yml @@ -25,8 +25,8 @@ steps: depends_on: - "front CI" commands: - - sed -Ei "s/VITE_BASE=/VITE_BASE=\\/${DRONE_BRANCH}/g" .env - - npm run build -- --base=/$DRONE_BRANCH + - echo "VITE_API_ENDPOINT=https://iqball.maxou.dev/api/dotnet-$(echo $DRONE_BRANCH | tr / _)" > .env.STAGE + - npm run build -- --base=/$DRONE_BRANCH/ --mode STAGE - mv dist/* /outputs diff --git a/src/Constants.ts b/src/Constants.ts index 013db50..4f77f36 100644 --- a/src/Constants.ts +++ b/src/Constants.ts @@ -6,4 +6,4 @@ export const API = import.meta.env.VITE_API_ENDPOINT /** * This constant defines the base app's endpoint. */ -export const BASE = import.meta.env.VITE_BASE +export const BASE = import.meta.env.BASE_URL.slice(0, import.meta.env.BASE_URL.length - 1) diff --git a/src/Fetcher.ts b/src/Fetcher.ts index f4af2ce..ab4c7ad 100644 --- a/src/Fetcher.ts +++ b/src/Fetcher.ts @@ -1,9 +1,6 @@ -import { API, BASE } from "./Constants" +import { API } from "./Constants" import { getSession, saveSession, Session } from "./api/session.ts" - -export function redirect(url: string) { - location.pathname = BASE + url -} +import { redirect } from "react-router-dom" export async function fetchAPI( url: string, @@ -62,11 +59,11 @@ async function handleResponse( redirectIfNotAuth: boolean, ): Promise { // if we provided a token but still unauthorized, the token has expired + console.log(response.status) if (response.status == 401) { if (!redirectIfNotAuth) return response - redirect("/login") saveSession({ ...session, urlTarget: location.pathname }) - return response + return redirect("/login") } const nextToken = response.headers.get("Next-Authorization")! diff --git a/src/pages/Editor.tsx b/src/pages/Editor.tsx index 45eb129..6277ad2 100644 --- a/src/pages/Editor.tsx +++ b/src/pages/Editor.tsx @@ -99,20 +99,23 @@ interface EditorPageProps { } export default function EditorPage({ guestMode }: EditorPageProps) { - const [tactic, setTactic] = useState() - const { tacticId: idStr } = useParams() - const id = guestMode ? -1 : parseInt(idStr!) - - useEffect(() => { + const [tactic, setTactic] = useState(() => { if (guestMode) { - setTactic({ + return { id: -1, courtType: "PLAIN", content: '{"components": []}', name: DEFAULT_TACTIC_NAME, - }) - return + } } + return null; + }) + const { tacticId: idStr } = useParams() + const id = guestMode ? -1 : parseInt(idStr!) + + useEffect(() => { + if (guestMode) + return async function initialize() { const infoResponse = fetchAPIGet(`tactics/${id}`) diff --git a/src/pages/HomePage.tsx b/src/pages/HomePage.tsx index 8b94656..6d480d3 100644 --- a/src/pages/HomePage.tsx +++ b/src/pages/HomePage.tsx @@ -1,11 +1,11 @@ import "../style/home/home.css" -// import AccountSvg from "../assets/account.svg?react" import { BASE } from "../Constants" import { getSession } from "../api/session.ts" -import { fetchAPIGet, redirect } from "../Fetcher.ts" -import { useLayoutEffect, useState } from "react" +import { useNavigate } from "react-router-dom" +import { startTransition, useLayoutEffect, useState } from "react" import { User } from "../model/User.ts" +import { fetchAPIGet } from "../Fetcher.ts" interface Tactic { id: number @@ -28,11 +28,15 @@ export default function HomePage() { teams: [], }) + const navigate = useNavigate() + useLayoutEffect(() => { const session = getSession() if (!session.auth) { - redirect("/login") + startTransition(() => { + navigate("/login") + }) return } @@ -42,7 +46,7 @@ export default function HomePage() { } getUser() - }, []) + }, [navigate]) tactics!.sort((a, b) => b.creationDate - a.creationDate) diff --git a/src/pages/LoginPage.tsx b/src/pages/LoginPage.tsx index 9b12ea3..6d65091 100644 --- a/src/pages/LoginPage.tsx +++ b/src/pages/LoginPage.tsx @@ -1,21 +1,22 @@ -import { FormEvent, useRef, useState } from "react" +import { FormEvent, startTransition, useState } from "react" import { BASE } from "../Constants.ts" -import { fetchAPI, redirect } from "../Fetcher.ts" +import { fetchAPI } from "../Fetcher.ts" import { Failure } from "../api/failure.ts" import { getSession, saveSession } from "../api/session.ts" import "../style/form.css" +import { useNavigate } from "react-router-dom" export default function LoginApp() { const [errors, setErrors] = useState([]) - const emailRef = useRef(null) - const passwordRef = useRef(null) + const navigate = useNavigate() async function handleSubmit(e: FormEvent) { e.preventDefault() - const email = emailRef.current!.value - const password = passwordRef.current!.value + const { email, password } = Object.fromEntries( + new FormData(e.target as HTMLFormElement), + ) const response = await fetchAPI( "auth/token", @@ -28,7 +29,9 @@ export default function LoginApp() { const session = getSession() const { token, expirationDate } = await response.json() saveSession({ ...session, auth: { token, expirationDate }, urlTarget: undefined }) - redirect(session.urlTarget ?? "/") + startTransition(() => { + navigate(session.urlTarget ?? "/") + }) return } @@ -38,7 +41,7 @@ export default function LoginApp() { { type: "Non autorisé", messages: [ - "L'adresse email ou le mot de passe sont invalide.", + "L'adresse email ou le mot de passe sont invalides.", ], }, ]) @@ -81,7 +84,6 @@ export default function LoginApp() {
Mot de passe : { // if user is not authenticated if (!getSession().auth) { - redirect(`/tactic/edit-guest`) + startTransition(() => { + navigate(`/tactic/edit-guest`) + }) } const response = await fetchAPI( @@ -62,8 +68,10 @@ function CourtKindButton({ ) const { id } = await response.json() - redirect(`/tactic/${id}/edit`) - }, [courtType])}> + startTransition(() => { + navigate(`/tactic/${id}/edit`) + }) + }, [courtType, navigate])}>
(null) @@ -14,6 +15,8 @@ export default function RegisterPage() { const [errors, setErrors] = useState([]) + const navigate = useNavigate() + async function handleSubmit(e: FormEvent) { e.preventDefault() @@ -44,7 +47,9 @@ export default function RegisterPage() { const { token, expirationDate } = await response.json() const session = getSession() saveSession({ ...session, auth: { token, expirationDate }, urlTarget: undefined }) - redirect(session.urlTarget ?? "/") + startTransition(() => { + navigate(session.urlTarget ?? "/") + }) return } diff --git a/src/pages/Visualizer.tsx b/src/pages/Visualizer.tsx index 6103358..6f3b67a 100644 --- a/src/pages/Visualizer.tsx +++ b/src/pages/Visualizer.tsx @@ -2,22 +2,22 @@ // import "../style/visualizer.css" // import Court from "../assets/court/full_court.svg" // -// // export default function Visualizer({ id, name }: { id: number; name: string }) { -// // const [style, setStyle] = useState({}) -// // -// // return ( -// //
-// //
-// //

{name}

-// //
-// //
-// // Basketball Court -// //
-// //
-// // ) -// // } +// export default function Visualizer({ id, name }: { id: number; name: string }) { +// const [style, setStyle] = useState({}) +// +// return ( +//
+//
+//

{name}

+//
+//
+// Basketball Court +//
+//
+// ) +// } diff --git a/src/pages/template/Header.tsx b/src/pages/template/Header.tsx index 5f93ae7..4316098 100644 --- a/src/pages/template/Header.tsx +++ b/src/pages/template/Header.tsx @@ -1,8 +1,9 @@ import AccountSvg from "../../assets/account.svg?react" import "../../style/template/header.css" -import { useEffect, useState } from "react" -import { fetchAPIGet, redirect } from "../../Fetcher.ts" +import { startTransition, useEffect, useState } from "react" +import { fetchAPIGet } from "../../Fetcher.ts" import { getSession, saveSession } from "../../api/session.ts" +import { useNavigate } from "react-router-dom" export function Header() { const session = getSession() @@ -10,6 +11,8 @@ export function Header() { session.username ?? null, ) + const navigate = useNavigate() + useEffect(() => { async function loadUsername() { const response = await fetchAPIGet("user", false) @@ -36,7 +39,7 @@ export function Header() {

redirect("/")}> + onClick={() => startTransition(() => navigate("/"))}> IQBall

@@ -46,14 +49,19 @@ export function Header() { id="clickable-header-right" onClick={() => { if (username) { - redirect("/settings") + startTransition(() => { + navigate("/settings") + }) return } saveSession({ ...session, urlTarget: location.pathname, }) - redirect("/login") + + startTransition(() => { + navigate("/login") + }) }}> {/* */} diff --git a/vite.config.ts b/vite.config.ts index 8e2f5c5..214327e 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -13,8 +13,6 @@ export default defineConfig({ cssInjectedByJsPlugin({ relativeCSSInjection: true, }), - svgr({ - include: "**/*.svg?react", - }), + svgr(), ], })