mirror of
https://git.rezel.net/LudoTech/traque.git
synced 2026-02-09 10:20:16 +01:00
Modification des paramètres en cours de jeu + UI + corrections
This commit is contained in:
@@ -76,9 +76,11 @@ export function initAdminSocketHandler() {
|
|||||||
}
|
}
|
||||||
if (!game.changeSettings(settings)) {
|
if (!game.changeSettings(settings)) {
|
||||||
socket.emit("error", "Invalid settings");
|
socket.emit("error", "Invalid settings");
|
||||||
|
socket.emit("game_settings", penaltyController.settings)
|
||||||
|
} else {
|
||||||
|
secureAdminBroadcast("game_settings", game.settings);
|
||||||
|
playersBroadcast("game_settings", game.settings);
|
||||||
}
|
}
|
||||||
secureAdminBroadcast("game_settings", game.settings);
|
|
||||||
playersBroadcast("game_settings", game.settings);
|
|
||||||
})
|
})
|
||||||
|
|
||||||
socket.on("set_zone_settings", (settings) => {
|
socket.on("set_zone_settings", (settings) => {
|
||||||
|
|||||||
@@ -297,7 +297,7 @@ export default {
|
|||||||
}
|
}
|
||||||
this.teams = this.teams.filter(t => t.id !== teamId);
|
this.teams = this.teams.filter(t => t.id !== teamId);
|
||||||
this.updateTeamChasing();
|
this.updateTeamChasing();
|
||||||
timeoutHandler.endSendPositionTimeout(team.id);
|
timeoutHandler.endSendPositionTimeout(teamId);
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -342,17 +342,23 @@ export default {
|
|||||||
* @returns false if failed
|
* @returns false if failed
|
||||||
*/
|
*/
|
||||||
setZoneSettings(newSettings) {
|
setZoneSettings(newSettings) {
|
||||||
//cannot change zones while playing
|
if ('min' in newSettings || 'max' in newSettings) {
|
||||||
|
const min = newSettings.min ?? zoneManager.zoneSettings.min;
|
||||||
|
const max = newSettings.max ?? zoneManager.zoneSettings.max;
|
||||||
|
// The end zone must be included in the start zone
|
||||||
|
if (!isInCircle(min.center, max.center, max.radius-min.radius)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
zoneManager.udpateSettings(newSettings);
|
||||||
if (this.state == GameState.PLAYING || this.state == GameState.FINISHED) {
|
if (this.state == GameState.PLAYING || this.state == GameState.FINISHED) {
|
||||||
return false;
|
zoneManager.reset()
|
||||||
|
if (!zoneManager.start()) {
|
||||||
|
this.setState(GameState.SETUP);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
var min = newSettings.min;
|
return true;
|
||||||
var max = newSettings.max;
|
|
||||||
// The end zone must be included in the start zone
|
|
||||||
if (!isInCircle(min.center, max.center, max.radius-min.radius)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return zoneManager.udpateSettings(newSettings);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -59,8 +59,8 @@ export function initPhotoUpload() {
|
|||||||
//App handler for serving the photo of a team given its secret ID
|
//App handler for serving the photo of a team given its secret ID
|
||||||
app.get("/photo/my", (req, res) => {
|
app.get("/photo/my", (req, res) => {
|
||||||
let team = game.getTeam(Number(req.query.team));
|
let team = game.getTeam(Number(req.query.team));
|
||||||
const imagePath = path.join(process.cwd(), UPLOAD_DIR, team.id.toString());
|
|
||||||
if (team) {
|
if (team) {
|
||||||
|
const imagePath = path.join(process.cwd(), UPLOAD_DIR, team.id.toString());
|
||||||
res.set("Content-Type", "image/png")
|
res.set("Content-Type", "image/png")
|
||||||
res.set("Access-Control-Allow-Origin", "*");
|
res.set("Access-Control-Allow-Origin", "*");
|
||||||
res.sendFile(fs.existsSync(imagePath) ? imagePath : path.join(process.cwd(), "images", "missing_image.jpg"));
|
res.sendFile(fs.existsSync(imagePath) ? imagePath : path.join(process.cwd(), "images", "missing_image.jpg"));
|
||||||
@@ -71,8 +71,8 @@ export function initPhotoUpload() {
|
|||||||
//App handler for serving the photo of the team chased by the team given by its secret ID
|
//App handler for serving the photo of the team chased by the team given by its secret ID
|
||||||
app.get("/photo/enemy", (req, res) => {
|
app.get("/photo/enemy", (req, res) => {
|
||||||
let team = game.getTeam(Number(req.query.team));
|
let team = game.getTeam(Number(req.query.team));
|
||||||
const imagePath = path.join(process.cwd(), UPLOAD_DIR, team.chasing.toString());
|
|
||||||
if (team) {
|
if (team) {
|
||||||
|
const imagePath = path.join(process.cwd(), UPLOAD_DIR, team.chasing.toString());
|
||||||
res.set("Content-Type", "image/png")
|
res.set("Content-Type", "image/png")
|
||||||
res.set("Access-Control-Allow-Origin", "*");
|
res.set("Access-Control-Allow-Origin", "*");
|
||||||
res.sendFile(fs.existsSync(imagePath) ? imagePath : path.join(process.cwd(), "images", "missing_image.jpg"));
|
res.sendFile(fs.existsSync(imagePath) ? imagePath : path.join(process.cwd(), "images", "missing_image.jpg"));
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ export default function AdminLayout({ children}) {
|
|||||||
<ul className='flex' >
|
<ul className='flex' >
|
||||||
<Link href="/admin"><li className="p-5 bg-gray-800 hover:bg-gray-600 transition-all cursor-pointer h-full">Admin</li></Link>
|
<Link href="/admin"><li className="p-5 bg-gray-800 hover:bg-gray-600 transition-all cursor-pointer h-full">Admin</li></Link>
|
||||||
<Link href="/admin/teams"><li className="p-5 bg-gray-800 hover:bg-gray-600 transition-all cursor-pointer h-full">Teams</li></Link>
|
<Link href="/admin/teams"><li className="p-5 bg-gray-800 hover:bg-gray-600 transition-all cursor-pointer h-full">Teams</li></Link>
|
||||||
|
<Link href="/admin/parameters"><li className="p-5 bg-gray-800 hover:bg-gray-600 transition-all cursor-pointer h-full">Parameters</li></Link>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div className="h-full overflow-y-scroll">
|
<div className="h-full overflow-y-scroll">
|
||||||
|
|||||||
@@ -1,16 +1,12 @@
|
|||||||
"use client";
|
"use client";
|
||||||
import { GameSettings } from "@/components/admin/gameSettings";
|
|
||||||
import { PenaltySettings } from "@/components/admin/penaltySettings";
|
|
||||||
import { TeamReady } from "@/components/admin/teamReady";
|
import { TeamReady } from "@/components/admin/teamReady";
|
||||||
import BlueButton, { GreenButton, RedButton } from "@/components/util/button";
|
import BlueButton, { GreenButton, RedButton } from "@/components/util/button";
|
||||||
import { useAdminConnexion } from "@/context/adminConnexionContext";
|
import { useAdminConnexion } from "@/context/adminConnexionContext";
|
||||||
import useAdmin from "@/hook/useAdmin";
|
import useAdmin from "@/hook/useAdmin";
|
||||||
import { GameState } from "@/util/gameState";
|
import { GameState } from "@/util/gameState";
|
||||||
import dynamic from "next/dynamic";
|
import dynamic from "next/dynamic";
|
||||||
|
import { TeamListFixed } from '@/components/admin/teamList';
|
||||||
|
|
||||||
const ZoneSelector = dynamic(() => import('@/components/admin/zoneSelector').then((mod) => mod.ZoneSelector), {
|
|
||||||
ssr: false
|
|
||||||
});
|
|
||||||
const LiveMap = dynamic(() => import('@/components/admin/mapPicker').then((mod) => mod.LiveMap), {
|
const LiveMap = dynamic(() => import('@/components/admin/mapPicker').then((mod) => mod.LiveMap), {
|
||||||
ssr: false
|
ssr: false
|
||||||
});
|
});
|
||||||
@@ -22,7 +18,7 @@ export default function AdminPage() {
|
|||||||
<div className='min-h-full bg-gray-200 p-10 flex flex-row content-start gap-5'>
|
<div className='min-h-full bg-gray-200 p-10 flex flex-row content-start gap-5'>
|
||||||
<div className="h-full w-2/6">
|
<div className="h-full w-2/6">
|
||||||
<div className='w-full mb-5 h-1/2 gap-3 bg-white p-10 flex flex-col text-center shadow-2xl '>
|
<div className='w-full mb-5 h-1/2 gap-3 bg-white p-10 flex flex-col text-center shadow-2xl '>
|
||||||
<h2 className="text-2xl">Game state </h2>
|
<h2 className="text-2xl">Game state</h2>
|
||||||
<strong className="p-5 bg-gray-900 text-white text-xl rounded">Current : {gameState}</strong>
|
<strong className="p-5 bg-gray-900 text-white text-xl rounded">Current : {gameState}</strong>
|
||||||
<div className="flex flex-row">
|
<div className="flex flex-row">
|
||||||
<RedButton onClick={() => changeState(GameState.SETUP)}>Reset game</RedButton>
|
<RedButton onClick={() => changeState(GameState.SETUP)}>Reset game</RedButton>
|
||||||
@@ -30,14 +26,11 @@ export default function AdminPage() {
|
|||||||
<BlueButton onClick={() => changeState(GameState.PLAYING)}>Start game</BlueButton>
|
<BlueButton onClick={() => changeState(GameState.PLAYING)}>Start game</BlueButton>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<GameSettings />
|
{gameState == GameState.PLACEMENT && <TeamReady />}
|
||||||
</div>
|
</div>
|
||||||
{gameState == GameState.PLACEMENT && <div className="max-h-5/6"><TeamReady /></div>}
|
<div className='grow flex-1 row-span-2 bg-white p-10 flex shadow-2xl'>
|
||||||
{(gameState == GameState.SETUP || gameState == GameState.PLACEMENT) && <ZoneSelector />}
|
|
||||||
{(gameState == GameState.SETUP || gameState == GameState.PLACEMENT) && <PenaltySettings />}
|
|
||||||
{gameState == GameState.PLAYING && <div className='grow flex-1 row-span-2 bg-white p-10 flex shadow-2xl'>
|
|
||||||
<LiveMap />
|
<LiveMap />
|
||||||
</div>}
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
22
traque-front/app/admin/parameters/page.js
Normal file
22
traque-front/app/admin/parameters/page.js
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
"use client";
|
||||||
|
import { GameSettings } from "@/components/admin/gameSettings";
|
||||||
|
import { PenaltySettings } from "@/components/admin/penaltySettings";
|
||||||
|
import { useAdminConnexion } from "@/context/adminConnexionContext";
|
||||||
|
import dynamic from "next/dynamic";
|
||||||
|
|
||||||
|
const ZoneSelector = dynamic(() => import('@/components/admin/zoneSelector').then((mod) => mod.ZoneSelector), {
|
||||||
|
ssr: false
|
||||||
|
});
|
||||||
|
export default function AdminPage() {
|
||||||
|
const { useProtect } = useAdminConnexion();
|
||||||
|
useProtect();
|
||||||
|
return (
|
||||||
|
<div className='min-h-full bg-gray-200 p-10 flex flex-row content-start gap-5'>
|
||||||
|
<div className="h-full w-2/6">
|
||||||
|
<GameSettings />
|
||||||
|
</div>
|
||||||
|
<ZoneSelector />
|
||||||
|
<PenaltySettings />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ import "leaflet/dist/leaflet.css";
|
|||||||
import { Circle, MapContainer, Marker, TileLayer, useMap, Tooltip, Polyline } from "react-leaflet";
|
import { Circle, MapContainer, Marker, TileLayer, useMap, Tooltip, Polyline } from "react-leaflet";
|
||||||
import { useMapCircleDraw } from "@/hook/mapDrawing";
|
import { useMapCircleDraw } from "@/hook/mapDrawing";
|
||||||
import useAdmin from "@/hook/useAdmin";
|
import useAdmin from "@/hook/useAdmin";
|
||||||
|
import { GameState } from "@/util/gameState";
|
||||||
|
|
||||||
const positionIcon = new L.Icon({
|
const positionIcon = new L.Icon({
|
||||||
iconUrl: '/icons/location.png',
|
iconUrl: '/icons/location.png',
|
||||||
@@ -109,7 +110,7 @@ export function ZonePicker({ minZone, setMinZone, maxZone, setMaxZone, editMode,
|
|||||||
export function LiveMap() {
|
export function LiveMap() {
|
||||||
const location = useLocation(Infinity);
|
const location = useLocation(Infinity);
|
||||||
const [timeLeftNextZone, setTimeLeftNextZone] = useState(null);
|
const [timeLeftNextZone, setTimeLeftNextZone] = useState(null);
|
||||||
const { zone, zoneExtremities, teams, nextZoneDate, isShrinking , getTeam} = useAdmin();
|
const { zone, zoneExtremities, teams, nextZoneDate, isShrinking , getTeam, gameState } = useAdmin();
|
||||||
|
|
||||||
// Remaining time before sending position
|
// Remaining time before sending position
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -143,16 +144,16 @@ export function LiveMap() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='min-h-full w-full'>
|
<div className='min-h-full w-full'>
|
||||||
<p>{`${isShrinking ? "Fin" : "Début"} du rétrécissement de la zone dans : ${formatTime(timeLeftNextZone)}`}</p>
|
{gameState == GameState.PLAYING && <p>{`${isShrinking ? "Fin" : "Début"} du rétrécissement de la zone dans : ${formatTime(timeLeftNextZone)}`}</p>}
|
||||||
<MapContainer className='min-h-full w-full' center={location} zoom={DEFAULT_ZOOM} scrollWheelZoom={true}>
|
<MapContainer className='min-h-full w-full' center={location} zoom={DEFAULT_ZOOM} scrollWheelZoom={true}>
|
||||||
<TileLayer
|
<TileLayer
|
||||||
attribution='© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
attribution='© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
||||||
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
|
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
|
||||||
/>
|
/>
|
||||||
<MapPan center={location} zoom={DEFAULT_ZOOM} />
|
<MapPan center={location} zoom={DEFAULT_ZOOM} />
|
||||||
{zone && <Circle center={zone.center} radius={zone.radius} color="blue" />}
|
{gameState == GameState.PLAYING && zone && <Circle center={zone.center} radius={zone.radius} color="blue" />}
|
||||||
{zoneExtremities && <Circle center={zoneExtremities.begin.center} radius={zoneExtremities.begin.radius} color='black' fill={false} />}
|
{gameState == GameState.PLAYING && zoneExtremities && <Circle center={zoneExtremities.begin.center} radius={zoneExtremities.begin.radius} color='black' fill={false} />}
|
||||||
{zoneExtremities && <Circle center={zoneExtremities.end.center} radius={zoneExtremities.end.radius} color='red' fill={false} />}
|
{gameState == GameState.PLAYING && zoneExtremities && <Circle center={zoneExtremities.end.center} radius={zoneExtremities.end.radius} color='red' fill={false} />}
|
||||||
{teams.map((team) => team.currentLocation && !team.captured &&
|
{teams.map((team) => team.currentLocation && !team.captured &&
|
||||||
<Marker key={team.id} position={team.currentLocation} icon={positionIcon}>
|
<Marker key={team.id} position={team.currentLocation} icon={positionIcon}>
|
||||||
<Tooltip permanent direction="top" offset={[0, -5]} className="custom-tooltip">{team.name}</Tooltip>
|
<Tooltip permanent direction="top" offset={[0, -5]} className="custom-tooltip">{team.name}</Tooltip>
|
||||||
|
|||||||
@@ -18,7 +18,14 @@ export const PenaltySettings = () => {
|
|||||||
}, [penaltySettings]);
|
}, [penaltySettings]);
|
||||||
|
|
||||||
function applySettings() {
|
function applySettings() {
|
||||||
changePenaltySettings({maxPenalties: Number(maxPenalties), allowedTimeOutOfZone: Number(allowedTimeOutOfZone), allowedTimeBetweenPositionUpdate: Number(allowedTimeBetweenUpdates)});
|
const newSettings = {maxPenalties: Number(maxPenalties), allowedTimeOutOfZone: Number(allowedTimeOutOfZone), allowedTimeBetweenPositionUpdate: Number(allowedTimeBetweenUpdates)};
|
||||||
|
const changingSettings = {};
|
||||||
|
for (const key in newSettings) {
|
||||||
|
if (newSettings[key] != penaltySettings[key]) {
|
||||||
|
changingSettings[key] = newSettings[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
changePenaltySettings(changingSettings);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -70,19 +70,17 @@ export default function TeamEdit({ selectedTeamId, setSelectedTeamId }) {
|
|||||||
<div className='flex w-1/2 flex-col space-y-2 h-min self-start'>
|
<div className='flex w-1/2 flex-col space-y-2 h-min self-start'>
|
||||||
<h2 className='text-2xl text-center'>Team details</h2>
|
<h2 className='text-2xl text-center'>Team details</h2>
|
||||||
<div>
|
<div>
|
||||||
<p>Secret : {String(team.id).padStart(6, '0')}</p>
|
<p>Secret : {String(team.id).padStart(6, '0').replace(/(\d{3})(\d{3})/, '$1 $2')}</p>
|
||||||
<p>Name : {team.name}</p>
|
<p>Capture code : {String(team.captureCode).padStart(4, '0')}</p>
|
||||||
<p>Chasing : {getTeamName(team.chasing)}</p>
|
<p>Chasing : {getTeamName(team.chasing)}</p>
|
||||||
<p>Chased by : {getTeamName(team.chased)}</p>
|
<p>Chased by : {getTeamName(team.chased)}</p>
|
||||||
<p>Capture code : {String(team.captureCode).padStart(4, '0')}</p>
|
|
||||||
<p>Captured : {team.captured ? "Yes" : "No"}</p>
|
|
||||||
<p>Has to send location before {new Date(team.locationSendDeadline).toTimeString()}</p>
|
|
||||||
<div className='flex flex-row'>
|
<div className='flex flex-row'>
|
||||||
<p>Penalties :</p>
|
<p>Penalties :</p>
|
||||||
<button className='w-7 h-7 mx-4 bg-blue-600 hover:bg-blue-500 text-md ease-out duration-200 text-white shadow-sm rounded' onClick={() => handleAddPenalty(-1)}>-</button>
|
<button className='w-7 h-7 mx-4 bg-blue-600 hover:bg-blue-500 text-md ease-out duration-200 text-white shadow-sm rounded' onClick={() => handleAddPenalty(-1)}>-</button>
|
||||||
<p>{team.penalties}</p>
|
<p>{team.penalties}</p>
|
||||||
<button className='w-7 h-7 mx-4 bg-blue-600 hover:bg-blue-500 text-md ease-out duration-200 text-white shadow-sm rounded' onClick={() => handleAddPenalty(1)}>+</button>
|
<button className='w-7 h-7 mx-4 bg-blue-600 hover:bg-blue-500 text-md ease-out duration-200 text-white shadow-sm rounded' onClick={() => handleAddPenalty(1)}>+</button>
|
||||||
</div>
|
</div>
|
||||||
|
<br/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -12,16 +12,9 @@ const reorder = (list, startIndex, endIndex) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function TeamListItem({ team, index, onSelected, itemSelected }) {
|
function TeamListItem({ team, index, onSelected, itemSelected }) {
|
||||||
let bgColor;
|
const bgColor = team.captured ? " bg-red-400" : " bg-gray-300";
|
||||||
if(itemSelected) {
|
const border = " border border-4 " + (itemSelected ? "border-black" : team.captured ? "border-red-400" : "border-gray-300");
|
||||||
bgColor = "bg-blue-400";
|
const classNames = 'w-full p-3 my-3' + (bgColor) + (border);
|
||||||
}else if(team.captured) {
|
|
||||||
bgColor = "bg-red-400";
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
bgColor = "bg-gray-100";
|
|
||||||
}
|
|
||||||
const classNames = 'w-full p-3 my-3 shadow ' + (bgColor);
|
|
||||||
return (
|
return (
|
||||||
<Draggable draggableId={team.id.toString()} index={index} onClick={() => onSelected(team.id)}>
|
<Draggable draggableId={team.id.toString()} index={index} onClick={() => onSelected(team.id)}>
|
||||||
{provided => (
|
{provided => (
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import useAdmin from "@/hook/useAdmin"
|
|||||||
|
|
||||||
export function TeamReady() {
|
export function TeamReady() {
|
||||||
const {teams} = useAdmin();
|
const {teams} = useAdmin();
|
||||||
return <div className='w-max h-full gap-1 bg-white p-10 flex flex-col text-center shadow-2xl overflow-y-scroll'>
|
return <div className='w-full h-full gap-1 bg-white p-10 flex flex-col text-center shadow-2xl overflow-y-scroll'>
|
||||||
<h2 className="text-2xl">Teams ready status</h2>
|
<h2 className="text-2xl">Teams ready status</h2>
|
||||||
{teams.map((team) => team.ready ? (
|
{teams.map((team) => team.ready ? (
|
||||||
<div key={team.id} className="p-2 text-white bg-green-500 shadow-md text-xl rounded flex flex-row">
|
<div key={team.id} className="p-2 text-white bg-green-500 shadow-md text-xl rounded flex flex-row">
|
||||||
|
|||||||
@@ -24,7 +24,14 @@ export function ZoneSelector() {
|
|||||||
}, [zoneSettings]);
|
}, [zoneSettings]);
|
||||||
|
|
||||||
function handleSettingsSubmit() {
|
function handleSettingsSubmit() {
|
||||||
changeZoneSettings({min:minZone, max:maxZone, reductionCount: Number(reductionCount), reductionDuration: Number(reductionDuration), reductionInterval: Number(reductionInterval)});
|
const newSettings = {min:minZone, max:maxZone, reductionCount: Number(reductionCount), reductionDuration: Number(reductionDuration), reductionInterval: Number(reductionInterval)};
|
||||||
|
const changingSettings = {};
|
||||||
|
for (const key in newSettings) {
|
||||||
|
if (newSettings[key] != zoneSettings[key]) {
|
||||||
|
changingSettings[key] = newSettings[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
changeZoneSettings(changingSettings);
|
||||||
}
|
}
|
||||||
|
|
||||||
//When the user set one zone, switch to the other
|
//When the user set one zone, switch to the other
|
||||||
@@ -39,8 +46,8 @@ export function ZoneSelector() {
|
|||||||
|
|
||||||
return <div className='w-2/5 h-full gap-1 bg-white p-10 flex flex-col text-center shadow-2xl overflow-y-scroll'>
|
return <div className='w-2/5 h-full gap-1 bg-white p-10 flex flex-col text-center shadow-2xl overflow-y-scroll'>
|
||||||
<h2 className="text-2xl">Edit zones</h2>
|
<h2 className="text-2xl">Edit zones</h2>
|
||||||
{editMode == EditMode.MIN && <RedButton onClick={() => setEditMode(EditMode.MAX)}>Edit end zone</RedButton>}
|
{editMode == EditMode.MIN && <BlueButton onClick={() => setEditMode(EditMode.MAX)}>Click to edit first zone</BlueButton>}
|
||||||
{editMode == EditMode.MAX && <BlueButton onClick={() => setEditMode(EditMode.MIN)}>Edit start zone</BlueButton>}
|
{editMode == EditMode.MAX && <RedButton onClick={() => setEditMode(EditMode.MIN)}>Click to edit last zone</RedButton>}
|
||||||
<ZonePicker minZone={minZone} maxZone={maxZone} editMode={editMode} setMinZone={setMinZone} setMaxZone={setMaxZone} />
|
<ZonePicker minZone={minZone} maxZone={maxZone} editMode={editMode} setMinZone={setMinZone} setMaxZone={setMaxZone} />
|
||||||
<div>
|
<div>
|
||||||
<p>Number of reductions</p>
|
<p>Number of reductions</p>
|
||||||
|
|||||||
Reference in New Issue
Block a user