apply suggestions
continuous-integration/drone/push Build is passing Details

pull/107/head
maxime 1 year ago
parent 8fc8aa6917
commit 1d1ac1e088

@ -1,2 +1,2 @@
VITE_API_ENDPOINT=http://localhost:5254 VITE_API_ENDPOINT=https://iqball.maxou.dev/api/dotnet-master
VITE_BASE= #VITE_API_ENDPOINT=http://localhost:5254

@ -5,6 +5,8 @@ module.exports = {
"eslint:recommended", "eslint:recommended",
"plugin:@typescript-eslint/recommended", "plugin:@typescript-eslint/recommended",
"plugin:react-hooks/recommended", "plugin:react-hooks/recommended",
"plugin:react/recommended",
"plugin:react/jsx-runtime",
], ],
ignorePatterns: ["dist", ".eslintrc.cjs"], ignorePatterns: ["dist", ".eslintrc.cjs"],
parser: "@typescript-eslint/parser", parser: "@typescript-eslint/parser",

1
.gitignore vendored

@ -14,7 +14,6 @@ dist-ssr
# Editor directories and files # Editor directories and files
.vscode/* .vscode/*
!.vscode/extensions.json
.idea .idea
.DS_Store .DS_Store
*.suo *.suo

@ -25,8 +25,9 @@ steps:
depends_on: depends_on:
- "front CI" - "front CI"
commands: commands:
- sed -Ei "s/VITE_BASE=/VITE_BASE=\\/${DRONE_BRANCH}/g" .env - # force to use the backend master branch if pushing on master
- npm run build -- --base=/$DRONE_BRANCH - 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 - mv dist/* /outputs

@ -6,4 +6,4 @@ export const API = import.meta.env.VITE_API_ENDPOINT
/** /**
* This constant defines the base app's 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)

@ -1,9 +1,6 @@
import { API, BASE } from "./Constants" import { API } from "./Constants"
import { getSession, saveSession, Session } from "./api/session.ts" import { getSession, saveSession, Session } from "./api/session.ts"
import { redirect } from "react-router-dom"
export function redirect(url: string) {
location.pathname = BASE + url
}
export async function fetchAPI( export async function fetchAPI(
url: string, url: string,
@ -62,11 +59,11 @@ async function handleResponse(
redirectIfNotAuth: boolean, redirectIfNotAuth: boolean,
): Promise<Response> { ): Promise<Response> {
// if we provided a token but still unauthorized, the token has expired // if we provided a token but still unauthorized, the token has expired
console.log(response.status)
if (response.status == 401) { if (response.status == 401) {
if (!redirectIfNotAuth) return response if (!redirectIfNotAuth) return response
redirect("/login")
saveSession({ ...session, urlTarget: location.pathname }) saveSession({ ...session, urlTarget: location.pathname })
return response return redirect("/login")
} }
const nextToken = response.headers.get("Next-Authorization")! const nextToken = response.headers.get("Next-Authorization")!

@ -99,20 +99,23 @@ interface EditorPageProps {
} }
export default function EditorPage({ guestMode }: EditorPageProps) { export default function EditorPage({ guestMode }: EditorPageProps) {
const [tactic, setTactic] = useState<TacticDto>() const [tactic, setTactic] = useState<TacticDto | null>(() => {
const { tacticId: idStr } = useParams()
const id = guestMode ? -1 : parseInt(idStr!)
useEffect(() => {
if (guestMode) { if (guestMode) {
setTactic({ return {
id: -1, id: -1,
courtType: "PLAIN", courtType: "PLAIN",
content: '{"components": []}', content: '{"components": []}',
name: DEFAULT_TACTIC_NAME, name: DEFAULT_TACTIC_NAME,
}) }
return
} }
return null;
})
const { tacticId: idStr } = useParams()
const id = guestMode ? -1 : parseInt(idStr!)
useEffect(() => {
if (guestMode)
return
async function initialize() { async function initialize() {
const infoResponse = fetchAPIGet(`tactics/${id}`) const infoResponse = fetchAPIGet(`tactics/${id}`)

@ -1,11 +1,11 @@
import "../style/home/home.css" import "../style/home/home.css"
// import AccountSvg from "../assets/account.svg?react"
import { BASE } from "../Constants" import { BASE } from "../Constants"
import { getSession } from "../api/session.ts" import { getSession } from "../api/session.ts"
import { fetchAPIGet, redirect } from "../Fetcher.ts" import { useNavigate } from "react-router-dom"
import { useLayoutEffect, useState } from "react" import { startTransition, useLayoutEffect, useState } from "react"
import { User } from "../model/User.ts" import { User } from "../model/User.ts"
import { fetchAPIGet } from "../Fetcher.ts"
interface Tactic { interface Tactic {
id: number id: number
@ -28,11 +28,15 @@ export default function HomePage() {
teams: [], teams: [],
}) })
const navigate = useNavigate()
useLayoutEffect(() => { useLayoutEffect(() => {
const session = getSession() const session = getSession()
if (!session.auth) { if (!session.auth) {
redirect("/login") startTransition(() => {
navigate("/login")
})
return return
} }
@ -42,7 +46,7 @@ export default function HomePage() {
} }
getUser() getUser()
}, []) }, [navigate])
tactics!.sort((a, b) => b.creationDate - a.creationDate) tactics!.sort((a, b) => b.creationDate - a.creationDate)

@ -1,21 +1,22 @@
import { FormEvent, useRef, useState } from "react" import { FormEvent, startTransition, useState } from "react"
import { BASE } from "../Constants.ts" import { BASE } from "../Constants.ts"
import { fetchAPI, redirect } from "../Fetcher.ts" import { fetchAPI } from "../Fetcher.ts"
import { Failure } from "../api/failure.ts" import { Failure } from "../api/failure.ts"
import { getSession, saveSession } from "../api/session.ts" import { getSession, saveSession } from "../api/session.ts"
import "../style/form.css" import "../style/form.css"
import { useNavigate } from "react-router-dom"
export default function LoginApp() { export default function LoginApp() {
const [errors, setErrors] = useState<Failure[]>([]) const [errors, setErrors] = useState<Failure[]>([])
const emailRef = useRef<HTMLInputElement>(null) const navigate = useNavigate()
const passwordRef = useRef<HTMLInputElement>(null)
async function handleSubmit(e: FormEvent) { async function handleSubmit(e: FormEvent) {
e.preventDefault() e.preventDefault()
const email = emailRef.current!.value const { email, password } = Object.fromEntries(
const password = passwordRef.current!.value new FormData(e.target as HTMLFormElement),
)
const response = await fetchAPI( const response = await fetchAPI(
"auth/token", "auth/token",
@ -28,7 +29,9 @@ export default function LoginApp() {
const session = getSession() const session = getSession()
const { token, expirationDate } = await response.json() const { token, expirationDate } = await response.json()
saveSession({ ...session, auth: { token, expirationDate }, urlTarget: undefined }) saveSession({ ...session, auth: { token, expirationDate }, urlTarget: undefined })
redirect(session.urlTarget ?? "/") startTransition(() => {
navigate(session.urlTarget ?? "/")
})
return return
} }
@ -38,7 +41,7 @@ export default function LoginApp() {
{ {
type: "Non autorisé", type: "Non autorisé",
messages: [ 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() {
<div className="form-group"> <div className="form-group">
<label htmlFor="email">Email :</label> <label htmlFor="email">Email :</label>
<input <input
ref={emailRef}
type="text" type="text"
id="email" id="email"
name="email" name="email"
@ -90,7 +92,6 @@ export default function LoginApp() {
<label htmlFor="password">Mot de passe :</label> <label htmlFor="password">Mot de passe :</label>
<input <input
ref={passwordRef}
type="password" type="password"
id="password" id="password"
name="password" name="password"

@ -4,9 +4,10 @@ import "../style/new_tactic_panel.css"
import plainCourt from "../assets/court/full_court.svg" import plainCourt from "../assets/court/full_court.svg"
import halfCourt from "../assets/court/half_court.svg" import halfCourt from "../assets/court/half_court.svg"
import { CourtType } from "../model/tactic/Tactic.ts" import { CourtType } from "../model/tactic/Tactic.ts"
import { useCallback } from "react" import { startTransition, useCallback } from "react"
import { fetchAPI, redirect } from "../Fetcher.ts" import { fetchAPI } from "../Fetcher.ts"
import { getSession } from "../api/session.ts" import { getSession } from "../api/session.ts"
import { useNavigate } from "react-router-dom"
export const DEFAULT_TACTIC_NAME = "Nouvelle tactique" export const DEFAULT_TACTIC_NAME = "Nouvelle tactique"
@ -43,13 +44,18 @@ function CourtKindButton({
image: string image: string
courtType: CourtType courtType: CourtType
}) { }) {
const navigate = useNavigate()
return ( return (
<div <div
className="court-kind-button" className="court-kind-button"
onClick={useCallback(async () => { onClick={useCallback(async () => {
// if user is not authenticated // if user is not authenticated
if (!getSession().auth) { if (!getSession().auth) {
redirect(`/tactic/edit-guest`) startTransition(() => {
navigate(`/tactic/edit-guest`)
})
} }
const response = await fetchAPI( const response = await fetchAPI(
@ -62,8 +68,10 @@ function CourtKindButton({
) )
const { id } = await response.json() const { id } = await response.json()
redirect(`/tactic/${id}/edit`) startTransition(() => {
}, [courtType])}> navigate(`/tactic/${id}/edit`)
})
}, [courtType, navigate])}>
<div className="court-kind-button-top"> <div className="court-kind-button-top">
<div className="court-kind-button-image-div"> <div className="court-kind-button-image-div">
<img <img

@ -1,10 +1,11 @@
import { FormEvent, useRef, useState } from "react" import { FormEvent, startTransition, useRef, useState } from "react"
import { BASE } from "../Constants.ts" import { BASE } from "../Constants.ts"
import "../style/form.css" import "../style/form.css"
import { Failure } from "../api/failure.ts" import { Failure } from "../api/failure.ts"
import { fetchAPI, redirect } from "../Fetcher.ts" import { fetchAPI } from "../Fetcher.ts"
import { getSession, saveSession } from "../api/session.ts" import { getSession, saveSession } from "../api/session.ts"
import { useNavigate } from "react-router-dom"
export default function RegisterPage() { export default function RegisterPage() {
const usernameField = useRef<HTMLInputElement>(null) const usernameField = useRef<HTMLInputElement>(null)
@ -14,6 +15,8 @@ export default function RegisterPage() {
const [errors, setErrors] = useState<Failure[]>([]) const [errors, setErrors] = useState<Failure[]>([])
const navigate = useNavigate()
async function handleSubmit(e: FormEvent) { async function handleSubmit(e: FormEvent) {
e.preventDefault() e.preventDefault()
@ -44,7 +47,9 @@ export default function RegisterPage() {
const { token, expirationDate } = await response.json() const { token, expirationDate } = await response.json()
const session = getSession() const session = getSession()
saveSession({ ...session, auth: { token, expirationDate }, urlTarget: undefined }) saveSession({ ...session, auth: { token, expirationDate }, urlTarget: undefined })
redirect(session.urlTarget ?? "/") startTransition(() => {
navigate(session.urlTarget ?? "/")
})
return return
} }

@ -2,22 +2,22 @@
// import "../style/visualizer.css" // import "../style/visualizer.css"
// import Court from "../assets/court/full_court.svg" // import Court from "../assets/court/full_court.svg"
// //
// // export default function Visualizer({ id, name }: { id: number; name: string }) { // export default function Visualizer({ id, name }: { id: number; name: string }) {
// // const [style, setStyle] = useState<CSSProperties>({}) // const [style, setStyle] = useState<CSSProperties>({})
// // //
// // return ( // return (
// // <div id="main"> // <div id="main">
// // <div id="topbar"> // <div id="topbar">
// // <h1>{name}</h1> // <h1>{name}</h1>
// // </div> // </div>
// // <div id="court-container"> // <div id="court-container">
// // <img // <img
// // id="court" // id="court"
// // src={Court} // src={Court}
// // style={style} // style={style}
// // alt="Basketball Court" // alt="Basketball Court"
// // /> // />
// // </div> // </div>
// // </div> // </div>
// // ) // )
// // } // }

@ -1,8 +1,9 @@
import AccountSvg from "../../assets/account.svg?react" import AccountSvg from "../../assets/account.svg?react"
import "../../style/template/header.css" import "../../style/template/header.css"
import { useEffect, useState } from "react" import { startTransition, useEffect, useState } from "react"
import { fetchAPIGet, redirect } from "../../Fetcher.ts" import { fetchAPIGet } from "../../Fetcher.ts"
import { getSession, saveSession } from "../../api/session.ts" import { getSession, saveSession } from "../../api/session.ts"
import { useNavigate } from "react-router-dom"
export function Header() { export function Header() {
const session = getSession() const session = getSession()
@ -10,6 +11,8 @@ export function Header() {
session.username ?? null, session.username ?? null,
) )
const navigate = useNavigate()
useEffect(() => { useEffect(() => {
async function loadUsername() { async function loadUsername() {
const response = await fetchAPIGet("user", false) const response = await fetchAPIGet("user", false)
@ -36,7 +39,7 @@ export function Header() {
<p <p
id="iqball" id="iqball"
className="clickable" className="clickable"
onClick={() => redirect("/")}> onClick={() => startTransition(() => navigate("/"))}>
IQBall IQBall
</p> </p>
</div> </div>
@ -46,14 +49,19 @@ export function Header() {
id="clickable-header-right" id="clickable-header-right"
onClick={() => { onClick={() => {
if (username) { if (username) {
redirect("/settings") startTransition(() => {
navigate("/settings")
})
return return
} }
saveSession({ saveSession({
...session, ...session,
urlTarget: location.pathname, urlTarget: location.pathname,
}) })
redirect("/login")
startTransition(() => {
navigate("/login")
})
}}> }}>
{/* <AccountSvg id="img-account" /> */} {/* <AccountSvg id="img-account" /> */}
<AccountSvg id="img-account" /> <AccountSvg id="img-account" />

@ -13,8 +13,6 @@ export default defineConfig({
cssInjectedByJsPlugin({ cssInjectedByJsPlugin({
relativeCSSInjection: true, relativeCSSInjection: true,
}), }),
svgr({ svgr(),
include: "**/*.svg?react",
}),
], ],
}) })

Loading…
Cancel
Save