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

maxime 1 year ago
parent 1d1ac1e088
commit bf5de96871

@ -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

@ -17,4 +17,9 @@ module.exports = {
{ allowConstantExport: true },
],
},
settings: {
react: {
version: "detect",
},
},
}

@ -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
- 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

@ -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,
)

@ -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<Response> {
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<Response> {
export async function fetchAPIGet(url: string): Promise<Response> {
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<Response> {
// 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")!

@ -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 (
<EditorView
tactic={{
@ -180,18 +191,31 @@ function Editor({ id, name, courtType, content }: EditorProps) {
)
return SaveStates.Guest
}
return fetchAPI(`tactics/${id}/1`, { content }, "PUT").then(
(r) => (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
}}
/>
)

@ -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)}
</td>
@ -212,13 +216,12 @@ function BodyPersonalSpace({ allTactics }: { allTactics: Tactic[] }) {
}
function Team({ teams }: { teams: Team[] }) {
const navigate = useNavigate()
return (
<div id="teams">
<div className="titre-side-menu">
<h2 className="title">Mes équipes</h2>
<button
className="new"
onClick={() => (location.pathname = BASE + "/team/new")}>
<button className="new" onClick={() => navigate("/team/new")}>
+
</button>
</div>
@ -228,6 +231,8 @@ function Team({ teams }: { teams: Team[] }) {
}
function Tactic({ lastTactics }: { lastTactics: Tactic[] }) {
const navigate = useNavigate()
return (
<div id="tactic">
<div className="titre-side-menu">
@ -235,7 +240,7 @@ function Tactic({ lastTactics }: { lastTactics: Tactic[] }) {
<button
className="new"
id="create-tactic"
onClick={() => (location.pathname = BASE + "/tactic/new")}>
onClick={() => navigate("/tactic/new")}>
+
</button>
</div>
@ -246,25 +251,29 @@ function Tactic({ lastTactics }: { lastTactics: Tactic[] }) {
function SetButtonTactic({ tactics }: { tactics: Tactic[] }) {
const lastTactics = tactics.map((tactic) => (
<ButtonLastTactic tactic={tactic} />
<ButtonLastTactic key={tactic.id} tactic={tactic} />
))
return <div className="set-button">{lastTactics}</div>
}
function SetButtonTeam({ teams }: { teams: Team[] }) {
const listTeam = teams.map((teams) => <ButtonTeam team={teams} />)
const listTeam = teams.map((team) => (
<ButtonTeam key={team.id} team={team} />
))
return <div className="set-button">{listTeam}</div>
}
function ButtonTeam({ team }: { team: Team }) {
const name = truncateString(team.name, 20)
const navigate = useNavigate()
return (
<div>
<div
id={"button-team" + team.id}
className="button-side-menu data"
onClick={() => {
location.pathname = BASE + "/team/" + team.id
navigate("/team/" + team.id)
}}>
{name}
</div>
@ -274,12 +283,14 @@ function ButtonTeam({ team }: { team: Team }) {
function ButtonLastTactic({ tactic }: { tactic: Tactic }) {
const name = truncateString(tactic.name, 20)
const navigate = useNavigate()
return (
<div
id={"button" + tactic.id}
className="button-side-menu data"
onClick={() => {
location.pathname = BASE + "/tactic/" + tactic.id + "/edit"
navigate("/tactic/" + tactic.id + "/edit")
}}>
{name}
</div>

@ -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<Failure[]>([])
@ -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() {
<form onSubmit={handleSubmit}>
<div className="form-group">
<label htmlFor="email">Email :</label>
<input
type="text"
id="email"
name="email"
required
/>
<input type="text" id="email" name="email" required />
<label htmlFor="password">Mot de passe :</label>
<input
@ -98,9 +95,9 @@ export default function LoginApp() {
required
/>
<a href={BASE + "/register"} className="inscr">
<Link to={"register"} className="inscr">
Vous n'avez pas de compte ?
</a>
</Link>
<br />
<br />
<div id="buttons">

@ -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`)

@ -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<HTMLInputElement>(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.
</label>
<a href={BASE + "/login"} className="inscr">
<Link to={"/login"} className="inscr">
Vous avez déjà un compte ?
</a>
</Link>
</div>
<div id="buttons">
<input

@ -15,9 +15,9 @@ export function Header() {
useEffect(() => {
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
}

Loading…
Cancel
Save