diff --git a/traque-front/app/admin/layout.js b/traque-front/app/admin/layout.js index 0463940..a951f13 100644 --- a/traque-front/app/admin/layout.js +++ b/traque-front/app/admin/layout.js @@ -6,8 +6,8 @@ export default function AdminLayout({ children}) { return ( -
-
+
+
Admin
  • Home
  • @@ -15,7 +15,7 @@ export default function AdminLayout({ children}) {
  • Map
-
+
{children}
diff --git a/traque-front/app/admin/page.js b/traque-front/app/admin/page.js index c8c7e5d..dbb81a6 100644 --- a/traque-front/app/admin/page.js +++ b/traque-front/app/admin/page.js @@ -1,4 +1,5 @@ "use client"; +import { TeamReady } from "@/components/admin/teamReady"; import BlueButton, { GreenButton, RedButton } from "@/components/util/button"; import { useAdminConnexion } from "@/context/adminConnexionContext"; import useAdmin from "@/hook/useAdmin"; @@ -9,14 +10,15 @@ export default function AdminPage() { const { gameState, changeState } = useAdmin(); useProtect(); return ( -
-
+
+

Game state

Current : {gameState} changeState(GameState.SETUP)}>Reset game changeState(GameState.PLACEMENT)}>Start placement changeState(GameState.PLAYING)}>Start game
+ {gameState == GameState.PLACEMENT &&
}
) } \ No newline at end of file diff --git a/traque-front/app/admin/teams/page.js b/traque-front/app/admin/teams/page.js index 0b6db60..8e1bd12 100644 --- a/traque-front/app/admin/teams/page.js +++ b/traque-front/app/admin/teams/page.js @@ -15,7 +15,7 @@ export default function TeamAdminPage() { return (
-
+

Team list

diff --git a/traque-front/app/team/track/page.js b/traque-front/app/team/track/page.js index fb16cbd..d7a6db1 100644 --- a/traque-front/app/team/track/page.js +++ b/traque-front/app/team/track/page.js @@ -1,5 +1,7 @@ "use client"; import ActionDrawer from '@/components/team/actionDrawer'; +import PlacementOverlay from '@/components/team/placementOverlay'; +import { WaitingScreen } from '@/components/team/waitingScreen'; import { useTeamConnexion } from '@/context/teamConnexionContext'; import useGame from '@/hook/useGame'; import { GameState } from '@/util/gameState'; @@ -15,24 +17,23 @@ const PlacementMap = dynamic(() => import('@/components/team/map').then((mod) => }); export default function Track() { - const { gameState, name, ready } = useGame(); + const { gameState, captured} = useGame(); const { useProtect } = useTeamConnexion(); useProtect(); return <> - {gameState == GameState.PLAYING &&
+ {gameState == GameState.SETUP && } + {gameState == GameState.PLAYING && !captured &&
} + {gameState == GameState.PLAYING && captured &&
+
Vous avez été capturé
+
Instructions de retour ici
+
} {gameState == GameState.PLACEMENT &&
-
-
Placement
-
{name}
- {!ready &&
Positionez vous dans le cercle
} - {ready &&
Restez dans le cercle en attendant que la partie commence
} - -
+
} diff --git a/traque-front/components/admin/mapPicker.jsx b/traque-front/components/admin/mapPicker.jsx index f6aa757..a265f1d 100644 --- a/traque-front/components/admin/mapPicker.jsx +++ b/traque-front/components/admin/mapPicker.jsx @@ -3,6 +3,7 @@ import { useLocation } from "@/hook/useLocation"; import { use, useEffect, useState } from "react"; import "leaflet/dist/leaflet.css"; import { Circle, MapContainer, TileLayer, useMap } from "react-leaflet"; +import { useMapCircleDraw } from "@/hook/mapDrawing"; function MapPan(props) { const map = useMap(); @@ -34,36 +35,26 @@ function MapEventListener({onClick, onMouseMove}) { return null; } +const DEFAULT_ZOOM = 17; export function CircularAreaPicker({area, setArea, ...props}) { - const DEFAULT_ZOOM = 17; const location = useLocation(Infinity); - const [drawing, setDrawing] = useState(false); - const [center, setCenter] = useState(area?.center || null); - const [radius, setRadius] = useState(area?.radius || null); + const {handleClick, handleMouseMove, center, radius} = useMapCircleDraw(area, setArea); + return ( + + + {center && radius && } + + + ) +} - useEffect(() => { - console.log(area) - setDrawing(false); - setCenter(area?.center || null); - setRadius(area?.radius || null); - }, [area]) - - function handleClick(e) { - if(!drawing) { - setCenter(e.latlng); - setRadius(null); - setDrawing(true); - } else { - setDrawing(false); - setArea({center, radius}); - } - } - - function handleMouseMove(e) { - if(drawing) { - setRadius(e.latlng.distanceTo(center)); - } - } +export function ZonePicker({minArea, setMinArea, maxArea, setMaxArea, ...props}) { + const location = useLocation(Infinity); + const {handleClick: maxClick, handleMouseMove: maxHover, center: maxCenter, radius: maxRadius} = useMapCircleDraw(minArea, setMinArea); + const {handleClick: minClick, handleMouseMove: minHover, center: minCenter, radius: minRadius} = useMapCircleDraw(maxArea, setMaxArea); return ( Rename
- Remove + updateTeam(team.id, {captured: !team.captured})}>{team.captured ? "Revive" : "Capture"} + Remove

Team details

@@ -55,6 +56,7 @@ export default function TeamEdit({ selectedTeamId, setSelectedTeamId }) {

Chasing : {getTeamName(team.chasing)}

Chased by : {getTeamName(team.chased)}

Capture code : {String(team.captureCode).padStart(4, '0')}

+

Captured : {team.captured ? "Yes" : "No"}

diff --git a/traque-front/components/admin/teamList.jsx b/traque-front/components/admin/teamList.jsx index 85e828a..ee6ee41 100644 --- a/traque-front/components/admin/teamList.jsx +++ b/traque-front/components/admin/teamList.jsx @@ -12,7 +12,16 @@ const reorder = (list, startIndex, endIndex) => { }; function TeamListItem({ team, index, onSelected, itemSelected }) { - const classNames = 'w-full p-3 m-3 shadow ' + (itemSelected ? "bg-blue-400" : "bg-gray-100"); + let bgColor; + if(itemSelected) { + bgColor = "bg-blue-400"; + }else if(team.captured) { + bgColor = "bg-red-400"; + } + else { + bgColor = "bg-gray-100"; + } + const classNames = 'w-full p-3 my-3 shadow ' + (bgColor); return ( onSelected(team.id)}> {provided => ( diff --git a/traque-front/components/admin/teamReady.jsx b/traque-front/components/admin/teamReady.jsx new file mode 100644 index 0000000..21eb6d3 --- /dev/null +++ b/traque-front/components/admin/teamReady.jsx @@ -0,0 +1,16 @@ +import useAdmin from "@/hook/useAdmin" + +export function TeamReady() { + const {teams} = useAdmin(); + return
+

Teams ready status

+ {teams.map((team) => team.ready ? ( +
+
{team.name} : Ready
+
) : ( +
+
{team.name} : Not ready
+
+ ))} +
+} \ No newline at end of file diff --git a/traque-front/components/team/actionDrawer.jsx b/traque-front/components/team/actionDrawer.jsx index f9e095e..fe9dfce 100644 --- a/traque-front/components/team/actionDrawer.jsx +++ b/traque-front/components/team/actionDrawer.jsx @@ -2,16 +2,34 @@ import useGame from "@/hook/useGame"; import { useState } from "react" import BlueButton, { GreenButton, RedButton } from "../util/button"; import TextInput from "../util/textInput"; +import { useTeamConnexion } from "@/context/teamConnexionContext"; export default function ActionDrawer() { const [visible, setVisible] = useState(false); - const { sendCurrentPosition } = useGame(); + const [enemyCaptureCode, setEnemyCaptureCode] = useState(""); + const { sendCurrentPosition, name, captureCode, capture } = useGame(); + const {logout} = useTeamConnexion(); + + function handleCapture() { + capture(enemyCaptureCode); + setEnemyCaptureCode(""); + } return ( -
+
setVisible(!visible)} /> - {visible &&
-
+ {visible &&
+
+ +
+
+
+ {name} +
+
+ Capture code + {captureCode} +
30min before penalty @@ -22,27 +40,22 @@ export default function ActionDrawer() {
-
+
Update position
-
- Message log -
-
- See target info -
-
+
+
Target
+
+ See target info +
- { console.log(i) }} /> - Capture target + setEnemyCaptureCode(e.target.value)} /> + Capture target
-
+
Signal emergency
-
- Log out -
}
diff --git a/traque-front/components/team/map.jsx b/traque-front/components/team/map.jsx index fab937e..725e5d5 100644 --- a/traque-front/components/team/map.jsx +++ b/traque-front/components/team/map.jsx @@ -82,7 +82,7 @@ export function PlacementMap({ ...props}) { } - + {startingArea && } ) } diff --git a/traque-front/components/team/placementOverlay.jsx b/traque-front/components/team/placementOverlay.jsx new file mode 100644 index 0000000..c4bb5a6 --- /dev/null +++ b/traque-front/components/team/placementOverlay.jsx @@ -0,0 +1,19 @@ +import { useTeamConnexion } from "@/context/teamConnexionContext"; +import useGame from "@/hook/useGame" + +export default function PlacementOverlay() { + const { name, ready } = useGame(); + const {logout} = useTeamConnexion(); + return ( + <> + +
+
Placement
+
{name}
+ {!ready &&
Positionez vous dans le cercle
} + {ready &&
Restez dans le cercle en attendant que la partie commence
} + +
+ + ) +} \ No newline at end of file diff --git a/traque-front/components/team/waitingScreen.jsx b/traque-front/components/team/waitingScreen.jsx new file mode 100644 index 0000000..9d534f7 --- /dev/null +++ b/traque-front/components/team/waitingScreen.jsx @@ -0,0 +1,21 @@ +import { useTeamConnexion } from "@/context/teamConnexionContext"; +import useGame from "@/hook/useGame" + +export function WaitingScreen() { + const { name } = useGame(); + const { logout } = useTeamConnexion(); + return ( +
+
+ Equipe : {name} +
+
+ Jeu en préparation, veuillez patienter... +
+
+ Vous avez perdu Le Jeu +
+ +
+ ) +} \ No newline at end of file diff --git a/traque-front/context/teamConnexionContext.jsx b/traque-front/context/teamConnexionContext.jsx index 9b82f24..4f0ae3e 100644 --- a/traque-front/context/teamConnexionContext.jsx +++ b/traque-front/context/teamConnexionContext.jsx @@ -7,10 +7,10 @@ import { usePasswordProtect } from "@/hook/usePasswordProtect"; const teamConnexionContext = createContext(); const TeamConnexionProvider = ({ children }) => { const { teamSocket } = useSocket(); - const { login, password: teamId, loggedIn, loading } = useSocketAuth(teamSocket, "team_password"); + const { login, password: teamId, loggedIn, loading, logout } = useSocketAuth(teamSocket, "team_password"); const useProtect = () => usePasswordProtect("/team", "/team/track", loading, loggedIn); - const value = useMemo(() => ({ teamId, login, loggedIn, loading, useProtect}), [teamId, login, loggedIn, loading]); + const value = useMemo(() => ({ teamId, login, logout, loggedIn, loading, useProtect}), [teamId, login, loggedIn, loading]); return ( diff --git a/traque-front/hook/mapDrawing.jsx b/traque-front/hook/mapDrawing.jsx new file mode 100644 index 0000000..f354905 --- /dev/null +++ b/traque-front/hook/mapDrawing.jsx @@ -0,0 +1,37 @@ +import { useEffect, useState } from "react"; +import { useLocation } from "./useLocation"; + +export function useMapCircleDraw(area, setArea) { + const [drawing, setDrawing] = useState(false); + const [center, setCenter] = useState(area?.center || null); + const [radius, setRadius] = useState(area?.radius || null); + + useEffect(() => { + setDrawing(false); + setCenter(area?.center || null); + setRadius(area?.radius || null); + }, [area]) + + function handleClick(e) { + if(!drawing) { + setCenter(e.latlng); + setRadius(null); + setDrawing(true); + } else { + setDrawing(false); + setArea({center, radius}); + } + } + + function handleMouseMove(e) { + if(drawing) { + setRadius(e.latlng.distanceTo(center)); + } + } + return { + handleClick, + handleMouseMove, + center, + radius, + } +} \ No newline at end of file diff --git a/traque-front/hook/useGame.jsx b/traque-front/hook/useGame.jsx index c867a23..6fd468c 100644 --- a/traque-front/hook/useGame.jsx +++ b/traque-front/hook/useGame.jsx @@ -14,17 +14,20 @@ export default function useGame() { teamSocket.emit("send_position"); } - useEffect(() => console.log("teamInfos", teamInfos), [teamInfos]); - + function capture(captureCode) { + teamSocket.emit("capture", captureCode); + } return { sendCurrentPosition, + capture, enemyPosition: teamInfos?.enemyLocation || null, currentPosition: teamInfos?.currentLocation || null, startingArea: teamInfos?.startingArea || null, captureCode: teamInfos?.captureCode || null, name: teamInfos?.name || null, ready: teamInfos?.ready || false, + captured: teamInfos?.captured || false, teamId, gameState, }; diff --git a/traque-front/hook/useSocketAuth.jsx b/traque-front/hook/useSocketAuth.jsx index 7f4bfdb..b17a2f5 100644 --- a/traque-front/hook/useSocketAuth.jsx +++ b/traque-front/hook/useSocketAuth.jsx @@ -4,6 +4,7 @@ import { useLocalStorage } from './useLocalStorage'; import { usePathname } from 'next/navigation'; const LOGIN_MESSAGE = "login"; +const LOGOUT_MESSAGE = "logout"; const LOGIN_RESPONSE_MESSAGE = "login_response"; export function useSocketAuth(socket, passwordName) { @@ -13,15 +14,24 @@ export function useSocketAuth(socket, passwordName) { const [savedPassword, setSavedPassword, savedPasswordLoading] = useLocalStorage(passwordName, null); useEffect(() => { + console.log("Checking saved password", savedPassword, loggedIn); if (savedPassword && !loggedIn) { + console.log("Logging in with saved password", savedPassword); socket.emit(LOGIN_MESSAGE, savedPassword); } }, [savedPassword]); function login(password) { + console.log("Logging", password); setSavedPassword(password) } + function logout() { + setSavedPassword(null); + setLoggedIn(false); + socket.emit(LOGOUT_MESSAGE) + } + useSocketListener(socket, LOGIN_RESPONSE_MESSAGE,(loginResponse) => { setWaitingForResponse(false); setLoggedIn(loginResponse); @@ -39,5 +49,5 @@ export function useSocketAuth(socket, passwordName) { }, [waitingForResponse, savedPasswordLoading, savedPassword]); - return {login,password: savedPassword, loggedIn, loading}; + return {login,logout,password: savedPassword, loggedIn, loading}; } \ No newline at end of file diff --git a/traque-front/public/icons/logout.png b/traque-front/public/icons/logout.png new file mode 100644 index 0000000..38c0cfd Binary files /dev/null and b/traque-front/public/icons/logout.png differ