Delete team pageweb (obselete + useless)

This commit is contained in:
Sebastien Riviere
2025-09-04 16:51:10 +02:00
parent 51b99a699f
commit 31c43f6361
16 changed files with 5 additions and 561 deletions

View File

@@ -1,19 +0,0 @@
import { useState } from "react";
import { BlueButton } from "@/components/button";
import { TextInput } from "@/components/input";
export default function LoginForm({ onSubmit, title, placeholder, buttonText}) {
const [value, setValue] = useState("");
function handleSubmit(e) {
e.preventDefault();
setValue("");
onSubmit(value);
}
return (
<form className="bg-white shadow-md max-w mx-auto p-5 mx-10 flex flex-col space-y-4" onSubmit={handleSubmit}>
<h1 className="text-2xl font-bold text-center text-gray-700">{title}</h1>
<TextInput inputMode="numeric" placeholder={placeholder} value={value} onChange={(e) => setValue(e.target.value)} name="team-id"/>
<BlueButton type="submit">{buttonText}</BlueButton>
</form>
)
}

View File

@@ -1,13 +0,0 @@
"use client";
import { TeamConnexionProvider } from "@/context/teamConnexionContext";
import { TeamProvider } from "@/context/teamContext";
export default function AdminLayout({ children }) {
return (
<TeamConnexionProvider>
<TeamProvider>
{children}
</TeamProvider>
</TeamConnexionProvider>
)
}

View File

@@ -1,13 +0,0 @@
"use client"
import { useTeamConnexion } from "@/context/teamConnexionContext";
import LoginForm from "./components/loginForm";
export default function Home() {
const { login,useProtect } = useTeamConnexion();
useProtect();
return (
<div>
<LoginForm title={"Team login"} placeholder={"team ID"} buttonText={"Login"} onSubmit={(value) => login(parseInt(value))}/>
</div>
);
}

View File

@@ -1,75 +0,0 @@
import { useEffect, useState } from "react"
import { BlueButton, GreenButton } from "@/components/button";
import { TextInput } from "@/components/input";
import useTeamConnexion from "@/context/teamConnexionContext";
import useGame from "@/hook/useGame";
import EnemyTeamModal from "./enemyTeamModal";
export default function ActionDrawer() {
const [visible, setVisible] = useState(false);
const [enemyCaptureCode, setEnemyCaptureCode] = useState("");
const { sendCurrentPosition, name, captureCode, capture, locationSendDeadline, penalties } = useGame();
const {logout} = useTeamConnexion();
const [timeLeftBeforePenalty, setTimeLeftBeforePenalty] = useState(0);
const [enemyModalVisible, setEnemyModalVisible] = useState(false);
useEffect(() => {
const interval = setInterval(() => {
console.log(locationSendDeadline)
const timeLeft = locationSendDeadline - Date.now();
setTimeLeftBeforePenalty(Math.floor(timeLeft / 1000 / 60));
}, 1000);
return () => clearInterval(interval);
}, [locationSendDeadline]);
function handleCapture() {
capture(enemyCaptureCode);
setEnemyCaptureCode("");
}
return (
<div className={"fixed w-screen bottom-0 z-10 bg-gray-100 flex justify-center rounded-t-2xl transition-all duration-200 flex flex-col " + (visible ? "h-full" : "h-20")}>
<img src="/icons/arrow_up.png" className={"w-full object-scale-down h-ful max-h-20 transition-all cursor-pointer duration-200 " + (visible && "rotate-180")} onClick={() => setVisible(!visible)} />
{visible && <div className="flex justify-between flex-col w-full h-full">
<div className="flex flex-row justify-around">
<img src="/icons/logout.png" onClick={logout} className='p-3 mx-2 w-14 h-14 bg-red-500 rounded-lg cursor-pointer bg-red z-20' />
</div>
<div className='shadow-2xl bg-white p-1 flex flex-col text-center mb-1 mx-auto w-4/5 rounded'>
<div>
<span className='text-2xl text-black'>{name}</span>
</div>
<div className='text-gray-700'>
<span> Capture code </span>
<span className='text-lg text-black'>{String(captureCode).padStart(4,"0")}</span>
</div>
<div className='text-gray-700'>
<span className='text-lg text-black'>{timeLeftBeforePenalty}min</span>
<span> before penalty</span>
</div>
<div className='w-1/2 flex flex-row justify-center mx-auto'>
{Array.from({ length: penalties }).map((_, i) => <div key={i} className='min-h-5 m-2 min-w-5 bg-red-400'></div>)}
{Array.from({ length: 3-penalties }).map((_, i) => <div key={i} className='min-h-5 m-2 min-w-5 bg-green-400'></div>)}
</div>
</div>
<div className="h-20">
<BlueButton onClick={sendCurrentPosition} className="h-10">Update position</BlueButton>
</div>
<div className="p-5 shadow-lg bg-white">
<div className="text-center text-2xl">Target</div>
<div className="h-20 my-1">
<GreenButton onClick={() => setEnemyModalVisible(true)}>See target info</GreenButton>
</div>
<div className="h-20 flex flex-row">
<TextInput inputMode="numeric" placeholder="Enemy code" value={enemyCaptureCode} onChange={(e) => setEnemyCaptureCode(e.target.value)} />
<GreenButton onClick={handleCapture}>Capture target</GreenButton>
</div>
</div>
{/* <div className="h-20">
<RedButton onClick={sendCurrentPosition}>Signal emergency</RedButton>
</div> */}
</div>
}
<EnemyTeamModal visible={enemyModalVisible} onClose={() => setEnemyModalVisible(false)} />
</div>
)
}

