|
|
|
@ -1,9 +1,9 @@
|
|
|
|
|
import "../style/home/home.css"
|
|
|
|
|
import { useNavigate } from "react-router-dom"
|
|
|
|
|
import { useEffect, useState } from "react"
|
|
|
|
|
import { User } from "../model/User.ts"
|
|
|
|
|
import { createContext, Dispatch, useContext, useEffect, useReducer } from "react"
|
|
|
|
|
import { useAppFetcher } from "../App.tsx"
|
|
|
|
|
import { Visualizer } from "../components/Visualizer.tsx"
|
|
|
|
|
import BinSvg from "../assets/icon/bin.svg?react"
|
|
|
|
|
|
|
|
|
|
interface Tactic {
|
|
|
|
|
id: number
|
|
|
|
@ -19,12 +19,55 @@ interface Team {
|
|
|
|
|
second_color: string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
enum HomePageStateActionKind {
|
|
|
|
|
UPDATE_TACTICS = "UPDATE_TACTICS",
|
|
|
|
|
UPDATE_TEAMS = "UPDATE_TEAMS",
|
|
|
|
|
INIT = "INIT"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type HomePageStateAction = {
|
|
|
|
|
type: HomePageStateActionKind.UPDATE_TACTICS,
|
|
|
|
|
tactics: Tactic[]
|
|
|
|
|
} | {
|
|
|
|
|
type: HomePageStateActionKind.UPDATE_TEAMS,
|
|
|
|
|
teams: Team[]
|
|
|
|
|
} | {
|
|
|
|
|
type: HomePageStateActionKind.INIT,
|
|
|
|
|
state: HomePageState
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface HomePageState {
|
|
|
|
|
tactics: Tactic[]
|
|
|
|
|
teams: Team[]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function homePageStateReducer(state: HomePageState, action: HomePageStateAction): HomePageState {
|
|
|
|
|
switch (action.type) {
|
|
|
|
|
case HomePageStateActionKind.UPDATE_TACTICS:
|
|
|
|
|
return { ...state!, tactics: action.tactics }
|
|
|
|
|
|
|
|
|
|
case HomePageStateActionKind.UPDATE_TEAMS:
|
|
|
|
|
return { ...state!, teams: action.teams }
|
|
|
|
|
|
|
|
|
|
case HomePageStateActionKind.INIT:
|
|
|
|
|
return action.state
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface HomeStateContextMutable {
|
|
|
|
|
state: HomePageState,
|
|
|
|
|
dispatch: Dispatch<HomePageStateAction>
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const HomeStateContext = createContext<HomeStateContextMutable | null>(null)
|
|
|
|
|
|
|
|
|
|
function useHomeState() {
|
|
|
|
|
return useContext(HomeStateContext)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export default function HomePage() {
|
|
|
|
|
type UserDataResponse = { user?: User; tactics: Tactic[]; teams: Team[] }
|
|
|
|
|
const [{ tactics, teams }, setInfo] = useState<UserDataResponse>({
|
|
|
|
|
tactics: [],
|
|
|
|
|
teams: [],
|
|
|
|
|
})
|
|
|
|
|
const [state, dispatch] = useReducer(homePageStateReducer, { tactics: [], teams: [] })
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const navigate = useNavigate()
|
|
|
|
|
const fetcher = useAppFetcher()
|
|
|
|
@ -32,70 +75,50 @@ export default function HomePage() {
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
async function initUserData() {
|
|
|
|
|
const response = await fetcher.fetchAPIGet("user-data")
|
|
|
|
|
|
|
|
|
|
if (response.status == 401) {
|
|
|
|
|
navigate("/login")
|
|
|
|
|
return // if unauthorized
|
|
|
|
|
}
|
|
|
|
|
setInfo(await response.json())
|
|
|
|
|
type UserDataResponse = { teams: Team[], tactics: Tactic[] }
|
|
|
|
|
const { teams, tactics }: UserDataResponse = await response.json()
|
|
|
|
|
tactics.sort((a, b) => b.creationDate - a.creationDate)
|
|
|
|
|
dispatch({ type: HomePageStateActionKind.INIT, state: { teams, tactics } })
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
initUserData()
|
|
|
|
|
}, [fetcher, navigate])
|
|
|
|
|
|
|
|
|
|
tactics!.sort((a, b) => b.creationDate - a.creationDate)
|
|
|
|
|
|
|
|
|
|
const lastTactics = tactics.slice(0, 5)
|
|
|
|
|
return <Home teams={teams!} tactics={tactics!} lastTactics={lastTactics} />
|
|
|
|
|
return (
|
|
|
|
|
<HomeStateContext.Provider value={{ state, dispatch }}>
|
|
|
|
|
<Home />
|
|
|
|
|
</HomeStateContext.Provider>
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function Home({
|
|
|
|
|
lastTactics,
|
|
|
|
|
tactics,
|
|
|
|
|
teams,
|
|
|
|
|
}: {
|
|
|
|
|
lastTactics: Tactic[]
|
|
|
|
|
tactics: Tactic[]
|
|
|
|
|
teams: Team[]
|
|
|
|
|
}) {
|
|
|
|
|
function Home() {
|
|
|
|
|
return (
|
|
|
|
|
<div id="main">
|
|
|
|
|
<Body lastTactics={lastTactics} tactics={tactics} teams={teams} />
|
|
|
|
|
<Body />
|
|
|
|
|
</div>
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function Body({
|
|
|
|
|
lastTactics,
|
|
|
|
|
tactics,
|
|
|
|
|
teams,
|
|
|
|
|
}: {
|
|
|
|
|
lastTactics: Tactic[]
|
|
|
|
|
tactics: Tactic[]
|
|
|
|
|
teams: Team[]
|
|
|
|
|
}) {
|
|
|
|
|
function Body() {
|
|
|
|
|
const widthPersonalSpace = 78
|
|
|
|
|
const widthSideMenu = 100 - widthPersonalSpace
|
|
|
|
|
return (
|
|
|
|
|
<div id="body">
|
|
|
|
|
<PersonalSpace width={widthPersonalSpace} tactics={tactics} />
|
|
|
|
|
<PersonalSpace width={widthPersonalSpace} />
|
|
|
|
|
<SideMenu
|
|
|
|
|
width={widthSideMenu}
|
|
|
|
|
tactics={lastTactics}
|
|
|
|
|
teams={teams}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function SideMenu({
|
|
|
|
|
width,
|
|
|
|
|
tactics,
|
|
|
|
|
teams,
|
|
|
|
|
}: {
|
|
|
|
|
width: number
|
|
|
|
|
tactics: Tactic[]
|
|
|
|
|
teams: Team[]
|
|
|
|
|
}) {
|
|
|
|
|
function SideMenu({ width }: { width: number }) {
|
|
|
|
|
return (
|
|
|
|
|
<div
|
|
|
|
|
id="side-menu"
|
|
|
|
@ -103,20 +126,16 @@ function SideMenu({
|
|
|
|
|
width: width + "%",
|
|
|
|
|
}}>
|
|
|
|
|
<div id="side-menu-content">
|
|
|
|
|
<LastTeamsSideMenu teams={teams} />
|
|
|
|
|
<LastTacticsSideMenu tactics={tactics} />
|
|
|
|
|
<LastTeamsSideMenu />
|
|
|
|
|
<LastTacticsSideMenu />
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function PersonalSpace({
|
|
|
|
|
width,
|
|
|
|
|
tactics,
|
|
|
|
|
}: {
|
|
|
|
|
width: number
|
|
|
|
|
tactics: Tactic[]
|
|
|
|
|
}) {
|
|
|
|
|
width,
|
|
|
|
|
}: { width: number }) {
|
|
|
|
|
return (
|
|
|
|
|
<div
|
|
|
|
|
id="personal-space"
|
|
|
|
@ -124,7 +143,7 @@ function PersonalSpace({
|
|
|
|
|
width: width + "%",
|
|
|
|
|
}}>
|
|
|
|
|
<TitlePersonalSpace />
|
|
|
|
|
<BodyPersonalSpace tactics={tactics} />
|
|
|
|
|
<BodyPersonalSpace />
|
|
|
|
|
</div>
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
@ -149,6 +168,8 @@ function TacticGrid({ tactics }: { tactics: Tactic[] }) {
|
|
|
|
|
|
|
|
|
|
function TacticCard({ tactic }: { tactic: Tactic }) {
|
|
|
|
|
const navigate = useNavigate()
|
|
|
|
|
const fetcher = useAppFetcher()
|
|
|
|
|
const { state: { tactics }, dispatch } = useHomeState()!
|
|
|
|
|
return (
|
|
|
|
|
<div className={"tactic-card"}>
|
|
|
|
|
<div
|
|
|
|
@ -159,12 +180,28 @@ function TacticCard({ tactic }: { tactic: Tactic }) {
|
|
|
|
|
tacticId={tactic.id}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<p className="tactic-card-title">{tactic.name}</p>
|
|
|
|
|
<div className="tactic-card-content">
|
|
|
|
|
<p className="tactic-card-title">{tactic.name}</p>
|
|
|
|
|
<BinSvg
|
|
|
|
|
className="tactic-card-remove-btn"
|
|
|
|
|
onClick={async () => {
|
|
|
|
|
const response = await fetcher.fetchAPI(`tactics/${tactic.id}`, {}, "DELETE")
|
|
|
|
|
if (!response.ok) {
|
|
|
|
|
throw Error(`Cannot delete tactic ${tactic.id}!`)
|
|
|
|
|
}
|
|
|
|
|
dispatch({
|
|
|
|
|
type: HomePageStateActionKind.UPDATE_TACTICS,
|
|
|
|
|
tactics: tactics.filter(t => t.id !== tactic.id),
|
|
|
|
|
})
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function BodyPersonalSpace({ tactics }: { tactics: Tactic[] }) {
|
|
|
|
|
function BodyPersonalSpace() {
|
|
|
|
|
const tactics = useHomeState()!.state.tactics
|
|
|
|
|
return (
|
|
|
|
|
<div id="body-personal-space">
|
|
|
|
|
{tactics.length == 0 ? (
|
|
|
|
@ -176,7 +213,7 @@ function BodyPersonalSpace({ tactics }: { tactics: Tactic[] }) {
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function LastTeamsSideMenu({ teams }: { teams: Team[] }) {
|
|
|
|
|
function LastTeamsSideMenu() {
|
|
|
|
|
const navigate = useNavigate()
|
|
|
|
|
return (
|
|
|
|
|
<div id="teams">
|
|
|
|
@ -186,12 +223,12 @@ function LastTeamsSideMenu({ teams }: { teams: Team[] }) {
|
|
|
|
|
+
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
<SetButtonTeam teams={teams} />
|
|
|
|
|
<SetButtonTeam />
|
|
|
|
|
</div>
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function LastTacticsSideMenu({ tactics }: { tactics: Tactic[] }) {
|
|
|
|
|
function LastTacticsSideMenu() {
|
|
|
|
|
const navigate = useNavigate()
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
@ -205,26 +242,29 @@ function LastTacticsSideMenu({ tactics }: { tactics: Tactic[] }) {
|
|
|
|
|
+
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
<SetButtonTactic tactics={tactics} />
|
|
|
|
|
<SetButtonTactic />
|
|
|
|
|
</div>
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function SetButtonTactic({ tactics }: { tactics: Tactic[] }) {
|
|
|
|
|
function SetButtonTactic() {
|
|
|
|
|
const tactics = useHomeState()!.state.tactics.slice(0, 5)
|
|
|
|
|
const lastTactics = tactics.map((tactic) => (
|
|
|
|
|
<ButtonLastTactic key={tactic.id} tactic={tactic} />
|
|
|
|
|
<LastTacticCard key={tactic.id} tactic={tactic} />
|
|
|
|
|
))
|
|
|
|
|
return <div className="set-button">{lastTactics}</div>
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function SetButtonTeam({ teams }: { teams: Team[] }) {
|
|
|
|
|
function SetButtonTeam() {
|
|
|
|
|
const teams = useHomeState()!.state.teams
|
|
|
|
|
|
|
|
|
|
const listTeam = teams.map((team) => (
|
|
|
|
|
<ButtonTeam key={team.id} team={team} />
|
|
|
|
|
<TeamCard key={team.id} team={team} />
|
|
|
|
|
))
|
|
|
|
|
return <div className="set-button">{listTeam}</div>
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function ButtonTeam({ team }: { team: Team }) {
|
|
|
|
|
function TeamCard({ team }: { team: Team }) {
|
|
|
|
|
const name = truncateString(team.name, 20)
|
|
|
|
|
const navigate = useNavigate()
|
|
|
|
|
|
|
|
|
@ -242,7 +282,7 @@ function ButtonTeam({ team }: { team: Team }) {
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function ButtonLastTactic({ tactic }: { tactic: Tactic }) {
|
|
|
|
|
function LastTacticCard({ tactic }: { tactic: Tactic }) {
|
|
|
|
|
const name = truncateString(tactic.name, 20)
|
|
|
|
|
const navigate = useNavigate()
|
|
|
|
|
|
|
|
|
|