From bf5de96871b3bbb9d75be091e91bee812c1cf390 Mon Sep 17 00:00:00 2001 From: maxime Date: Wed, 21 Feb 2024 17:14:44 +0100 Subject: [PATCH] apply suggestions --- .env | 4 +-- .eslintrc.cjs | 5 +++ ci/.drone.yml | 65 +++++++++++++++++------------------ src/Constants.ts | 5 ++- src/Fetcher.ts | 19 +++------- src/pages/Editor.tsx | 50 ++++++++++++++++++++------- src/pages/HomePage.tsx | 33 ++++++++++++------ src/pages/LoginPage.tsx | 21 +++++------ src/pages/NewTacticPage.tsx | 7 +++- src/pages/RegisterPage.tsx | 13 ++++--- src/pages/template/Header.tsx | 4 +-- 11 files changed, 132 insertions(+), 94 deletions(-) diff --git a/.env b/.env index 5fc3483..f9d70fc 100644 --- a/.env +++ b/.env @@ -1,2 +1,2 @@ -VITE_API_ENDPOINT=https://iqball.maxou.dev/api/dotnet-master -#VITE_API_ENDPOINT=http://localhost:5254 +#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 da35694..6607275 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -17,4 +17,9 @@ module.exports = { { allowConstantExport: true }, ], }, + settings: { + react: { + version: "detect", + }, + }, } diff --git a/ci/.drone.yml b/ci/.drone.yml index 2575ff6..42ae5f4 100644 --- a/ci/.drone.yml +++ b/ci/.drone.yml @@ -3,42 +3,41 @@ type: docker name: "CI and Deploy on maxou.dev" volumes: - - name: server - temp: { } + - name: server + temp: {} trigger: - event: - - push + event: + - push steps: - - image: node:latest - name: "front CI" - commands: - - npm install - - npm run tsc + - image: node:latest + name: "front CI" + commands: + - npm install + - npm run tsc - - image: node:latest - name: "build react" - volumes: &outputs - - name: server - path: /outputs - depends_on: - - "front CI" - commands: - - # force to use the backend master branch if pushing on master - - echo "VITE_API_ENDPOINT=https://iqball.maxou.dev/api/dotnet-$([ "$DRONE_BRANCH" = master ] && echo master || cat .stage-backend-branch | tr / _)" > .env.STAGE - - npm run build -- --base=/$DRONE_BRANCH/ --mode STAGE - - mv dist/* /outputs + - image: node:latest + name: "build react" + volumes: &outputs + - name: server + path: /outputs + depends_on: + - "front CI" + commands: + - # force to use the backend master branch if pushing on masterŒ + - echo "VITE_API_ENDPOINT=https://iqball.maxou.dev/api/dotnet-$([ "$DRONE_BRANCH" = master ] && echo master || cat .stage-backend-branch | tr / _)" > .env.STAGE + - npm run build -- --base=/$DRONE_BRANCH/ --mode STAGE + - mv dist/* /outputs - - - image: eeacms/rsync:latest - name: Deliver on staging server branch - depends_on: - - "build react" - volumes: *outputs - environment: - SERVER_PRIVATE_KEY: - from_secret: SERVER_PRIVATE_KEY - commands: - - chmod +x ci/deploy.sh - - ci/deploy.sh \ No newline at end of file + - image: eeacms/rsync:latest + name: Deliver on staging server branch + depends_on: + - "build react" + volumes: *outputs + environment: + SERVER_PRIVATE_KEY: + from_secret: SERVER_PRIVATE_KEY + commands: + - chmod +x ci/deploy.sh + - ci/deploy.sh diff --git a/src/Constants.ts b/src/Constants.ts index 4f77f36..cc414d1 100644 --- a/src/Constants.ts +++ b/src/Constants.ts @@ -6,4 +6,7 @@ export const API = import.meta.env.VITE_API_ENDPOINT /** * This constant defines the base app's endpoint. */ -export const BASE = import.meta.env.BASE_URL.slice(0, import.meta.env.BASE_URL.length - 1) +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 ab4c7ad..08872c2 100644 --- a/src/Fetcher.ts +++ b/src/Fetcher.ts @@ -1,12 +1,10 @@ import { API } from "./Constants" import { getSession, saveSession, Session } from "./api/session.ts" -import { redirect } from "react-router-dom" export async function fetchAPI( url: string, payload: unknown, method = "POST", - redirectIfUnauthorized: boolean = true, ): Promise { const session = getSession() const token = session?.auth?.token @@ -26,13 +24,10 @@ export async function fetchAPI( body: JSON.stringify(payload), }) - return await handleResponse(session, response, redirectIfUnauthorized) + return await handleResponse(session, response) } -export async function fetchAPIGet( - url: string, - redirectIfNotAuth: boolean = true, -): Promise { +export async function fetchAPIGet(url: string): Promise { const session = getSession() const token = session?.auth?.token @@ -50,20 +45,16 @@ export async function fetchAPIGet( headers, }) - return await handleResponse(session, response, redirectIfNotAuth) + return await handleResponse(session, response) } async function handleResponse( session: Session, response: Response, - 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 - saveSession({ ...session, urlTarget: location.pathname }) - return redirect("/login") + if (!response.ok) { + return response } const nextToken = response.headers.get("Next-Authorization")! diff --git a/src/pages/Editor.tsx b/src/pages/Editor.tsx index 6277ad2..11df02c 100644 --- a/src/pages/Editor.tsx +++ b/src/pages/Editor.tsx @@ -72,7 +72,7 @@ import { removePlayer, } from "../editor/PlayerDomains" import { CourtBall } from "../components/editor/CourtBall" -import { useParams } from "react-router-dom" +import { useNavigate, useParams } from "react-router-dom" import { DEFAULT_TACTIC_NAME } from "./NewTacticPage.tsx" const ERROR_STYLE: CSSProperties = { @@ -108,26 +108,35 @@ export default function EditorPage({ guestMode }: EditorPageProps) { name: DEFAULT_TACTIC_NAME, } } - return null; + return null }) const { tacticId: idStr } = useParams() const id = guestMode ? -1 : parseInt(idStr!) + const navigation = useNavigate() useEffect(() => { - if (guestMode) - return + if (guestMode) return async function initialize() { - const infoResponse = fetchAPIGet(`tactics/${id}`) - const contentResponse = fetchAPIGet(`tactics/${id}/1`) - const { name, courtType } = await (await infoResponse).json() - const { content } = await (await contentResponse).json() + const infoResponsePromise = fetchAPIGet(`tactics/${id}`) + const contentResponsePromise = fetchAPIGet(`tactics/${id}/1`) + + const infoResponse = await infoResponsePromise + const contentResponse = await contentResponsePromise + + if (infoResponse.status == 401 || contentResponse.status == 401) { + navigation("/login") + return + } + + const { name, courtType } = await infoResponse.json() + const { content } = await contentResponse.json() setTactic({ id, name, courtType, content }) } initialize() - }, [guestMode, id, idStr]) + }, [guestMode, id, idStr, navigation]) if (tactic) { return ( @@ -164,6 +173,8 @@ function Editor({ id, name, courtType, content }: EditorProps) { const storageName = localStorage.getItem(GUEST_MODE_TITLE_STORAGE_KEY) const editorName = isInGuestMode && storageName != null ? storageName : name + const navigate = useNavigate() + return ( (r.ok ? SaveStates.Ok : SaveStates.Err), + const response = await fetchAPI( + `tactics/${id}/1`, + { content }, + "PUT", ) + if (response.status == 401) { + navigate("/login") + } + return response.ok ? SaveStates.Ok : SaveStates.Err }} onNameChange={async (name: string) => { if (isInGuestMode) { localStorage.setItem(GUEST_MODE_TITLE_STORAGE_KEY, name) return true //simulate that the name has been changed } - return fetchAPI(`tactics/${id}/name`, { name }, "PUT").then( - (r) => r.ok, + + const response = await fetchAPI( + `tactics/${id}/name`, + { name }, + "PUT", ) + if (response.status == 401) { + navigate("/login") + } + return response.ok }} /> ) diff --git a/src/pages/HomePage.tsx b/src/pages/HomePage.tsx index 6d480d3..1ec9647 100644 --- a/src/pages/HomePage.tsx +++ b/src/pages/HomePage.tsx @@ -1,6 +1,4 @@ import "../style/home/home.css" - -import { BASE } from "../Constants" import { getSession } from "../api/session.ts" import { useNavigate } from "react-router-dom" import { startTransition, useLayoutEffect, useState } from "react" @@ -42,6 +40,10 @@ export default function HomePage() { async function getUser() { const response = await fetchAPIGet("user-data") + if (response.status == 401) { + navigate("/login") + return // if unauthorized + } setInfo(await response.json()) } @@ -166,6 +168,8 @@ function TableData({ allTactics }: { allTactics: Tactic[] }) { } }) + const navigate = useNavigate() + i = 0 while (i < nbRow) { listTactic[i] = listTactic[i].map((tactic: Tactic) => ( @@ -173,7 +177,7 @@ function TableData({ allTactics }: { allTactics: Tactic[] }) { key={tactic.id} className="data" onClick={() => { - location.pathname = BASE + "/tactic/" + tactic.id + "/edit" + navigate("/tactic/" + tactic.id + "/edit") }}> {truncateString(tactic.name, 25)} @@ -212,13 +216,12 @@ function BodyPersonalSpace({ allTactics }: { allTactics: Tactic[] }) { } function Team({ teams }: { teams: Team[] }) { + const navigate = useNavigate() return (