View File

@@ -1,40 +0,0 @@
import { useEffect, useRef } from "react";
import { env } from 'next-runtime-env';
import { RedButton } from "@/components/button";
import useGame from "@/hook/useGame";
export default function EnemyTeamModal({ visible, onClose }) {
const { teamId, enemyName } = useGame();
const imageRef = useRef(null);
useEffect(() => {
if (visible) {
refreshImage();
}
}, [visible]);
function refreshImage() {
imageRef.current.src = SERVER_URL + "/photo/enemy?team=" + teamId.toString() + "&t=" + new Date().getTime();
}
var protocol = "https://";
const NEXT_PUBLIC_SOCKET_HOST = env("NEXT_PUBLIC_SOCKET_HOST");
if (NEXT_PUBLIC_SOCKET_HOST == "localhost") {
protocol = "http://";
}
const SERVER_URL = protocol + NEXT_PUBLIC_SOCKET_HOST + "/back";
console.log(SERVER_URL);
return (visible &&
<>
<div className='fixed w-screen h-screen bg-black bg-opacity-50 z-10 text-center'></div>
<div className='fixed w-11/12 h-5/6 p-5 z-20 mx-auto inset-x-0 my-auto inset-y-0 flex flex-col justify-center rounded-xl transition-all shadow-xl bg-white '>
<h1 className="w-full text-center text-3xl mb-5">{enemyName}</h1>
<img ref={imageRef} src={SERVER_URL + "/photo/enemy?team=" + teamId.toString()} className='w-full h-full object-contain' />
<div className="h-20">
<RedButton onClick={onClose}>Close</RedButton>
</div>
</div>
</>
)
}

View File

