mirror of
https://git.rezel.net/LudoTech/traque.git
synced 2026-02-09 02:10:18 +01:00
Polygon zones AND circle zones
This commit is contained in:
@@ -90,9 +90,9 @@ export function initAdminSocketHandler() {
|
||||
}
|
||||
if (!zoneManager.changeSettings(settings)) {
|
||||
socket.emit("error", "Error changing zone");
|
||||
socket.emit("zone_settings", zoneManager.settings)
|
||||
socket.emit("zone_settings", settings)
|
||||
} else {
|
||||
secureAdminBroadcast("zone_settings", zoneManager.settings)
|
||||
secureAdminBroadcast("zone_settings", settings)
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
@@ -4,6 +4,11 @@ import { secureAdminBroadcast } from './admin_socket.js';
|
||||
|
||||
/* -------------------------------- Useful functions and constants -------------------------------- */
|
||||
|
||||
const zoneTypes = {
|
||||
circle: "circle",
|
||||
polygon: "polygon"
|
||||
}
|
||||
|
||||
const EARTH_RADIUS = 6_371_000; // Radius of the earth in m
|
||||
|
||||
function haversine_distance({ lat: lat1, lng: lon1 }, { lat: lat2, lng: lon2 }) {
|
||||
@@ -20,9 +25,64 @@ function latlngEqual(latlng1, latlng2, epsilon = 1e-9) {
|
||||
}
|
||||
|
||||
|
||||
/* -------------------------------- Circle zones -------------------------------- */
|
||||
|
||||
const defaultCircleSettings = {type: zoneTypes.circle, min: null, max: null, reductionCount: 4, duration: 10}
|
||||
|
||||
function circleZone(center, radius, duration) {
|
||||
return {
|
||||
center: center,
|
||||
radius: radius,
|
||||
duration: duration,
|
||||
|
||||
isInZone(location) {
|
||||
return haversine_distance(center, location) < this.radius;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function circleSettingsToZones(settings) {
|
||||
const {min, max, reductionCount, duration} = settings;
|
||||
|
||||
if (!min || !max) return [];
|
||||
if (haversine_distance(max.center, min.center) > max.radius - min.radius) return [];
|
||||
|
||||
const zones = [circleZone(max.center, max.radius, duration)];
|
||||
const radiusReductionLength = (max.radius - min.radius) / reductionCount;
|
||||
let center = max.center;
|
||||
let radius = max.radius;
|
||||
|
||||
for (let i = 1; i < reductionCount; i++) {
|
||||
radius -= radiusReductionLength;
|
||||
let new_center = null;
|
||||
while (!new_center || haversine_distance(new_center, min.center) > radius - min.radius) {
|
||||
const angle = Math.random() * 2 * Math.PI;
|
||||
const angularDistance = Math.sqrt(Math.random()) * radiusReductionLength / EARTH_RADIUS;
|
||||
const lat0Rad = center.lat * Math.PI / 180;
|
||||
const lon0Rad = center.lng * Math.PI / 180;
|
||||
const latRad = Math.asin(
|
||||
Math.sin(lat0Rad) * Math.cos(angularDistance) +
|
||||
Math.cos(lat0Rad) * Math.sin(angularDistance) * Math.cos(angle)
|
||||
);
|
||||
|
||||
const lonRad = lon0Rad + Math.atan2(
|
||||
Math.sin(angle) * Math.sin(angularDistance) * Math.cos(lat0Rad),
|
||||
Math.cos(angularDistance) - Math.sin(lat0Rad) * Math.sin(latRad)
|
||||
);
|
||||
new_center = {lat: latRad * 180 / Math.PI, lng: lonRad * 180 / Math.PI};
|
||||
}
|
||||
center = new_center;
|
||||
zones.push(circleZone(center, radius, duration))
|
||||
}
|
||||
zones.push(circleZone(min.center, min.radius, 0));
|
||||
|
||||
return zones;
|
||||
}
|
||||
|
||||
|
||||
/* -------------------------------- Polygon zones -------------------------------- */
|
||||
|
||||
const defaultPolygonSettings = [];
|
||||
const defaultPolygonSettings = {type: zoneTypes.polygon, polygons: []}
|
||||
|
||||
function polygonZone(points, duration) {
|
||||
return {
|
||||
@@ -82,9 +142,11 @@ function mergePolygons(poly1, poly2) {
|
||||
}
|
||||
|
||||
function polygonSettingsToZones(settings) {
|
||||
const {polygons} = settings;
|
||||
|
||||
const zones = [];
|
||||
|
||||
for (const { polygon, duration } of settings.slice().reverse()) {
|
||||
for (const { polygon, duration } of polygons.slice().reverse()) {
|
||||
const length = zones.length;
|
||||
|
||||
if (length == 0) {
|
||||
@@ -104,67 +166,13 @@ function polygonSettingsToZones(settings) {
|
||||
}
|
||||
|
||||
|
||||
/* -------------------------------- Circle zones -------------------------------- */
|
||||
|
||||
const defaultCircleSettings = { min: null, max: null, reductionCount: 4, duration: 1 };
|
||||
|
||||
function circleZone(center, radius, duration) {
|
||||
return {
|
||||
center: center,
|
||||
radius: radius,
|
||||
duration: duration,
|
||||
|
||||
isInZone(location) {
|
||||
return haversine_distance(center, location) < this.radius;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function circleSettingsToZones(settings) {
|
||||
const {min, max, reductionCount, duration} = settings;
|
||||
if (haversine_distance(max.center, min.center) > max.radius - min.radius) {
|
||||
return null;
|
||||
}
|
||||
const zones = [circleZone(max.center, max.radius, duration)];
|
||||
const radiusReductionLength = (max.radius - min.radius) / reductionCount;
|
||||
let center = max.center;
|
||||
let radius = max.radius;
|
||||
for (let i = 1; i < reductionCount; i++) {
|
||||
radius -= radiusReductionLength;
|
||||
let new_center = null;
|
||||
while (!new_center || haversine_distance(new_center, min.center) > radius - min.radius) {
|
||||
const angle = Math.random() * 2 * Math.PI;
|
||||
const angularDistance = Math.sqrt(Math.random()) * radiusReductionLength / EARTH_RADIUS;
|
||||
const lat0Rad = center.lat * Math.PI / 180;
|
||||
const lon0Rad = center.lng * Math.PI / 180;
|
||||
const latRad = Math.asin(
|
||||
Math.sin(lat0Rad) * Math.cos(angularDistance) +
|
||||
Math.cos(lat0Rad) * Math.sin(angularDistance) * Math.cos(angle)
|
||||
);
|
||||
|
||||
const lonRad = lon0Rad + Math.atan2(
|
||||
Math.sin(angle) * Math.sin(angularDistance) * Math.cos(lat0Rad),
|
||||
Math.cos(angularDistance) - Math.sin(lat0Rad) * Math.sin(latRad)
|
||||
);
|
||||
new_center = {lat: latRad * 180 / Math.PI, lng: lonRad * 180 / Math.PI};
|
||||
}
|
||||
center = new_center;
|
||||
zones.push(circleZone(center, radius, duration))
|
||||
}
|
||||
zones.push(circleZone(min.center, min.radius, 0));
|
||||
return zones;
|
||||
}
|
||||
|
||||
|
||||
/* -------------------------------- Zone manager -------------------------------- */
|
||||
|
||||
export default {
|
||||
isRunning: false,
|
||||
zones: [], // A zone has to be connected space that doesn't contain an earth pole
|
||||
currentZone: { id: 0, timeoutId: null, endDate: null },
|
||||
zoneType: "polygon",
|
||||
settings: defaultPolygonSettings,
|
||||
settingsToZones: polygonSettingsToZones,
|
||||
|
||||
start() {
|
||||
this.isRunning = true;
|
||||
@@ -209,25 +217,18 @@ export default {
|
||||
},
|
||||
|
||||
changeSettings(settings) {
|
||||
const zones = this.settingsToZones(settings);
|
||||
if (!zones) return false;
|
||||
this.zones = zones;
|
||||
switch (settings.type) {
|
||||
case zoneTypes.circle:
|
||||
this.zones = circleSettingsToZones(settings);
|
||||
break;
|
||||
case zoneTypes.polygon:
|
||||
this.zones = polygonSettingsToZones(settings);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
this.settings = settings;
|
||||
this.zoneBroadcast();
|
||||
return true;
|
||||
},
|
||||
|
||||
changeZoneType(type) {
|
||||
if (this.zoneType == type) return;
|
||||
if (type == "circle") {
|
||||
this.zoneType = "circle";
|
||||
this.settings = defaultCircleSettings;
|
||||
this.settingsToZones = circleSettingsToZones;
|
||||
} else if (type == "polygon") {
|
||||
this.zoneType = "polygon";
|
||||
this.settings = defaultPolygonSettings;
|
||||
this.settingsToZones = polygonSettingsToZones;
|
||||
}
|
||||
},
|
||||
|
||||
zoneBroadcast() {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { Marker, Tooltip, Polyline, Polygon } from "react-leaflet";
|
||||
import { Marker, Tooltip, Polyline, Polygon, Circle } from "react-leaflet";
|
||||
import "leaflet/dist/leaflet.css";
|
||||
import { CustomMapContainer } from "@/components/map";
|
||||
import useAdmin from "@/hook/useAdmin";
|
||||
@@ -13,9 +13,14 @@ const positionIcon = new L.Icon({
|
||||
shadowSize: [30, 30],
|
||||
});
|
||||
|
||||
const zoneTypes = {
|
||||
circle: "circle",
|
||||
polygon: "polygon"
|
||||
}
|
||||
|
||||
export default function LiveMap({mapStyle, showZones, showNames, showArrows}) {
|
||||
const { zoneSettings, zoneExtremities, teams, nextZoneDate, getTeam, gameState } = useAdmin();
|
||||
const [timeLeftNextZone, setTimeLeftNextZone] = useState(null);
|
||||
const { zoneExtremities, teams, nextZoneDate, getTeam, gameState } = useAdmin();
|
||||
|
||||
// Remaining time before sending position
|
||||
useEffect(() => {
|
||||
@@ -49,12 +54,34 @@ export default function LiveMap({mapStyle, showZones, showNames, showArrows}) {
|
||||
}
|
||||
}
|
||||
|
||||
function Zones() {
|
||||
if (!(showZones && gameState == GameState.PLAYING && zoneSettings)) return null;
|
||||
|
||||
switch (zoneSettings.type) {
|
||||
case zoneTypes.circle:
|
||||
return (
|
||||
<div>
|
||||
{ zoneExtremities.begin && <Circle center={zoneExtremities.begin.center} radius={zoneExtremities.begin.radius} color="red" fillColor="red" />}
|
||||
{ zoneExtremities.end && <Circle center={zoneExtremities.end.center} radius={zoneExtremities.end.radius} color="green" fillColor="green" />}
|
||||
</div>
|
||||
);
|
||||
case zoneTypes.polygon:
|
||||
return (
|
||||
<div>
|
||||
{ zoneExtremities.begin && <Polygon positions={zoneExtremities.begin.points} pathOptions={{ color: 'red', fillColor: 'red', fillOpacity: '0.1', weight: 3 }} />}
|
||||
{ zoneExtremities.end && <Polygon positions={zoneExtremities.end.points} pathOptions={{ color: 'green', fillColor: 'green', fillOpacity: '0.1', weight: 3 }} />}
|
||||
</div>
|
||||
);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='h-full w-full flex flex-col'>
|
||||
{gameState == GameState.PLAYING && <p>{`Next zone in : ${formatTime(timeLeftNextZone)}`}</p>}
|
||||
<CustomMapContainer mapStyle={mapStyle}>
|
||||
{showZones && gameState == GameState.PLAYING && zoneExtremities.begin && <Polygon positions={zoneExtremities.begin.points} pathOptions={{ color: 'red', fillColor: 'red', fillOpacity: '0.1', weight: 3 }} />}
|
||||
{showZones && gameState == GameState.PLAYING && zoneExtremities.end && <Polygon positions={zoneExtremities.end.points} pathOptions={{ color: 'green', fillColor: 'green', fillOpacity: '0.1', weight: 3 }} />}
|
||||
<Zones/>
|
||||
{teams.map((team) => team.currentLocation && !team.captured &&
|
||||
<Marker key={team.id} position={team.currentLocation} icon={positionIcon}>
|
||||
{showNames && <Tooltip permanent direction="top" offset={[0.5, -15]} className="custom-tooltip">{team.name}</Tooltip>}
|
||||
|
||||
@@ -49,55 +49,61 @@ function Drawings({ minZone, setMinZone, maxZone, setMaxZone, editMode }) {
|
||||
);
|
||||
}
|
||||
|
||||
export default function CircleZoneSelector() {
|
||||
export default function CircleZoneSelector({zoneSettings, updateZoneSettings, applyZoneSettings}) {
|
||||
const {penaltySettings, changePenaltySettings} = useAdmin();
|
||||
const [allowedTimeOutOfZone, setAllowedTimeOutOfZone] = useState("");
|
||||
const [editMode, setEditMode] = useState(EditMode.MIN);
|
||||
const [minZone, setMinZone] = useState(null);
|
||||
const [maxZone, setMaxZone] = useState(null);
|
||||
const [reductionCount, setReductionCount] = useState("");
|
||||
const [duration, setDuration] = useState("");
|
||||
const {zoneSettings, changeZoneSettings} = useAdmin();
|
||||
|
||||
useEffect(() => {
|
||||
if (zoneSettings) {
|
||||
setMinZone(zoneSettings.min);
|
||||
setMaxZone(zoneSettings.max);
|
||||
setReductionCount(zoneSettings.reductionCount.toString());
|
||||
setDuration(zoneSettings.duration.toString());
|
||||
}
|
||||
}, [zoneSettings]);
|
||||
setEditMode(editMode == EditMode.MIN ? EditMode.MAX : EditMode.MIN);
|
||||
}, [zoneSettings.min, zoneSettings.max])
|
||||
|
||||
// When the user set one zone, switch to the other
|
||||
useEffect(() => {
|
||||
if(editMode == EditMode.MIN) {
|
||||
setEditMode(EditMode.MAX);
|
||||
} else {
|
||||
setEditMode(EditMode.MIN);
|
||||
if (penaltySettings) {
|
||||
setAllowedTimeOutOfZone(penaltySettings.allowedTimeOutOfZone.toString());
|
||||
}
|
||||
|
||||
}, [minZone, maxZone]);
|
||||
}, [penaltySettings]);
|
||||
|
||||
function handleSettingsSubmit() {
|
||||
const newSettings = {min:minZone, max:maxZone, reductionCount: Number(reductionCount), duration: Number(duration)};
|
||||
changeZoneSettings(newSettings);
|
||||
console.log(zoneSettings)
|
||||
applyZoneSettings();
|
||||
changePenaltySettings({allowedTimeOutOfZone: Number(allowedTimeOutOfZone)});
|
||||
}
|
||||
|
||||
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>
|
||||
{editMode == EditMode.MIN && <BlueButton onClick={() => setEditMode(EditMode.MAX)}>Click to edit first zone</BlueButton>}
|
||||
{editMode == EditMode.MAX && <RedButton onClick={() => setEditMode(EditMode.MIN)}>Click to edit last zone</RedButton>}
|
||||
<CustomMapContainer>
|
||||
<Drawings minZone={minZone} maxZone={maxZone} editMode={editMode} setMinZone={setMinZone} setMaxZone={setMaxZone} />
|
||||
</CustomMapContainer>
|
||||
<div>
|
||||
<p>Number of zones</p>
|
||||
<TextInput value={reductionCount} onChange={(e) => setReductionCount(e.target.value)}></TextInput>
|
||||
<div className='h-full w-full bg-white p-3 gap-3 flex flex-row shadow-2xl'>
|
||||
<div className="h-full flex-1">
|
||||
<CustomMapContainer>
|
||||
<Drawings minZone={zoneSettings.min} setMinZone={(e) => updateZoneSettings("min", e)} maxZone={zoneSettings.max} setMaxZone={(e) => updateZoneSettings("max", e)} editMode={editMode} />
|
||||
</CustomMapContainer>
|
||||
</div>
|
||||
<div>
|
||||
<p>Duration of a zone</p>
|
||||
<TextInput value={duration} onChange={(e) => setDuration(e.target.value)}></TextInput>
|
||||
<div className="h-full w-1/6 flex flex-col gap-3">
|
||||
<div className="w-full h-15">
|
||||
{editMode == EditMode.MIN && <BlueButton onClick={() => setEditMode(EditMode.MAX)}>Click to edit first zone</BlueButton>}
|
||||
{editMode == EditMode.MAX && <RedButton onClick={() => setEditMode(EditMode.MIN)}>Click to edit last zone</RedButton>}
|
||||
</div>
|
||||
<div className="w-full flex flex-row gap-2 items-center justify-between">
|
||||
<p>Number</p>
|
||||
<div className="w-16 h-10">
|
||||
<TextInput value={zoneSettings.reductionCount} onChange={(e) => updateZoneSettings("reductionCount", e.target.value)} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-full flex flex-row gap-2 items-center justify-between">
|
||||
<p>Duration</p>
|
||||
<div className="w-16 h-10">
|
||||
<TextInput value={zoneSettings.duration} onChange={(e) => updateZoneSettings("duration", e.target.value)} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-full flex flex-row gap-2 items-center justify-between">
|
||||
<p>Timeout</p>
|
||||
<div className="w-16 h-10">
|
||||
<TextInput value={allowedTimeOutOfZone} onChange={(e) => setAllowedTimeOutOfZone(e.target.value)} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-full h-15">
|
||||
<GreenButton onClick={handleSettingsSubmit}>Apply</GreenButton>
|
||||
</div>
|
||||
</div>
|
||||
<GreenButton onClick={handleSettingsSubmit}>Apply</GreenButton>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -94,45 +94,46 @@ function Drawings({ polygons, addPolygon, removePolygon }) {
|
||||
);
|
||||
}
|
||||
|
||||
export default function PolygonZoneSelector() {
|
||||
export default function PolygonZoneSelector({zoneSettings, updateZoneSettings, applyZoneSettings}) {
|
||||
const defaultDuration = 10;
|
||||
const [zones, setZones] = useState([]);
|
||||
const [polygons, setPolygons] = useState([]);
|
||||
const {zoneSettings, changeZoneSettings} = useAdmin();
|
||||
const {penaltySettings, changePenaltySettings} = useAdmin();
|
||||
const [allowedTimeOutOfZone, setAllowedTimeOutOfZone] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
setPolygons(zones.map((zone) => zone.polygon));
|
||||
}, [zones])
|
||||
if (zoneSettings) {
|
||||
const newPolygons = zoneSettings.polygons.map((zone) => ({id: idFromPolygon(zone.polygon), polygon: zone.polygon, duration: zone.duration}));
|
||||
setPolygons(newPolygons.map((zone) => zone.polygon));
|
||||
}
|
||||
}, [zoneSettings]);
|
||||
|
||||
useEffect(() => {
|
||||
if (zoneSettings) {
|
||||
setZones(zoneSettings.map((zone) => ({id: idFromPolygon(zone.polygon), polygon: zone.polygon, duration: zone.duration})));
|
||||
}
|
||||
if (penaltySettings) {
|
||||
setAllowedTimeOutOfZone(penaltySettings.allowedTimeOutOfZone.toString());
|
||||
}
|
||||
}, [zoneSettings, penaltySettings]);
|
||||
}, [penaltySettings]);
|
||||
|
||||
function idFromPolygon(polygon) {
|
||||
return (polygon[0].lat + polygon[1].lat + polygon[2].lat).toString() + (polygon[0].lng + polygon[1].lng + polygon[2].lng).toString();
|
||||
}
|
||||
|
||||
function addPolygon(polygon) {
|
||||
setZones([...zones, {id: idFromPolygon(polygon), polygon: polygon, duration: defaultDuration}]);
|
||||
const newPolygons = [...zoneSettings.polygons, {id: idFromPolygon(polygon), polygon: polygon, duration: defaultDuration}];
|
||||
updateZoneSettings("polygons", newPolygons);
|
||||
}
|
||||
|
||||
function removePolygon(i) {
|
||||
setZones(zones.filter((_, index) => index !== i));
|
||||
const newPolygons = zoneSettings.polygons.filter((_, index) => index !== i);
|
||||
updateZoneSettings("polygons", newPolygons);
|
||||
}
|
||||
|
||||
function updateDuration(i, duration) {
|
||||
setZones(zones.map((zone, index) => index === i ? {id: zone.id, polygon: zone.polygon, duration: duration} : zone));
|
||||
const newPolygons = zoneSettings.polygons.map((zone, index) => index === i ? {id: zone.id, polygon: zone.polygon, duration: duration} : zone);
|
||||
updateZoneSettings("polygons", newPolygons);
|
||||
}
|
||||
|
||||
function handleSettingsSubmit() {
|
||||
changeZoneSettings(zones);
|
||||
applyZoneSettings();
|
||||
changePenaltySettings({allowedTimeOutOfZone: Number(allowedTimeOutOfZone)});
|
||||
}
|
||||
|
||||
@@ -147,7 +148,7 @@ export default function PolygonZoneSelector() {
|
||||
<div className="w-full text-center">
|
||||
<h2 className="text-xl">Reduction order</h2>
|
||||
</div>
|
||||
<ReorderList droppableId="zones-order" array={zones} setArray={setZones}>
|
||||
<ReorderList droppableId="zones-order" array={zoneSettings.polygons} setArray={(polygons) => updateZoneSettings("polygons", polygons)}>
|
||||
{ (zone, i) =>
|
||||
<div className="w-full p-2 bg-white flex flex-row gap-2 items-center justify-between">
|
||||
<p>Zone {i+1}</p>
|
||||
|
||||
@@ -4,6 +4,7 @@ import dynamic from "next/dynamic";
|
||||
import Link from "next/link";
|
||||
import { TextInput } from "@/components/input";
|
||||
import { Section } from "@/components/section";
|
||||
import { BlueButton } from "@/components/button";
|
||||
import { useAdminConnexion } from "@/context/adminConnexionContext";
|
||||
import useAdmin from '@/hook/useAdmin';
|
||||
import Messages from "./components/messages";
|
||||
@@ -13,17 +14,20 @@ import TeamManager from './components/teamManager';
|
||||
const PolygonZoneSelector = dynamic(() => import('./components/polygonZoneSelector'), { ssr: false });
|
||||
const CircleZoneSelector = dynamic(() => import('./components/circleZoneSelector'), { ssr: false });
|
||||
|
||||
const zoneSelectors = {
|
||||
const zoneTypes = {
|
||||
circle: "circle",
|
||||
polygon: "polygon"
|
||||
}
|
||||
|
||||
const defaultCircleSettings = {type: zoneTypes.circle, min: null, max: null, reductionCount: 4, duration: 10}
|
||||
const defaultPolygonSettings = {type: zoneTypes.polygon, polygons: []}
|
||||
|
||||
export default function ConfigurationPage() {
|
||||
const {penaltySettings, changePenaltySettings, addTeam} = useAdmin();
|
||||
const {zoneSettings, changeZoneSettings, penaltySettings, changePenaltySettings, addTeam} = useAdmin();
|
||||
const { useProtect } = useAdminConnexion();
|
||||
const [allowedTimeBetweenUpdates, setAllowedTimeBetweenUpdates] = useState("");
|
||||
const [teamName, setTeamName] = useState('');
|
||||
const [zoneSelector, setZoneSelector] = useState(zoneSelectors.polygon);
|
||||
const [localZoneSettings, setLocalZoneSettings] = useState(zoneSettings);
|
||||
|
||||
useProtect();
|
||||
|
||||
@@ -33,12 +37,26 @@ export default function ConfigurationPage() {
|
||||
}
|
||||
}, [penaltySettings]);
|
||||
|
||||
useEffect(() => {
|
||||
if (zoneSettings) {
|
||||
setLocalZoneSettings(zoneSettings);
|
||||
}
|
||||
}, [zoneSettings]);
|
||||
|
||||
function updateLocalZoneSettings(key, value) {
|
||||
setLocalZoneSettings(prev => ({...prev, [key]: value}));
|
||||
};
|
||||
|
||||
function applySettings() {
|
||||
if (Number(allowedTimeBetweenUpdates) != penaltySettings.allowedTimeBetweenPositionUpdate) {
|
||||
changePenaltySettings({allowedTimeBetweenPositionUpdate: Number(allowedTimeBetweenUpdates)});
|
||||
}
|
||||
}
|
||||
|
||||
function handleChangeZoneType() {
|
||||
setLocalZoneSettings(localZoneSettings.type == zoneTypes.circle ? defaultPolygonSettings : defaultCircleSettings)
|
||||
}
|
||||
|
||||
function handleSubmit(e) {
|
||||
e.preventDefault();
|
||||
if (teamName !== "") {
|
||||
@@ -77,9 +95,18 @@ export default function ConfigurationPage() {
|
||||
</div>
|
||||
</Section>
|
||||
</div>
|
||||
<div className="h-full flex-1">
|
||||
{zoneSelector == zoneSelectors.circle && <CircleZoneSelector/>}
|
||||
{zoneSelector == zoneSelectors.polygon && <PolygonZoneSelector/>}
|
||||
<div className="h-full flex-1 flex flex-col">
|
||||
<div className="w-full h-20">
|
||||
{localZoneSettings && <BlueButton onClick={handleChangeZoneType}>Change zone type</BlueButton>}
|
||||
</div>
|
||||
<div className="w-full flex-1">
|
||||
{localZoneSettings && localZoneSettings.type == zoneTypes.circle &&
|
||||
<CircleZoneSelector zoneSettings={localZoneSettings} updateZoneSettings={updateLocalZoneSettings} applyZoneSettings={() => changeZoneSettings(localZoneSettings)}/>
|
||||
}
|
||||
{localZoneSettings && localZoneSettings.type == zoneTypes.polygon &&
|
||||
<PolygonZoneSelector zoneSettings={localZoneSettings} updateZoneSettings={updateLocalZoneSettings} applyZoneSettings={() => changeZoneSettings(localZoneSettings)}/>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -2,12 +2,11 @@ import { useEffect, useState } from 'react';
|
||||
import { DragDropContext, Draggable, Droppable } from '@hello-pangea/dnd';
|
||||
|
||||
export function List({array, children}) {
|
||||
// TODO : change key
|
||||
return (
|
||||
<div className='w-full h-full bg-gray-300 overflow-y-scroll'>
|
||||
<ul className="w-full p-1 pb-0">
|
||||
{array.map((elem, i) => (
|
||||
<li className="w-full" key={elem.id ?? i}>
|
||||
<li className="w-full" key={elem.id}>
|
||||
{children(elem, i)}
|
||||
<div className="w-full h-1"/>
|
||||
</li>
|
||||
|
||||
Reference in New Issue
Block a user