Mes équipes

-
@@ -228,6 +231,8 @@ function Team({ teams }: { teams: Team[] }) { } function Tactic({ lastTactics }: { lastTactics: Tactic[] }) { + const navigate = useNavigate() + return (
@@ -235,7 +240,7 @@ function Tactic({ lastTactics }: { lastTactics: Tactic[] }) {
@@ -246,25 +251,29 @@ function Tactic({ lastTactics }: { lastTactics: Tactic[] }) { function SetButtonTactic({ tactics }: { tactics: Tactic[] }) { const lastTactics = tactics.map((tactic) => ( - + )) return
{lastTactics}
} function SetButtonTeam({ teams }: { teams: Team[] }) { - const listTeam = teams.map((teams) => ) + const listTeam = teams.map((team) => ( + + )) return
{listTeam}
} function ButtonTeam({ team }: { team: Team }) { const name = truncateString(team.name, 20) + const navigate = useNavigate() + return (
{ - location.pathname = BASE + "/team/" + team.id + navigate("/team/" + team.id) }}> {name}
@@ -274,12 +283,14 @@ function ButtonTeam({ team }: { team: Team }) { function ButtonLastTactic({ tactic }: { tactic: Tactic }) { const name = truncateString(tactic.name, 20) + const navigate = useNavigate() + return (
{ - location.pathname = BASE + "/tactic/" + tactic.id + "/edit" + navigate("/tactic/" + tactic.id + "/edit") }}> {name}
diff --git a/src/pages/LoginPage.tsx b/src/pages/LoginPage.tsx index 6d65091..ae83a9b 100644 --- a/src/pages/LoginPage.tsx +++ b/src/pages/LoginPage.tsx @@ -1,10 +1,9 @@ import { FormEvent, startTransition, useState } from "react" -import { BASE } from "../Constants.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" +import { Link, useNavigate } from "react-router-dom" export default function LoginApp() { const [errors, setErrors] = useState([]) @@ -22,13 +21,16 @@ export default function LoginApp() { "auth/token", { email, password }, "POST", - false, ) if (response.ok) { const session = getSession() const { token, expirationDate } = await response.json() - saveSession({ ...session, auth: { token, expirationDate }, urlTarget: undefined }) + saveSession({ + ...session, + auth: { token, expirationDate }, + urlTarget: undefined, + }) startTransition(() => { navigate(session.urlTarget ?? "/") }) @@ -83,12 +85,7 @@ export default function LoginApp() {
- + - + Vous n'avez pas de compte ? - +

diff --git a/src/pages/NewTacticPage.tsx b/src/pages/NewTacticPage.tsx index 355a70a..c390f0e 100644 --- a/src/pages/NewTacticPage.tsx +++ b/src/pages/NewTacticPage.tsx @@ -44,7 +44,6 @@ function CourtKindButton({ image: string courtType: CourtType }) { - const navigate = useNavigate() return ( @@ -67,6 +66,12 @@ function CourtKindButton({ "POST", ) + if (response.status === 401) { + // if unauthorized + navigate("/login") + return + } + const { id } = await response.json() startTransition(() => { navigate(`/tactic/${id}/edit`) diff --git a/src/pages/RegisterPage.tsx b/src/pages/RegisterPage.tsx index 21d5347..baffa09 100644 --- a/src/pages/RegisterPage.tsx +++ b/src/pages/RegisterPage.tsx @@ -1,11 +1,10 @@ import { FormEvent, startTransition, useRef, useState } from "react" -import { BASE } from "../Constants.ts" import "../style/form.css" import { Failure } from "../api/failure.ts" import { fetchAPI } from "../Fetcher.ts" import { getSession, saveSession } from "../api/session.ts" -import { useNavigate } from "react-router-dom" +import { Link, useNavigate } from "react-router-dom" export default function RegisterPage() { const usernameField = useRef(null) @@ -46,7 +45,11 @@ export default function RegisterPage() { if (response.ok) { const { token, expirationDate } = await response.json() const session = getSession() - saveSession({ ...session, auth: { token, expirationDate }, urlTarget: undefined }) + saveSession({ + ...session, + auth: { token, expirationDate }, + urlTarget: undefined, + }) startTransition(() => { navigate(session.urlTarget ?? "/") }) @@ -135,9 +138,9 @@ export default function RegisterPage() { confidentialité de Sportify. - + Vous avez déjà un compte ? - +
{ async function loadUsername() { - const response = await fetchAPIGet("user", false) + const response = await fetchAPIGet("user") - if (response.status == 401) { + if (response.status != 401) { //if unauthorized return }