@@ -1,105 +0,0 @@
import React, { useEffect, useState } from 'react'
import { Circle, MapContainer, Marker, Popup, TileLayer, useMap } from 'react-leaflet'
import 'leaflet-defaulticon-compatibility/dist/leaflet-defaulticon-compatibility.css'
import "leaflet-defaulticon-compatibility";
import "leaflet/dist/leaflet.css";
import useTeamContext from '@/context/teamContext';
import useGame from '@/hook/useGame';
const DEFAULT_ZOOM = 14;
// Pan to the center of the map when the position of the user is updated for the first time
function MapPan(props) {
const map = useMap();
const [initialized, setInitialized] = useState(false);
useEffect(() => {
if (!initialized && props.center) {
map.flyTo(props.center, DEFAULT_ZOOM, { animate: false });
setInitialized(true)
}
}, [props.center]);
return null;
}
function LiveZone() {
const { zone } = useTeamContext();
console.log('Zone', zone);
return zone && <Circle center={zone.center} radius={zone.radius} color='blue' fill={false} />
}
function ZoneExtremities() {
const { zoneExtremities } = useTeamContext();
console.log('Zone extremities', zoneExtremities);
return zoneExtremities?.begin && zoneExtremities?.end && <>
{/* <Circle center={zoneExtremities.begin.center} radius={zoneExtremities.begin.radius} color='black' fill={false} /> */}
<Circle center={zoneExtremities.end.center} radius={zoneExtremities.end.radius} color='red' fill={false} />
</>
}
export function LiveMap({ ...props }) {
const { currentPosition, enemyPosition } = useGame();
useEffect(() => {
console.log('Current position', currentPosition);
}, [currentPosition]);
return (
<MapContainer {...props} className='min-h-full z-0' center={[0, 0]} zoom={0} scrollWheelZoom={true}>
<TileLayer
attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
{currentPosition && <Marker position={currentPosition} icon={new L.Icon({
iconUrl: '/icons/location.png',
iconSize: [30, 30],
iconAnchor: [15, 15],
popupAnchor: [0, -15],
shadowSize: [30, 30],
})}>
<Popup>
Votre position
</Popup>
</Marker>}
{enemyPosition && <Marker position={enemyPosition} icon={new L.Icon({
iconUrl: '/icons/target.png',
iconSize: [30, 30],
iconAnchor: [15, 15],
popupAnchor: [0, -15],
shadowSize: [30, 30],
})}>
<Popup>
Position de l&apos;ennemi
</Popup>
</Marker>}
<MapPan center={currentPosition} />
<LiveZone />
<ZoneExtremities />
</MapContainer>
)
}
export function PlacementMap({ ...props }) {
const { currentPosition, startingArea } = useGame();
return (
<MapContainer {...props} className='min-h-full w-full z-0' center={[0, 0]} zoom={0} scrollWheelZoom={true}>
<TileLayer
attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
{currentPosition && <Marker position={currentPosition} icon={new L.Icon({
iconUrl: '/icons/location.png',
iconSize: [30, 30],
iconAnchor: [15, 15],
popupAnchor: [0, -15],
shadowSize: [30, 30],
})}>
<Popup>
Votre position
</Popup>
</Marker>}
<MapPan center={currentPosition} />
{startingArea && <Circle center={startingArea?.center} radius={startingArea?.radius} color='blue' />}
</MapContainer>
)
}

View File

@@ -1,56 +0,0 @@
import { useEffect, useState } from "react";
import { useSocketListener } from "@/hook/useSocketListener";
export default function Notification({ socket }) {
const [visible, setVisible] = useState(false);
const [timeoutId, setTimeoutId] = useState(null);
const [notification, setNotification] = useState(null);
useSocketListener(socket, "error", (notification) => {
console.log("error", notification);
setNotification({ type: "error", text: notification });
setVisible(true);
});
useSocketListener(socket, "success", (notification) => {
console.log("success", notification);
setNotification({ type: "success", text: notification });
setVisible(true);
});
useSocketListener(socket, "warning", (notification) => {
console.log("warning", notification);
setNotification({ type: "warning", text: notification });
setVisible(true);
});
// Hide the notification after 5 seconds
useEffect(() => {
console.log({ visible });
if (visible && notification?.text != undefined) {
clearTimeout(timeoutId);
if (notification?.type == "success") {
setTimeoutId(setTimeout(() => {
setVisible(false);
}, 3000));
}
}
}, [visible]);
const bgColorMap = {
error: "bg-red-500 text-white",
success: "bg-green-500",
warning: "bg-yellow-500"
}
const classNames = 'fixed relative w-11/12 p-5 z-30 mx-auto inset-x-0 flex justify-center rounded-xl transition-all shadow-xl ' + (visible ? "top-5 " : "-translate-y-full ");
return (
Object.keys(bgColorMap).map((key) =>
notification?.type == key &&
<div key={key} className={classNames + bgColorMap[key]} onClick={() => setVisible(false)}>
<p className="absolute top-2 right-2 p-2 rounded-l text-3xl bg-white">x</p>
<p className='text-center text-xl'>{notification?.text}</p>
</div>
));
}

View File

@@ -1,19 +0,0 @@
import useTeamConnexion from "@/context/teamConnexionContext";
import useGame from "@/hook/useGame";
export default function PlacementOverlay() {
const { name, ready } = useGame();
const {logout} = useTeamConnexion();
return (
<>
<img src="/icons/logout.png" onClick={logout} className='w-12 h-12 bg-red-500 p-2 top-1 right-1 rounded-lg cursor-pointer bg-red fixed z-20' />
<div className='fixed top-0 p-3 w-full bg-gray-300 shadow-xl rounded-b-xl flex flex-col z-10 justify-center items-center'>
<div className='text-2xl my-3'>Placement</div>
<div className='text-md'>{name}</div>
{!ready && <div className='text-lg font-semibold text-red-700'>Positionez vous dans le cercle</div>}
{ready && <div className='text-lg font-semibold text-green-700 text-center'>Restez dans le cercle en attendant que la partie commence</div>}
</div>
</>
)
}

