mirror of
https://git.rezel.net/LudoTech/traque.git
synced 2026-02-09 10:20:16 +01:00
admin interface basic functionalities
This commit is contained in:
130
.gitignore
vendored
Normal file
130
.gitignore
vendored
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
.pnpm-debug.log*
|
||||||
|
|
||||||
|
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||||
|
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||||
|
|
||||||
|
# Runtime data
|
||||||
|
pids
|
||||||
|
*.pid
|
||||||
|
*.seed
|
||||||
|
*.pid.lock
|
||||||
|
|
||||||
|
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||||
|
lib-cov
|
||||||
|
|
||||||
|
# Coverage directory used by tools like istanbul
|
||||||
|
coverage
|
||||||
|
*.lcov
|
||||||
|
|
||||||
|
# nyc test coverage
|
||||||
|
.nyc_output
|
||||||
|
|
||||||
|
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||||
|
.grunt
|
||||||
|
|
||||||
|
# Bower dependency directory (https://bower.io/)
|
||||||
|
bower_components
|
||||||
|
|
||||||
|
# node-waf configuration
|
||||||
|
.lock-wscript
|
||||||
|
|
||||||
|
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||||
|
build/Release
|
||||||
|
|
||||||
|
# Dependency directories
|
||||||
|
node_modules/
|
||||||
|
jspm_packages/
|
||||||
|
|
||||||
|
# Snowpack dependency directory (https://snowpack.dev/)
|
||||||
|
web_modules/
|
||||||
|
|
||||||
|
# TypeScript cache
|
||||||
|
*.tsbuildinfo
|
||||||
|
|
||||||
|
# Optional npm cache directory
|
||||||
|
.npm
|
||||||
|
|
||||||
|
# Optional eslint cache
|
||||||
|
.eslintcache
|
||||||
|
|
||||||
|
# Optional stylelint cache
|
||||||
|
.stylelintcache
|
||||||
|
|
||||||
|
# Microbundle cache
|
||||||
|
.rpt2_cache/
|
||||||
|
.rts2_cache_cjs/
|
||||||
|
.rts2_cache_es/
|
||||||
|
.rts2_cache_umd/
|
||||||
|
|
||||||
|
# Optional REPL history
|
||||||
|
.node_repl_history
|
||||||
|
|
||||||
|
# Output of 'npm pack'
|
||||||
|
*.tgz
|
||||||
|
|
||||||
|
# Yarn Integrity file
|
||||||
|
.yarn-integrity
|
||||||
|
|
||||||
|
# dotenv environment variable files
|
||||||
|
.env
|
||||||
|
.env.development.local
|
||||||
|
.env.test.local
|
||||||
|
.env.production.local
|
||||||
|
.env.local
|
||||||
|
|
||||||
|
# parcel-bundler cache (https://parceljs.org/)
|
||||||
|
.cache
|
||||||
|
.parcel-cache
|
||||||
|
|
||||||
|
# Next.js build output
|
||||||
|
.next
|
||||||
|
out
|
||||||
|
|
||||||
|
# Nuxt.js build / generate output
|
||||||
|
.nuxt
|
||||||
|
dist
|
||||||
|
|
||||||
|
# Gatsby files
|
||||||
|
.cache/
|
||||||
|
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||||
|
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||||
|
# public
|
||||||
|
|
||||||
|
# vuepress build output
|
||||||
|
.vuepress/dist
|
||||||
|
|
||||||
|
# vuepress v2.x temp and cache directory
|
||||||
|
.temp
|
||||||
|
.cache
|
||||||
|
|
||||||
|
# Docusaurus cache and generated files
|
||||||
|
.docusaurus
|
||||||
|
|
||||||
|
# Serverless directories
|
||||||
|
.serverless/
|
||||||
|
|
||||||
|
# FuseBox cache
|
||||||
|
.fusebox/
|
||||||
|
|
||||||
|
# DynamoDB Local files
|
||||||
|
.dynamodb/
|
||||||
|
|
||||||
|
# TernJS port file
|
||||||
|
.tern-port
|
||||||
|
|
||||||
|
# Stores VSCode versions used for testing VSCode extensions
|
||||||
|
.vscode-test
|
||||||
|
|
||||||
|
# yarn v2
|
||||||
|
.yarn/cache
|
||||||
|
.yarn/unplugged
|
||||||
|
.yarn/build-state.yml
|
||||||
|
.yarn/install-state.gz
|
||||||
|
.pnp.*
|
||||||
130
traque-back/.gitignore
vendored
Normal file
130
traque-back/.gitignore
vendored
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
.pnpm-debug.log*
|
||||||
|
|
||||||
|
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||||
|
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||||
|
|
||||||
|
# Runtime data
|
||||||
|
pids
|
||||||
|
*.pid
|
||||||
|
*.seed
|
||||||
|
*.pid.lock
|
||||||
|
|
||||||
|
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||||
|
lib-cov
|
||||||
|
|
||||||
|
# Coverage directory used by tools like istanbul
|
||||||
|
coverage
|
||||||
|
*.lcov
|
||||||
|
|
||||||
|
# nyc test coverage
|
||||||
|
.nyc_output
|
||||||
|
|
||||||
|
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||||
|
.grunt
|
||||||
|
|
||||||
|
# Bower dependency directory (https://bower.io/)
|
||||||
|
bower_components
|
||||||
|
|
||||||
|
# node-waf configuration
|
||||||
|
.lock-wscript
|
||||||
|
|
||||||
|
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||||
|
build/Release
|
||||||
|
|
||||||
|
# Dependency directories
|
||||||
|
node_modules/
|
||||||
|
jspm_packages/
|
||||||
|
|
||||||
|
# Snowpack dependency directory (https://snowpack.dev/)
|
||||||
|
web_modules/
|
||||||
|
|
||||||
|
# TypeScript cache
|
||||||
|
*.tsbuildinfo
|
||||||
|
|
||||||
|
# Optional npm cache directory
|
||||||
|
.npm
|
||||||
|
|
||||||
|
# Optional eslint cache
|
||||||
|
.eslintcache
|
||||||
|
|
||||||
|
# Optional stylelint cache
|
||||||
|
.stylelintcache
|
||||||
|
|
||||||
|
# Microbundle cache
|
||||||
|
.rpt2_cache/
|
||||||
|
.rts2_cache_cjs/
|
||||||
|
.rts2_cache_es/
|
||||||
|
.rts2_cache_umd/
|
||||||
|
|
||||||
|
# Optional REPL history
|
||||||
|
.node_repl_history
|
||||||
|
|
||||||
|
# Output of 'npm pack'
|
||||||
|
*.tgz
|
||||||
|
|
||||||
|
# Yarn Integrity file
|
||||||
|
.yarn-integrity
|
||||||
|
|
||||||
|
# dotenv environment variable files
|
||||||
|
.env
|
||||||
|
.env.development.local
|
||||||
|
.env.test.local
|
||||||
|
.env.production.local
|
||||||
|
.env.local
|
||||||
|
|
||||||
|
# parcel-bundler cache (https://parceljs.org/)
|
||||||
|
.cache
|
||||||
|
.parcel-cache
|
||||||
|
|
||||||
|
# Next.js build output
|
||||||
|
.next
|
||||||
|
out
|
||||||
|
|
||||||
|
# Nuxt.js build / generate output
|
||||||
|
.nuxt
|
||||||
|
dist
|
||||||
|
|
||||||
|
# Gatsby files
|
||||||
|
.cache/
|
||||||
|
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||||
|
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||||
|
# public
|
||||||
|
|
||||||
|
# vuepress build output
|
||||||
|
.vuepress/dist
|
||||||
|
|
||||||
|
# vuepress v2.x temp and cache directory
|
||||||
|
.temp
|
||||||
|
.cache
|
||||||
|
|
||||||
|
# Docusaurus cache and generated files
|
||||||
|
.docusaurus
|
||||||
|
|
||||||
|
# Serverless directories
|
||||||
|
.serverless/
|
||||||
|
|
||||||
|
# FuseBox cache
|
||||||
|
.fusebox/
|
||||||
|
|
||||||
|
# DynamoDB Local files
|
||||||
|
.dynamodb/
|
||||||
|
|
||||||
|
# TernJS port file
|
||||||
|
.tern-port
|
||||||
|
|
||||||
|
# Stores VSCode versions used for testing VSCode extensions
|
||||||
|
.vscode-test
|
||||||
|
|
||||||
|
# yarn v2
|
||||||
|
.yarn/cache
|
||||||
|
.yarn/unplugged
|
||||||
|
.yarn/build-state.yml
|
||||||
|
.yarn/install-state.gz
|
||||||
|
.pnp.*
|
||||||
106
traque-back/game.js
Normal file
106
traque-back/game.js
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
import { Socket } from "socket.io";
|
||||||
|
|
||||||
|
export default class Game {
|
||||||
|
constructor() {
|
||||||
|
this.teams = [];
|
||||||
|
this.started = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
start() {
|
||||||
|
this.started = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
stop() {
|
||||||
|
this.started = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
getNewTeamId() {
|
||||||
|
let id = null;
|
||||||
|
while(id === null || this.teams.find(t => t.id === id)) {
|
||||||
|
id = Math.floor(Math.random() * 1000000);
|
||||||
|
}
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
addTeam(teamName) {
|
||||||
|
let id = this.getNewTeamId();
|
||||||
|
this.teams.push({
|
||||||
|
id: id,
|
||||||
|
name: teamName,
|
||||||
|
chasing: null,
|
||||||
|
chased: null,
|
||||||
|
currentLocation: [0, 0],
|
||||||
|
lastSentLocation: [0, 0],
|
||||||
|
enemyLocation: [0, 0]
|
||||||
|
});
|
||||||
|
this.updateTeamChasing();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateTeamChasing() {
|
||||||
|
if(this.teams.length <= 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
this.teams[0].chased = this.teams[this.teams.length - 1].id;
|
||||||
|
this.teams[this.teams.length - 1].chasing = this.teams[0].id;
|
||||||
|
|
||||||
|
for(let i = 0; i < this.teams.length - 1; i++) {
|
||||||
|
this.teams[i].chasing = this.teams[i + 1].id;
|
||||||
|
this.teams[i+1].chased = this.teams[i].id;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
reorderTeams(newOrder) {
|
||||||
|
this.teams = newOrder;
|
||||||
|
return this.updateTeamChasing();
|
||||||
|
}
|
||||||
|
|
||||||
|
getTeam(teamId) {
|
||||||
|
return this.teams.find(t => t.id === teamId);
|
||||||
|
}
|
||||||
|
|
||||||
|
renameTeam(teamId, newName) {
|
||||||
|
let team = this.getTeam(teamId);
|
||||||
|
if(team == undefined) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
team.name = newName;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateLocation(teamId, location) {
|
||||||
|
if(!this.started) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let team = this.getTeam(teamId);
|
||||||
|
if(team == undefined) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
team.currentLocation = location;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
sendLocation(teamId) {
|
||||||
|
if(!this.started) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let team = this.getTeam(teamId);
|
||||||
|
if(team == undefined) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
team.lastSentLocation = team.currentLocation;
|
||||||
|
team.enemyLocation = this.getTeam(team.chasing).lastSentLocation;
|
||||||
|
return team;
|
||||||
|
}
|
||||||
|
|
||||||
|
removeTeam(teamId) {
|
||||||
|
if(this.getTeam(teamId) == undefined) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
//remove the team from the list
|
||||||
|
this.teams = this.teams.filter(t => t.id !== teamId);
|
||||||
|
this.updateTeamChasing();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
186
traque-back/index.js
Normal file
186
traque-back/index.js
Normal file
@@ -0,0 +1,186 @@
|
|||||||
|
import { createServer } from "http";
|
||||||
|
import { Server } from "socket.io";
|
||||||
|
import Game from "./game.js";
|
||||||
|
|
||||||
|
const httpServer = createServer();
|
||||||
|
//Password that socket clients will have to send to be able to send admin commands
|
||||||
|
//TODO: put this in an environment variable
|
||||||
|
const ADMIN_PASSWORD = "admin";
|
||||||
|
|
||||||
|
//set cors to allow all origins
|
||||||
|
const io = new Server(httpServer, {
|
||||||
|
cors: {
|
||||||
|
origin: "*",
|
||||||
|
methods: ["GET", "POST"]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a message to all logged in sockets
|
||||||
|
* @param {String} event The event name
|
||||||
|
* @param {String} data The data to send
|
||||||
|
*/
|
||||||
|
function secureBroadcast(event, data) {
|
||||||
|
loggedInSockets.forEach(s => {
|
||||||
|
io.of("admin").to(s).emit(event, data);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const game = new Game();
|
||||||
|
|
||||||
|
//Array of logged in sockets
|
||||||
|
let loggedInSockets = [];
|
||||||
|
|
||||||
|
|
||||||
|
//Admin namespace
|
||||||
|
io.of("admin").on("connection", (socket) => {
|
||||||
|
//Flag to check if the user is logged in, defined for each socket
|
||||||
|
let loggedIn = false;
|
||||||
|
|
||||||
|
socket.on("disconnect", () => {
|
||||||
|
console.log("user disconnected");
|
||||||
|
//Remove the socket from the logged in sockets array
|
||||||
|
loggedInSockets = loggedInSockets.filter(s => s !== socket.id);
|
||||||
|
});
|
||||||
|
|
||||||
|
//User is attempting to log in
|
||||||
|
socket.on("login", (password) => {
|
||||||
|
if (password === ADMIN_PASSWORD && !loggedIn) {
|
||||||
|
//Attempt successful
|
||||||
|
socket.emit("login_response", true);
|
||||||
|
loggedInSockets.push(socket.id);
|
||||||
|
loggedIn = true;
|
||||||
|
} else {
|
||||||
|
//Attempt unsuccessful
|
||||||
|
socket.emit("login_response", false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//User is attempting to add a new team
|
||||||
|
socket.on("add_team", (teamName) => {
|
||||||
|
if(!loggedIn) {
|
||||||
|
socket.emit("error", "Not logged in");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(game.addTeam(teamName)) {
|
||||||
|
secureBroadcast("teams", game.teams);
|
||||||
|
}else {
|
||||||
|
socket.emit("error", "Error adding team");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//User is attempting to remove a team
|
||||||
|
socket.on("remove_team", (teamId) => {
|
||||||
|
if(!loggedIn) {
|
||||||
|
socket.emit("error", "Not logged in");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(game.removeTeam(teamId)) {
|
||||||
|
secureBroadcast("teams", game.teams);
|
||||||
|
}else {
|
||||||
|
socket.emit("error", "Error removing team");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//User is attempting to start the game
|
||||||
|
socket.on("start_game", () => {
|
||||||
|
if(!loggedIn) {
|
||||||
|
socket.emit("error", "Not logged in");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(game.start()) {
|
||||||
|
secureBroadcast("game_started", true);
|
||||||
|
}else {
|
||||||
|
socket.emit("error", "Error starting game");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//User is attempting to stop the game
|
||||||
|
socket.on("stop_game", () => {
|
||||||
|
if(!loggedIn) {
|
||||||
|
socket.emit("error", "Not logged in");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(game.stop()) {
|
||||||
|
secureBroadcast("game_started", false);
|
||||||
|
}else {
|
||||||
|
socket.emit("error", "Error stopping game");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//Use is sending a new list containing the new order of the teams
|
||||||
|
//Note that we never check if the new order contains the same teams as the old order, so it behaves more like a setTeams function
|
||||||
|
//But the frontend should always send the same teams in a different order
|
||||||
|
socket.on("reorder_teams", (newOrder) => {
|
||||||
|
if(!loggedIn) {
|
||||||
|
socket.emit("error", "Not logged in");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(game.reorderTeams(newOrder)) {
|
||||||
|
secureBroadcast("teams", game.teams);
|
||||||
|
} else {
|
||||||
|
socket.emit("error", "Error reordering teams");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//Change the name of a team given its id
|
||||||
|
socket.on("rename_team", (teamId, newName) => {
|
||||||
|
if(!loggedIn) {
|
||||||
|
socket.emit("error", "Not logged in");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(game.renameTeam(teamId, newName)) {
|
||||||
|
secureBroadcast("teams", game.teams);
|
||||||
|
} else {
|
||||||
|
socket.emit("error", "Error renaming team");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//Request an update of the team list
|
||||||
|
//We only reply to the sender to prevent spam
|
||||||
|
socket.on("get_teams", () => {
|
||||||
|
if(!loggedIn) {
|
||||||
|
socket.emit("error", "Not logged in");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
socket.emit("teams", game.teams);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
io.of("player").on("connection", (socket) => {
|
||||||
|
let teamId = null;
|
||||||
|
console.log("a user connected");
|
||||||
|
|
||||||
|
socket.on("disconnect", () => {
|
||||||
|
console.log("user disconnected");
|
||||||
|
game.getTeam(teamId).sockets = game.getTeam(teamId).sockets.filter(s => s !== socket.id);
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("login", (teamId) => {
|
||||||
|
if(game.getTeam(teamId) === undefined) {
|
||||||
|
socket.emit("login_response", false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
game.getTeam(teamId).sockets.push(socket.id);
|
||||||
|
socket.emit("login_response", true);
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("update_position", (position) => {
|
||||||
|
game.updateLocation(teamId, position);
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("send_position", () => {
|
||||||
|
console.log("send_position", position);
|
||||||
|
game.sendLocation(teamId);
|
||||||
|
game.getTeam(teamId).sockets.forEach(s => {
|
||||||
|
io.of("player").to(s).emit("enemy_position", game.getTeam(teamId).enemyLocation);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
httpServer.listen(3000);
|
||||||
237
traque-back/package-lock.json
generated
Normal file
237
traque-back/package-lock.json
generated
Normal file
@@ -0,0 +1,237 @@
|
|||||||
|
{
|
||||||
|
"name": "traque-back",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"name": "traque-back",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"socket.io": "^4.7.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@socket.io/component-emitter": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg=="
|
||||||
|
},
|
||||||
|
"node_modules/@types/cookie": {
|
||||||
|
"version": "0.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz",
|
||||||
|
"integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q=="
|
||||||
|
},
|
||||||
|
"node_modules/@types/cors": {
|
||||||
|
"version": "2.8.17",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz",
|
||||||
|
"integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/node": {
|
||||||
|
"version": "20.11.30",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.30.tgz",
|
||||||
|
"integrity": "sha512-dHM6ZxwlmuZaRmUPfv1p+KrdD1Dci04FbdEm/9wEMouFqxYoFl5aMkt0VMAUtYRQDyYvD41WJLukhq/ha3YuTw==",
|
||||||
|
"dependencies": {
|
||||||
|
"undici-types": "~5.26.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/accepts": {
|
||||||
|
"version": "1.3.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
|
||||||
|
"integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
|
||||||
|
"dependencies": {
|
||||||
|
"mime-types": "~2.1.34",
|
||||||
|
"negotiator": "0.6.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/base64id": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==",
|
||||||
|
"engines": {
|
||||||
|
"node": "^4.5.0 || >= 5.9"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/cookie": {
|
||||||
|
"version": "0.4.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz",
|
||||||
|
"integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/cors": {
|
||||||
|
"version": "2.8.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
|
||||||
|
"integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
|
||||||
|
"dependencies": {
|
||||||
|
"object-assign": "^4",
|
||||||
|
"vary": "^1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/debug": {
|
||||||
|
"version": "4.3.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
||||||
|
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"ms": "2.1.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"supports-color": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/engine.io": {
|
||||||
|
"version": "6.5.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.4.tgz",
|
||||||
|
"integrity": "sha512-KdVSDKhVKyOi+r5uEabrDLZw2qXStVvCsEB/LN3mw4WFi6Gx50jTyuxYVCwAAC0U46FdnzP/ScKRBTXb/NiEOg==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/cookie": "^0.4.1",
|
||||||
|
"@types/cors": "^2.8.12",
|
||||||
|
"@types/node": ">=10.0.0",
|
||||||
|
"accepts": "~1.3.4",
|
||||||
|
"base64id": "2.0.0",
|
||||||
|
"cookie": "~0.4.1",
|
||||||
|
"cors": "~2.8.5",
|
||||||
|
"debug": "~4.3.1",
|
||||||
|
"engine.io-parser": "~5.2.1",
|
||||||
|
"ws": "~8.11.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/engine.io-parser": {
|
||||||
|
"version": "5.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.2.tgz",
|
||||||
|
"integrity": "sha512-RcyUFKA93/CXH20l4SoVvzZfrSDMOTUS3bWVpTt2FuFP+XYrL8i8oonHP7WInRyVHXh0n/ORtoeiE1os+8qkSw==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/mime-db": {
|
||||||
|
"version": "1.52.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||||
|
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/mime-types": {
|
||||||
|
"version": "2.1.35",
|
||||||
|
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
||||||
|
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
||||||
|
"dependencies": {
|
||||||
|
"mime-db": "1.52.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/ms": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||||
|
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||||
|
},
|
||||||
|
"node_modules/negotiator": {
|
||||||
|
"version": "0.6.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
|
||||||
|
"integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/object-assign": {
|
||||||
|
"version": "4.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||||
|
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/socket.io": {
|
||||||
|
"version": "4.7.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.5.tgz",
|
||||||
|
"integrity": "sha512-DmeAkF6cwM9jSfmp6Dr/5/mfMwb5Z5qRrSXLpo3Fq5SqyU8CMF15jIN4ZhfSwu35ksM1qmHZDQ/DK5XTccSTvA==",
|
||||||
|
"dependencies": {
|
||||||
|
"accepts": "~1.3.4",
|
||||||
|
"base64id": "~2.0.0",
|
||||||
|
"cors": "~2.8.5",
|
||||||
|
"debug": "~4.3.2",
|
||||||
|
"engine.io": "~6.5.2",
|
||||||
|
"socket.io-adapter": "~2.5.2",
|
||||||
|
"socket.io-parser": "~4.2.4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/socket.io-adapter": {
|
||||||
|
"version": "2.5.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.4.tgz",
|
||||||
|
"integrity": "sha512-wDNHGXGewWAjQPt3pyeYBtpWSq9cLE5UW1ZUPL/2eGK9jtse/FpXib7epSTsz0Q0m+6sg6Y4KtcFTlah1bdOVg==",
|
||||||
|
"dependencies": {
|
||||||
|
"debug": "~4.3.4",
|
||||||
|
"ws": "~8.11.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/socket.io-parser": {
|
||||||
|
"version": "4.2.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz",
|
||||||
|
"integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==",
|
||||||
|
"dependencies": {
|
||||||
|
"@socket.io/component-emitter": "~3.1.0",
|
||||||
|
"debug": "~4.3.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/undici-types": {
|
||||||
|
"version": "5.26.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
|
||||||
|
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="
|
||||||
|
},
|
||||||
|
"node_modules/vary": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/ws": {
|
||||||
|
"version": "8.11.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz",
|
||||||
|
"integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"bufferutil": "^4.0.1",
|
||||||
|
"utf-8-validate": "^5.0.2"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"bufferutil": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"utf-8-validate": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,8 +4,13 @@
|
|||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "echo \"Error: no test specified\" && exit 1"
|
"test": "echo \"Error: no test specified\" && exit 1",
|
||||||
|
"start": "node index.js"
|
||||||
},
|
},
|
||||||
"author": "Quentin Roussel",
|
"author": "Quentin Roussel",
|
||||||
"license": "ISC"
|
"license": "ISC",
|
||||||
|
"type": "module",
|
||||||
|
"dependencies": {
|
||||||
|
"socket.io": "^4.7.5"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
12
traque-front/app/admin/layout.js
Normal file
12
traque-front/app/admin/layout.js
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import { AdminConnexionProvider } from "@/context/adminConnexionContext";
|
||||||
|
import { AdminProvider } from "@/context/adminContext";
|
||||||
|
|
||||||
|
export default function AdminLayout({ children}) {
|
||||||
|
return (
|
||||||
|
<AdminConnexionProvider>
|
||||||
|
<AdminProvider>
|
||||||
|
{children}
|
||||||
|
</AdminProvider>
|
||||||
|
</AdminConnexionProvider>
|
||||||
|
)
|
||||||
|
}
|
||||||
17
traque-front/app/admin/login/page.js
Normal file
17
traque-front/app/admin/login/page.js
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
"use client";
|
||||||
|
import LoginForm from '@/components/team/loginForm'
|
||||||
|
import { useAdminConnexion } from '@/context/adminConnexionContext';
|
||||||
|
import { redirect } from 'next/navigation';
|
||||||
|
import React, { useEffect } from 'react'
|
||||||
|
|
||||||
|
export default function AdminLoginPage() {
|
||||||
|
const { login, loggedIn } = useAdminConnexion();
|
||||||
|
useEffect(() => {
|
||||||
|
if (loggedIn) {
|
||||||
|
redirect("/admin");
|
||||||
|
}
|
||||||
|
}, [loggedIn]);
|
||||||
|
return (
|
||||||
|
<LoginForm title="Admin login" placeholder="Admin password" buttonText={"Login"} onSubmit={login} />
|
||||||
|
)
|
||||||
|
}
|
||||||
36
traque-front/app/admin/page.js
Normal file
36
traque-front/app/admin/page.js
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
"use client";
|
||||||
|
import TeamAddForm from '@/components/admin/teamAdd';
|
||||||
|
import TeamEdit from '@/components/admin/teamEdit';
|
||||||
|
import TeamList from '@/components/admin/teamList';
|
||||||
|
import { useAdminConnexion } from '@/context/adminConnexionContext';
|
||||||
|
import useAdmin from '@/hook/useAdmin';
|
||||||
|
import { redirect } from 'next/navigation';
|
||||||
|
import React, { useEffect, useState } from 'react'
|
||||||
|
|
||||||
|
|
||||||
|
export default function Admin() {
|
||||||
|
const [selectedTeamId, setSelectedTeamId] = useState(null);
|
||||||
|
const { loggedIn } = useAdminConnexion();
|
||||||
|
const { addTeam } = useAdmin();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!loggedIn) {
|
||||||
|
redirect("/admin/login");
|
||||||
|
}
|
||||||
|
}, [loggedIn]);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='h-full p-10 flex flex-row justify-between'>
|
||||||
|
<div className='w-5/12 h-full p-4 shadow-md rounded outline'>
|
||||||
|
<h2 className='text-2xl text-center'>Team list</h2>
|
||||||
|
<TeamAddForm onAddTeam={addTeam}/>
|
||||||
|
<TeamList selectedTeamId={selectedTeamId} onSelected={setSelectedTeamId}/>
|
||||||
|
</div>
|
||||||
|
<div className='w-5/12 h-full p-4 shadow-md rounded outline'>
|
||||||
|
<TeamEdit selectedTeamId={selectedTeamId} setSelectedTeamId={setSelectedTeamId}/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import { Inter } from "next/font/google";
|
import { Inter } from "next/font/google";
|
||||||
import "./globals.css";
|
import "./globals.css";
|
||||||
|
import SocketProvider from "@/context/socketContext";
|
||||||
|
|
||||||
const inter = Inter({ subsets: ["latin"] });
|
const inter = Inter({ subsets: ["latin"] });
|
||||||
|
|
||||||
@@ -10,7 +11,9 @@ export const metadata = {
|
|||||||
export default function RootLayout({ children }) {
|
export default function RootLayout({ children }) {
|
||||||
return (
|
return (
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<body className={inter.className + " h-screen"}>{children}</body>
|
<SocketProvider>
|
||||||
|
<body className={inter.className + " h-screen"}>{children}</body>
|
||||||
|
</SocketProvider>
|
||||||
</html>
|
</html>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
|
"use client"
|
||||||
import LoginForm from "@/components/team/loginForm";
|
import LoginForm from "@/components/team/loginForm";
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
|
function login(teamId) {
|
||||||
|
console.log(teamId);
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<LoginForm />
|
<LoginForm title={"Team login"} placeholder={"team ID"} buttonText={"Login"} onSubmit={login}/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
"use client";
|
"use client";
|
||||||
import Button from '@/components/util/button';
|
import Button from '@/components/util/button';
|
||||||
import dynamic from 'next/dynamic';
|
import dynamic from 'next/dynamic';
|
||||||
import React, { useEffect, useState } from 'react'
|
import React, { useEffect } from 'react'
|
||||||
|
import useGame from '../../hook/useGame';
|
||||||
|
|
||||||
//Load the map without SSR
|
//Load the map without SSR
|
||||||
const LiveMap = dynamic(() => import('@/components/team/map'), {
|
const LiveMap = dynamic(() => import('@/components/team/map'), {
|
||||||
@@ -9,27 +10,13 @@ const LiveMap = dynamic(() => import('@/components/team/map'), {
|
|||||||
});
|
});
|
||||||
|
|
||||||
export default function Track() {
|
export default function Track() {
|
||||||
const [currentPosition, setCurrentPosition] = useState([0,0]);
|
const { currentPosition, enemyPosition, updateCurrentPosition, sendCurrentPosition } = useGame();
|
||||||
const [enemyPosition, setEnemyPosition] = useState([0,0]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const t = setTimeout(() => {
|
|
||||||
setEnemyPosition([currentPosition[0] + Math.random() / 100, currentPosition[1] + Math.random() / 100]);
|
|
||||||
}, 1000);
|
|
||||||
return () => clearInterval(t);
|
|
||||||
}, [currentPosition]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
navigator.geolocation.watchPosition((position) => {
|
|
||||||
setCurrentPosition([position.coords.latitude, position.coords.longitude]);
|
|
||||||
});
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='h-full flex flex-col justify-between'>
|
<div className='h-full flex flex-col justify-between'>
|
||||||
<LiveMap currentPosition={currentPosition} enemyPosition={enemyPosition} className="h-4/5" />
|
<LiveMap currentPosition={currentPosition} enemyPosition={enemyPosition} className="h-4/5" />
|
||||||
<Button>Update position</Button>
|
<Button onClick={sendCurrentPosition}>Update position</Button>
|
||||||
<div className='shadow-lg m-5 p-2 flex flex-col text-center mx-auto w-4/5 rounded'>
|
<div className='shadow-lg m-5 p-2 flex flex-col text-center mx-auto w-4/5 rounded'>
|
||||||
<p className='text-xl text-black'>30min</p>
|
<p className='text-xl text-black'>30min</p>
|
||||||
<p className='text-gray-700'> before penalty</p>
|
<p className='text-gray-700'> before penalty</p>
|
||||||
|
|||||||
22
traque-front/components/admin/teamAdd.jsx
Normal file
22
traque-front/components/admin/teamAdd.jsx
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import TextInput from '../util/textInput'
|
||||||
|
import Button from '../util/button'
|
||||||
|
|
||||||
|
export default function TeamAddForm({onAddTeam}) {
|
||||||
|
const [teamName, setTeamName] = React.useState('');
|
||||||
|
function handleSubmit(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
onAddTeam(teamName);
|
||||||
|
setTeamName("")
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<form className='flex flex-row m-y-5' onSubmit={handleSubmit}>
|
||||||
|
<div className='w-4/5'>
|
||||||
|
<TextInput name="teamName" label='Team name' value={teamName} onChange={(e) => setTeamName(e.target.value)}/>
|
||||||
|
</div>
|
||||||
|
<div className='w-1/5'>
|
||||||
|
<Button type="submit" className="w-5">+</Button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
)
|
||||||
|
}
|
||||||
62
traque-front/components/admin/teamEdit.jsx
Normal file
62
traque-front/components/admin/teamEdit.jsx
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
import React, { useEffect, useState } from 'react'
|
||||||
|
import TextInput from '../util/textInput'
|
||||||
|
import Button from '../util/button';
|
||||||
|
import useAdmin from '@/hook/useAdmin';
|
||||||
|
|
||||||
|
export default function TeamEdit({selectedTeamId, setSelectedTeamId}) {
|
||||||
|
const [newTeamName, setNewTeamName] = React.useState('');
|
||||||
|
const {setTeamName, getTeamName, removeTeam, getTeam} = useAdmin();
|
||||||
|
const [team, setTeam] = useState({})
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let team = getTeam(selectedTeamId);
|
||||||
|
if (team != undefined) {
|
||||||
|
setNewTeamName(team.name);
|
||||||
|
}
|
||||||
|
},[selectedTeamId])
|
||||||
|
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let team = getTeam(selectedTeamId);
|
||||||
|
if (team != undefined) {
|
||||||
|
setTeam(team);
|
||||||
|
}
|
||||||
|
}, [selectedTeamId])
|
||||||
|
|
||||||
|
|
||||||
|
function handleSubmit(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
setTeamName(team.id, newTeamName);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleRemove() {
|
||||||
|
removeTeam(team.id);
|
||||||
|
setSelectedTeamId(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (team &&
|
||||||
|
<div className='flex flex-row'>
|
||||||
|
<div className='w-1/2 flex flex-col space-y-3 mx-2'>
|
||||||
|
<h2 className='text-2xl text-center'>Actions</h2>
|
||||||
|
<form className='flex flex-row' onSubmit={handleSubmit}>
|
||||||
|
<div className='w-4/5'>
|
||||||
|
<TextInput name="teamName" label='Team name' value={newTeamName} onChange={(e) => setNewTeamName(e.target.value)}/>
|
||||||
|
</div>
|
||||||
|
<div className='w-2/5'>
|
||||||
|
<Button type="submit">Rename</Button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<Button onClick={handleRemove}>Eliminate</Button>
|
||||||
|
</div>
|
||||||
|
<div className='w-1/2 flex flex-col space-y-2 mx-2'>
|
||||||
|
<h2 className='text-2xl text-center'>Team details</h2>
|
||||||
|
<div>
|
||||||
|
<p>Secret : {team.id}</p>
|
||||||
|
<p>Name : {team.name}</p>
|
||||||
|
<p>Chasing : {getTeamName(team.chasing)}</p>
|
||||||
|
<p>Chased by : {getTeamName(team.chased)}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
63
traque-front/components/admin/teamList.jsx
Normal file
63
traque-front/components/admin/teamList.jsx
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
"use client";
|
||||||
|
import useAdmin from '@/hook/useAdmin';
|
||||||
|
import { DragDropContext, Draggable, Droppable } from '@hello-pangea/dnd';
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
const reorder = (list, startIndex, endIndex) => {
|
||||||
|
const result = Array.from(list);
|
||||||
|
const [removed] = result.splice(startIndex, 1);
|
||||||
|
result.splice(endIndex, 0, removed);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
function TeamListItem({ team, index, onSelected, itemSelected }) {
|
||||||
|
const classNames = 'w-full p-3 m-3 shadow ' + (itemSelected ? "bg-blue-400" : "bg-gray-100");
|
||||||
|
return (
|
||||||
|
<Draggable draggableId={team.id.toString()} index={index} onClick={() => onSelected(team.id)}>
|
||||||
|
{provided => (
|
||||||
|
<div className={classNames} {...provided.draggableProps} {...provided.dragHandleProps} ref={provided.innerRef}>
|
||||||
|
<p className='text-center'>{team.name}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
)}
|
||||||
|
</Draggable>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function TeamList({selectedTeamId, onSelected}) {
|
||||||
|
const {teams, reorderTeams} = useAdmin();
|
||||||
|
function onDragEnd(result) {
|
||||||
|
if (!result.destination) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.destination.index === result.source.index) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const newTeams = reorder(
|
||||||
|
teams,
|
||||||
|
result.source.index,
|
||||||
|
result.destination.index
|
||||||
|
);
|
||||||
|
|
||||||
|
reorderTeams(newTeams);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<DragDropContext onDragEnd={onDragEnd} >
|
||||||
|
<Droppable droppableId='team-list'>
|
||||||
|
{provided => (
|
||||||
|
<ul ref={provided.innerRef} {...provided.droppableProps}>
|
||||||
|
{teams.map((team, i) => (
|
||||||
|
<li key={team.id} onClick={() => onSelected(team.id)}>
|
||||||
|
<TeamListItem onSelected={onSelected} index={i} itemSelected={selectedTeamId === team.id} team={team} />
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
{provided.placeholder}
|
||||||
|
</ul>
|
||||||
|
)}
|
||||||
|
</Droppable>
|
||||||
|
</DragDropContext>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,12 +1,20 @@
|
|||||||
|
"use client";
|
||||||
|
import { useState } from "react";
|
||||||
import Button from "../util/button";
|
import Button from "../util/button";
|
||||||
import TextInput from "../util/textInput";
|
import TextInput from "../util/textInput";
|
||||||
|
|
||||||
export default function LoginForm() {
|
export default function LoginForm({ onSubmit, title, placeholder, buttonText}) {
|
||||||
|
const [value, setValue] = useState("");
|
||||||
|
function handleSubmit(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
setValue("");
|
||||||
|
onSubmit(value);
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<form className="bg-white shadow-md max-w mx-auto p-5 mx-10 flex flex-col space-y-4">
|
<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">Connexion équipe</h1>
|
<h1 className="text-2xl font-bold text-center text-gray-700">{title}</h1>
|
||||||
<TextInput placeholder="Code d'équipe" name="team-id" />
|
<TextInput placeholder={placeholder} value={value} onChange={(e) => setValue(e.target.value)} name="team-id"/>
|
||||||
<Button type="submit">Se connecter</Button>
|
<Button type="submit">{buttonText}</Button>
|
||||||
</form>
|
</form>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -22,13 +22,28 @@ function MapPan(props) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function LiveMap({enemyPosition, currentPosition, ...props}) {
|
export default function LiveMap({enemyPosition, currentPosition, ...props}) {
|
||||||
|
const [positionSet, setPositionSet] = useState(false);
|
||||||
|
useEffect(() => {
|
||||||
|
if(!positionSet && JSON.stringify(currentPosition) != "[0,0]") {
|
||||||
|
setPositionSet(true);
|
||||||
|
}
|
||||||
|
}, [currentPosition]);
|
||||||
|
const [enemyPositionSet, setEnemyPositionSet] = useState(false);
|
||||||
|
useEffect(() => {
|
||||||
|
if(!enemyPositionSet && JSON.stringify(enemyPosition) != "[0,0]") {
|
||||||
|
setEnemyPositionSet(true);
|
||||||
|
}
|
||||||
|
}, [enemyPosition]);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MapContainer {...props} center={[0,0]} zoom={0} scrollWheelZoom={true}>
|
<MapContainer {...props} center={[0,0]} zoom={0} 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"
|
||||||
/>
|
/>
|
||||||
<Marker position={currentPosition} icon={new L.Icon({
|
{positionSet && <Marker position={currentPosition} icon={new L.Icon({
|
||||||
iconUrl: '/icons/location.png',
|
iconUrl: '/icons/location.png',
|
||||||
iconSize: [41, 41],
|
iconSize: [41, 41],
|
||||||
iconAnchor: [12, 41],
|
iconAnchor: [12, 41],
|
||||||
@@ -38,8 +53,8 @@ export default function LiveMap({enemyPosition, currentPosition, ...props}) {
|
|||||||
<Popup>
|
<Popup>
|
||||||
Votre position
|
Votre position
|
||||||
</Popup>
|
</Popup>
|
||||||
</Marker>
|
</Marker>}
|
||||||
<Marker position={enemyPosition} icon={new L.Icon({
|
{enemyPositionSet && <Marker position={enemyPosition} icon={new L.Icon({
|
||||||
iconUrl: '/icons/target.png',
|
iconUrl: '/icons/target.png',
|
||||||
iconSize: [41, 41],
|
iconSize: [41, 41],
|
||||||
iconAnchor: [12, 41],
|
iconAnchor: [12, 41],
|
||||||
@@ -49,7 +64,7 @@ export default function LiveMap({enemyPosition, currentPosition, ...props}) {
|
|||||||
<Popup>
|
<Popup>
|
||||||
Position de l'ennemi
|
Position de l'ennemi
|
||||||
</Popup>
|
</Popup>
|
||||||
</Marker>
|
</Marker>}
|
||||||
<MapPan center={currentPosition}/>
|
<MapPan center={currentPosition}/>
|
||||||
</MapContainer>
|
</MapContainer>
|
||||||
)
|
)
|
||||||
|
|||||||
37
traque-front/context/adminConnexionContext.jsx
Normal file
37
traque-front/context/adminConnexionContext.jsx
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
"use client";
|
||||||
|
import { createContext, useContext, useEffect, useState } from "react";
|
||||||
|
import { useSocket } from "./socketContext";
|
||||||
|
|
||||||
|
const adminContext = createContext();
|
||||||
|
const AdminConnexionProvider = ({ children }) => {
|
||||||
|
const [loggedIn, setLoggedIn] = useState(false);
|
||||||
|
const { adminSocket } = useSocket();
|
||||||
|
|
||||||
|
function login(password) {
|
||||||
|
adminSocket.emit("login", password);
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
function updateLoginStatus(status) {
|
||||||
|
setLoggedIn(status);
|
||||||
|
}
|
||||||
|
adminSocket.on("login_response", updateLoginStatus);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
adminSocket.off("login_response", updateLoginStatus);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<adminContext.Provider value={{ login, loggedIn }}>
|
||||||
|
{children}
|
||||||
|
</adminContext.Provider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function useAdminConnexion() {
|
||||||
|
return useContext(adminContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
export { AdminConnexionProvider, useAdminConnexion};
|
||||||
|
|
||||||
20
traque-front/context/adminContext.jsx
Normal file
20
traque-front/context/adminContext.jsx
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
"use client";
|
||||||
|
import { createContext, useContext, useState } from "react";
|
||||||
|
|
||||||
|
const adminContext = createContext();
|
||||||
|
|
||||||
|
function AdminProvider({children}) {
|
||||||
|
const [teams, setTeams] = useState([]);
|
||||||
|
const [started, setStarted] = useState(false);
|
||||||
|
return (
|
||||||
|
<adminContext.Provider value={{teams, setTeams, started, setStarted}}>
|
||||||
|
{children}
|
||||||
|
</adminContext.Provider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function useAdminContext() {
|
||||||
|
return useContext(adminContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
export { AdminProvider, useAdminContext };
|
||||||
22
traque-front/context/socketContext.jsx
Normal file
22
traque-front/context/socketContext.jsx
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
"use client";
|
||||||
|
import { createContext, useContext } from "react";
|
||||||
|
|
||||||
|
const { io } = require("socket.io-client");
|
||||||
|
|
||||||
|
const SOCKET_URL = "http://localhost:3000";
|
||||||
|
const USER_SOCKET_URL = SOCKET_URL + "/user";
|
||||||
|
const ADMIN_SOCKET_URL = SOCKET_URL + "/admin";
|
||||||
|
|
||||||
|
export const userSocket = io(USER_SOCKET_URL);
|
||||||
|
export const adminSocket = io(ADMIN_SOCKET_URL);
|
||||||
|
export const SocketContext = createContext();
|
||||||
|
|
||||||
|
export default function SocketProvider({ children }) {
|
||||||
|
return (
|
||||||
|
<SocketContext.Provider value={{userSocket, adminSocket}}>{children}</SocketContext.Provider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useSocket() {
|
||||||
|
return useContext(SocketContext);
|
||||||
|
}
|
||||||
68
traque-front/hook/useAdmin.jsx
Normal file
68
traque-front/hook/useAdmin.jsx
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
import { useAdminContext } from "@/context/adminContext";
|
||||||
|
import { useSocket } from "@/context/socketContext";
|
||||||
|
import { Underdog } from "next/font/google";
|
||||||
|
|
||||||
|
const { useEffect, useState } = require("react");
|
||||||
|
|
||||||
|
export default function useAdmin(){
|
||||||
|
const {teams, setTeams, started, setStarted} = useAdminContext();
|
||||||
|
const {adminSocket} = useSocket();
|
||||||
|
|
||||||
|
function pollTeams() {
|
||||||
|
adminSocket.emit("get_teams");
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
pollTeams();
|
||||||
|
}, []);
|
||||||
|
useEffect(() => {
|
||||||
|
adminSocket.emit("get_teams");
|
||||||
|
adminSocket.on("teams", setTeams);
|
||||||
|
return () => {
|
||||||
|
adminSocket.off("teams", setTeams);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
function getTeam(teamId) {
|
||||||
|
return teams.find(team => team.id === teamId);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTeamName(teamId) {
|
||||||
|
let team = getTeam(teamId);
|
||||||
|
return team ? team.name : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
function reorderTeams(newOrder) {
|
||||||
|
adminSocket.emit("reorder_teams", newOrder);
|
||||||
|
}
|
||||||
|
|
||||||
|
function addTeam(teamName) {
|
||||||
|
adminSocket.emit("add_team", teamName);
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeTeam(teamId) {
|
||||||
|
adminSocket.emit("remove_team", teamId);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setTeamName(teamId, newName) {
|
||||||
|
adminSocket.emit("rename_team", teamId, newName);
|
||||||
|
}
|
||||||
|
|
||||||
|
function startGame() {
|
||||||
|
adminSocket.emit("start_game");
|
||||||
|
}
|
||||||
|
|
||||||
|
function stopGame() {
|
||||||
|
adminSocket.emit("stop_game");
|
||||||
|
}
|
||||||
|
|
||||||
|
useState(() => {
|
||||||
|
adminSocket.on("game_started", setStarted);
|
||||||
|
return () => {
|
||||||
|
adminSocket.off("game_started", setStarted);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return { teams, started, pollTeams, getTeam, getTeamName, reorderTeams, addTeam, removeTeam, startGame, stopGame, setTeamName };
|
||||||
|
|
||||||
|
}
|
||||||
61
traque-front/hook/useGame.jsx
Normal file
61
traque-front/hook/useGame.jsx
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useSocket } from "@/context/socketContext";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
|
export default function useGame() {
|
||||||
|
const {userSocket} = useSocket();
|
||||||
|
const [loggedIn, setLoggedIn] = useState(false);
|
||||||
|
const [teamId, setTeamId] = useState(null);
|
||||||
|
const [enemyPosition, setEnemyPosition] = useState([0, 0]);
|
||||||
|
const [currentPosition, setCurrentPosition] = useState([0, 0]);
|
||||||
|
|
||||||
|
function updateCurrentPosition(position) {
|
||||||
|
setCurrentPosition(position);
|
||||||
|
userSocket.emit("update_position", position);
|
||||||
|
}
|
||||||
|
|
||||||
|
function sendCurrentPosition() {
|
||||||
|
userSocket.emit("send_position", currentPosition);
|
||||||
|
}
|
||||||
|
useEffect(() => {
|
||||||
|
function updateEnemyPosition(position) {
|
||||||
|
setEnemyPosition(position);
|
||||||
|
}
|
||||||
|
userSocket.on("enemy_position", updateEnemyPosition);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
userSocket.off("enemy_position", updateEnemyPosition);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
function login(teamId) {
|
||||||
|
setTeamId(teamId);
|
||||||
|
userSocket.emit("login", teamId);
|
||||||
|
}
|
||||||
|
useEffect(() => {
|
||||||
|
function updateLoginStatus(status) {
|
||||||
|
setLoggedIn(status);
|
||||||
|
}
|
||||||
|
userSocket.on("login_reponse", updateLoginStatus);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
userSocket.off("login_response", updateLoginStatus);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
function udpate() {
|
||||||
|
console.log("update")
|
||||||
|
const position = navigator.geolocation.getCurrentPosition((position) => {
|
||||||
|
updateCurrentPosition([position.coords.latitude, position.coords.longitude]);
|
||||||
|
}, () => { }, { enableHighAccuracy: true, timeout: Infinity, maximumAge: 0 });
|
||||||
|
}
|
||||||
|
setInterval(udpate, 1000);
|
||||||
|
return () => {
|
||||||
|
clearInterval(udpate);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return { updateCurrentPosition, sendCurrentPosition, login, enemyPosition, currentPosition, loggedIn, teamId };
|
||||||
|
}
|
||||||
249
traque-front/package-lock.json
generated
249
traque-front/package-lock.json
generated
@@ -8,11 +8,13 @@
|
|||||||
"name": "traque-front",
|
"name": "traque-front",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@hello-pangea/dnd": "^16.6.0",
|
||||||
"leaflet-defaulticon-compatibility": "^0.1.2",
|
"leaflet-defaulticon-compatibility": "^0.1.2",
|
||||||
"next": "14.1.4",
|
"next": "14.1.4",
|
||||||
"react": "^18",
|
"react": "^18",
|
||||||
"react-dom": "^18",
|
"react-dom": "^18",
|
||||||
"react-leaflet": "^4.2.1"
|
"react-leaflet": "^4.2.1",
|
||||||
|
"socket.io-client": "^4.7.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"autoprefixer": "^10.0.1",
|
"autoprefixer": "^10.0.1",
|
||||||
@@ -47,7 +49,6 @@
|
|||||||
"version": "7.24.1",
|
"version": "7.24.1",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.1.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.1.tgz",
|
||||||
"integrity": "sha512-+BIznRzyqBf+2wCTxcKE3wDjfGeCoVE61KSHGpkzqrLi8qxqFwBeUFyId2cxkTmm55fzDGnm0+yCxaxygrLUnQ==",
|
"integrity": "sha512-+BIznRzyqBf+2wCTxcKE3wDjfGeCoVE61KSHGpkzqrLi8qxqFwBeUFyId2cxkTmm55fzDGnm0+yCxaxygrLUnQ==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"regenerator-runtime": "^0.14.0"
|
"regenerator-runtime": "^0.14.0"
|
||||||
},
|
},
|
||||||
@@ -111,6 +112,72 @@
|
|||||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@hello-pangea/dnd": {
|
||||||
|
"version": "16.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@hello-pangea/dnd/-/dnd-16.6.0.tgz",
|
||||||
|
"integrity": "sha512-vfZ4GydqbtUPXSLfAvKvXQ6xwRzIjUSjVU0Sx+70VOhc2xx6CdmJXJ8YhH70RpbTUGjxctslQTHul9sIOxCfFQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.24.1",
|
||||||
|
"css-box-model": "^1.2.1",
|
||||||
|
"memoize-one": "^6.0.0",
|
||||||
|
"raf-schd": "^4.0.3",
|
||||||
|
"react-redux": "^8.1.3",
|
||||||
|
"redux": "^4.2.1",
|
||||||
|
"use-memo-one": "^1.1.3"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.8.5 || ^17.0.0 || ^18.0.0",
|
||||||
|
"react-dom": "^16.8.5 || ^17.0.0 || ^18.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@hello-pangea/dnd/node_modules/memoize-one": {
|
||||||
|
"version": "6.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz",
|
||||||
|
"integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw=="
|
||||||
|
},
|
||||||
|
"node_modules/@hello-pangea/dnd/node_modules/react-is": {
|
||||||
|
"version": "18.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz",
|
||||||
|
"integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w=="
|
||||||
|
},
|
||||||
|
"node_modules/@hello-pangea/dnd/node_modules/react-redux": {
|
||||||
|
"version": "8.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.1.3.tgz",
|
||||||
|
"integrity": "sha512-n0ZrutD7DaX/j9VscF+uTALI3oUPa/pO4Z3soOBIjuRn/FzVu6aehhysxZCLi6y7duMf52WNZGMl7CtuK5EnRw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.12.1",
|
||||||
|
"@types/hoist-non-react-statics": "^3.3.1",
|
||||||
|
"@types/use-sync-external-store": "^0.0.3",
|
||||||
|
"hoist-non-react-statics": "^3.3.2",
|
||||||
|
"react-is": "^18.0.0",
|
||||||
|
"use-sync-external-store": "^1.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "^16.8 || ^17.0 || ^18.0",
|
||||||
|
"@types/react-dom": "^16.8 || ^17.0 || ^18.0",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0",
|
||||||
|
"react-native": ">=0.59",
|
||||||
|
"redux": "^4 || ^5.0.0-beta.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react-dom": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"react-dom": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"react-native": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"redux": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@humanwhocodes/config-array": {
|
"node_modules/@humanwhocodes/config-array": {
|
||||||
"version": "0.11.14",
|
"version": "0.11.14",
|
||||||
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz",
|
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz",
|
||||||
@@ -446,6 +513,11 @@
|
|||||||
"integrity": "sha512-0HejFckBN2W+ucM6cUOlwsByTKt9/+0tWhqUffNIcHqCXkthY/mZ7AuYPK/2IIaGWhdl0h+tICDO0ssLMd6XMQ==",
|
"integrity": "sha512-0HejFckBN2W+ucM6cUOlwsByTKt9/+0tWhqUffNIcHqCXkthY/mZ7AuYPK/2IIaGWhdl0h+tICDO0ssLMd6XMQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/@socket.io/component-emitter": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg=="
|
||||||
|
},
|
||||||
"node_modules/@swc/helpers": {
|
"node_modules/@swc/helpers": {
|
||||||
"version": "0.5.2",
|
"version": "0.5.2",
|
||||||
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.2.tgz",
|
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.2.tgz",
|
||||||
@@ -454,12 +526,46 @@
|
|||||||
"tslib": "^2.4.0"
|
"tslib": "^2.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/hoist-non-react-statics": {
|
||||||
|
"version": "3.3.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.5.tgz",
|
||||||
|
"integrity": "sha512-SbcrWzkKBw2cdwRTwQAswfpB9g9LJWfjtUeW/jvNwbhC8cpmmNYVePa+ncbUe0rGTQ7G3Ff6mYUN2VMfLVr+Sg==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"hoist-non-react-statics": "^3.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/json5": {
|
"node_modules/@types/json5": {
|
||||||
"version": "0.0.29",
|
"version": "0.0.29",
|
||||||
"resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
|
"resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
|
||||||
"integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==",
|
"integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/prop-types": {
|
||||||
|
"version": "15.7.12",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz",
|
||||||
|
"integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q=="
|
||||||
|
},
|
||||||
|
"node_modules/@types/react": {
|
||||||
|
"version": "18.2.69",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.69.tgz",
|
||||||
|
"integrity": "sha512-W1HOMUWY/1Yyw0ba5TkCV+oqynRjG7BnteBB+B7JmAK7iw3l2SW+VGOxL+akPweix6jk2NNJtyJKpn4TkpfK3Q==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/prop-types": "*",
|
||||||
|
"@types/scheduler": "*",
|
||||||
|
"csstype": "^3.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/scheduler": {
|
||||||
|
"version": "0.16.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.8.tgz",
|
||||||
|
"integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A=="
|
||||||
|
},
|
||||||
|
"node_modules/@types/use-sync-external-store": {
|
||||||
|
"version": "0.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz",
|
||||||
|
"integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA=="
|
||||||
|
},
|
||||||
"node_modules/@typescript-eslint/parser": {
|
"node_modules/@typescript-eslint/parser": {
|
||||||
"version": "6.21.0",
|
"version": "6.21.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz",
|
||||||
@@ -1181,6 +1287,14 @@
|
|||||||
"node": ">= 8"
|
"node": ">= 8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/css-box-model": {
|
||||||
|
"version": "1.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/css-box-model/-/css-box-model-1.2.1.tgz",
|
||||||
|
"integrity": "sha512-a7Vr4Q/kd/aw96bnJG332W9V9LkJO69JRcaCYDUqjp6/z0w6VcZjgAcTbgFxEPfBgdnAwlh3iwu+hLopa+flJw==",
|
||||||
|
"dependencies": {
|
||||||
|
"tiny-invariant": "^1.0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/cssesc": {
|
"node_modules/cssesc": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
|
||||||
@@ -1193,6 +1307,11 @@
|
|||||||
"node": ">=4"
|
"node": ">=4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/csstype": {
|
||||||
|
"version": "3.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
|
||||||
|
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
|
||||||
|
},
|
||||||
"node_modules/damerau-levenshtein": {
|
"node_modules/damerau-levenshtein": {
|
||||||
"version": "1.0.8",
|
"version": "1.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz",
|
"resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz",
|
||||||
@@ -1254,7 +1373,6 @@
|
|||||||
"version": "4.3.4",
|
"version": "4.3.4",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
||||||
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
|
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ms": "2.1.2"
|
"ms": "2.1.2"
|
||||||
},
|
},
|
||||||
@@ -1370,6 +1488,26 @@
|
|||||||
"integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
|
"integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/engine.io-client": {
|
||||||
|
"version": "6.5.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.5.3.tgz",
|
||||||
|
"integrity": "sha512-9Z0qLB0NIisTRt1DZ/8U2k12RJn8yls/nXMZLn+/N8hANT3TcYjKFKcwbw5zFQiN4NTde3TSY9zb79e1ij6j9Q==",
|
||||||
|
"dependencies": {
|
||||||
|
"@socket.io/component-emitter": "~3.1.0",
|
||||||
|
"debug": "~4.3.1",
|
||||||
|
"engine.io-parser": "~5.2.1",
|
||||||
|
"ws": "~8.11.0",
|
||||||
|
"xmlhttprequest-ssl": "~2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/engine.io-parser": {
|
||||||
|
"version": "5.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.2.tgz",
|
||||||
|
"integrity": "sha512-RcyUFKA93/CXH20l4SoVvzZfrSDMOTUS3bWVpTt2FuFP+XYrL8i8oonHP7WInRyVHXh0n/ORtoeiE1os+8qkSw==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/enhanced-resolve": {
|
"node_modules/enhanced-resolve": {
|
||||||
"version": "5.16.0",
|
"version": "5.16.0",
|
||||||
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.16.0.tgz",
|
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.16.0.tgz",
|
||||||
@@ -2443,6 +2581,14 @@
|
|||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/hoist-non-react-statics": {
|
||||||
|
"version": "3.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
|
||||||
|
"integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
|
||||||
|
"dependencies": {
|
||||||
|
"react-is": "^16.7.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ignore": {
|
"node_modules/ignore": {
|
||||||
"version": "5.3.1",
|
"version": "5.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz",
|
||||||
@@ -3150,8 +3296,7 @@
|
|||||||
"node_modules/ms": {
|
"node_modules/ms": {
|
||||||
"version": "2.1.2",
|
"version": "2.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
|
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/mz": {
|
"node_modules/mz": {
|
||||||
"version": "2.7.0",
|
"version": "2.7.0",
|
||||||
@@ -3785,6 +3930,11 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"node_modules/raf-schd": {
|
||||||
|
"version": "4.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/raf-schd/-/raf-schd-4.0.3.tgz",
|
||||||
|
"integrity": "sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ=="
|
||||||
|
},
|
||||||
"node_modules/react": {
|
"node_modules/react": {
|
||||||
"version": "18.2.0",
|
"version": "18.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
|
||||||
@@ -3811,8 +3961,7 @@
|
|||||||
"node_modules/react-is": {
|
"node_modules/react-is": {
|
||||||
"version": "16.13.1",
|
"version": "16.13.1",
|
||||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||||
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
|
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/react-leaflet": {
|
"node_modules/react-leaflet": {
|
||||||
"version": "4.2.1",
|
"version": "4.2.1",
|
||||||
@@ -3848,6 +3997,14 @@
|
|||||||
"node": ">=8.10.0"
|
"node": ">=8.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/redux": {
|
||||||
|
"version": "4.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz",
|
||||||
|
"integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.9.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/reflect.getprototypeof": {
|
"node_modules/reflect.getprototypeof": {
|
||||||
"version": "1.0.6",
|
"version": "1.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.6.tgz",
|
||||||
@@ -3872,8 +4029,7 @@
|
|||||||
"node_modules/regenerator-runtime": {
|
"node_modules/regenerator-runtime": {
|
||||||
"version": "0.14.1",
|
"version": "0.14.1",
|
||||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
|
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
|
||||||
"integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==",
|
"integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/regexp.prototype.flags": {
|
"node_modules/regexp.prototype.flags": {
|
||||||
"version": "1.5.2",
|
"version": "1.5.2",
|
||||||
@@ -4158,6 +4314,32 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/socket.io-client": {
|
||||||
|
"version": "4.7.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.7.5.tgz",
|
||||||
|
"integrity": "sha512-sJ/tqHOCe7Z50JCBCXrsY3I2k03iOiUe+tj1OmKeD2lXPiGH/RUCdTZFoqVyN7l1MnpIzPrGtLcijffmeouNlQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@socket.io/component-emitter": "~3.1.0",
|
||||||
|
"debug": "~4.3.2",
|
||||||
|
"engine.io-client": "~6.5.2",
|
||||||
|
"socket.io-parser": "~4.2.4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/socket.io-parser": {
|
||||||
|
"version": "4.2.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz",
|
||||||
|
"integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==",
|
||||||
|
"dependencies": {
|
||||||
|
"@socket.io/component-emitter": "~3.1.0",
|
||||||
|
"debug": "~4.3.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/source-map-js": {
|
"node_modules/source-map-js": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz",
|
||||||
@@ -4501,6 +4683,11 @@
|
|||||||
"node": ">=0.8"
|
"node": ">=0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/tiny-invariant": {
|
||||||
|
"version": "1.3.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz",
|
||||||
|
"integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg=="
|
||||||
|
},
|
||||||
"node_modules/to-regex-range": {
|
"node_modules/to-regex-range": {
|
||||||
"version": "5.0.1",
|
"version": "5.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||||
@@ -4713,6 +4900,22 @@
|
|||||||
"punycode": "^2.1.0"
|
"punycode": "^2.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/use-memo-one": {
|
||||||
|
"version": "1.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/use-memo-one/-/use-memo-one-1.1.3.tgz",
|
||||||
|
"integrity": "sha512-g66/K7ZQGYrI6dy8GLpVcMsBp4s17xNkYJVSMvTEevGy3nDxHOfE6z8BVE22+5G5x7t3+bhzrlTDB7ObrEE0cQ==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/use-sync-external-store": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/util-deprecate": {
|
"node_modules/util-deprecate": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||||
@@ -4913,6 +5116,34 @@
|
|||||||
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
|
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/ws": {
|
||||||
|
"version": "8.11.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz",
|
||||||
|
"integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"bufferutil": "^4.0.1",
|
||||||
|
"utf-8-validate": "^5.0.2"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"bufferutil": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"utf-8-validate": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/xmlhttprequest-ssl": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/yallist": {
|
"node_modules/yallist": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||||
|
|||||||
@@ -10,11 +10,13 @@
|
|||||||
"lint": "next lint"
|
"lint": "next lint"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@hello-pangea/dnd": "^16.6.0",
|
||||||
"leaflet-defaulticon-compatibility": "^0.1.2",
|
"leaflet-defaulticon-compatibility": "^0.1.2",
|
||||||
"next": "14.1.4",
|
"next": "14.1.4",
|
||||||
"react": "^18",
|
"react": "^18",
|
||||||
"react-dom": "^18",
|
"react-dom": "^18",
|
||||||
"react-leaflet": "^4.2.1"
|
"react-leaflet": "^4.2.1",
|
||||||
|
"socket.io-client": "^4.7.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"autoprefixer": "^10.0.1",
|
"autoprefixer": "^10.0.1",
|
||||||
|
|||||||
Reference in New Issue
Block a user