View File

@@ -1,56 +0,0 @@
import { useRef } from "react";
import { env } from 'next-runtime-env';
import { GreenButton, LogoutButton } from "@/components/button";
import useTeamContext from "@/context/teamContext";
import useGame from "@/hook/useGame"
export default function WaitingScreen() {
const { name, teamId } = useGame();
const { gameSettings } = useTeamContext();
const imageRef = useRef(null);
const NEXT_PUBLIC_SOCKET_HOST = env("NEXT_PUBLIC_SOCKET_HOST");
var protocol = "https://";
if (NEXT_PUBLIC_SOCKET_HOST == "localhost") {
protocol = "http://";
}
const SERVER_URL = protocol + NEXT_PUBLIC_SOCKET_HOST + "/back";
console.log(SERVER_URL);
function sendImage() {
let data = new FormData();
data.append('file', document.querySelector('input[type="file"]').files[0]);
fetch(SERVER_URL + "/upload?team=" + teamId.toString() , {
method: 'POST',
body: data
}).then((response) => {
console.log(response);
refreshImage();
});
}
function refreshImage() {
imageRef.current.src = SERVER_URL + "/photo/my?team=" + teamId.toString() + "&t=" + new Date().getTime();
}
return (
<div className='h-full flex flex-col items-center justify-center'>
<LogoutButton />
<div className='text-4xl mt-10 text-center'>
Equipe : {name}
</div>
<div className='text-2xl text-center'>
{gameSettings?.waitingMessage}
</div>
<div className='text-2xl text-center my-10'>
<p>Uploadez une photo tous les membres de l&apos;équipe sont visibles</p>
<input type="file" name="file" accept="image/*" className=" my-5 block w-full text-slate-500 file:mr-4 file:py-2 file:px-4 file:rounded-md file:border-0 file:bg-blue-50 file:text-blue-700 hover:file:bg-blue-100"/>
<div className="h-20">
<GreenButton onClick={sendImage}>Envoyer</GreenButton>
</div>
</div>
{teamId && <img ref={imageRef} src={SERVER_URL + "/photo/my?team=" + teamId.toString()} className='w-screen h-1/2 object-contain' />}
</div>
)
}

View File

@@ -1,55 +0,0 @@
"use client";
import React from 'react';
import dynamic from 'next/dynamic';
import { LogoutButton } from '@/components/button';
import { useSocket } from '@/context/socketContext';
import { useTeamConnexion } from '@/context/teamConnexionContext';
import { useTeamContext } from '@/context/teamContext';
import useGame from '@/hook/useGame';
import { GameState } from '@/util/gameState';
import ActionDrawer from './components/actionDrawer';
import Notification from './components/notification';
import PlacementOverlay from './components/placementOverlay';
import WaitingScreen from './components/waitingScreen';
//Load the map without SSR
const LiveMap = dynamic(() => import('./components/map').then((mod) => mod.LiveMap), {
ssr: false
});
const PlacementMap = dynamic(() => import('./components/map').then((mod) => mod.PlacementMap), {
ssr: false
});
export default function Track() {
const { gameState, captured } = useGame();
const { gameSettings} = useTeamContext()
const { useProtect } = useTeamConnexion();
const { teamSocket: socket } = useSocket();
useProtect();
return <>
<Notification socket={socket} />
{gameState == GameState.SETUP && <WaitingScreen />}
{gameState == GameState.PLAYING && !captured && <div className='h-full'>
<LiveMap />
<ActionDrawer />
</div>
}
{gameState == GameState.PLAYING && captured && <div className='h-full'>
<div className='text-center text-2xl'>Vous avez été capturé</div>
<div className='text-center text-md'>{gameSettings?.capturedMessage}</div>
</div>}
{gameState == GameState.PLACEMENT &&
<div className='h-full'>
<PlacementOverlay />
<PlacementMap />
</div>
}
{gameState == GameState.FINISHED && <div className='h-full'>
<LogoutButton />
<div className='text-center text-2xl'>Partie terminée</div>
{captured && <div className='text-center text-md'>{gameSettings?.loserEndGameMessage}</div>}
{!captured && <div className='text-center text-md'>{gameSettings?.winnerEndGameMessage}</div>}
</div>
}
</>
}