diff --git a/client/public/stylesheets/atc.css b/client/public/stylesheets/atc.css index 652d866e..baab81f5 100644 --- a/client/public/stylesheets/atc.css +++ b/client/public/stylesheets/atc.css @@ -80,10 +80,22 @@ +[data-board-type="tower"] .ol-strip-board-strip > * { + text-align: center; +} + +[data-board-type="tower"] .ol-strip-board-strip > :first-child { + text-align: left; +} + [data-board-type="tower"] .ol-strip-board-strip :nth-child(2) input { width:25px; } +[data-board-type="tower"] .ol-strip-board-strip :nth-child(2) { + font-size:10px; +} + @@ -109,7 +121,12 @@ padding:4px 8px; } -.ol-strip-board-add-flight button { +.add-flight-by-click img { + filter:invert(); + height: 12px; +} + +.ol-strip-board-add-flight button[type="submit"] { background-color: darkgreen; border-bottom-right-radius: var( --border-radius-sm ); border-bottom-left-radius: 0; diff --git a/client/routes/api/atc.js b/client/routes/api/atc.js index 82e8865a..f29e88e6 100644 --- a/client/routes/api/atc.js +++ b/client/routes/api/atc.js @@ -25,12 +25,13 @@ function uuidv4() { -function Flight( name, boardId ) { +function Flight( name, boardId, unitId ) { this.id = uuidv4(); this.boardId = boardId; this.name = name; this.status = "unknown"; this.takeoffTime = -1; + this.unitId = parseInt( unitId ); } Flight.prototype.getData = function() { @@ -39,7 +40,8 @@ Flight.prototype.getData = function() { "boardId" : this.boardId, "name" : this.name, "status" : this.status, - "takeoffTime" : this.takeoffTime + "takeoffTime" : this.takeoffTime, + "unitId" : this.unitId }; } @@ -180,7 +182,11 @@ app.post( "/flight", ( req, res ) => { res.status( 400 ).send( "Invalid/missing flight name" ); } - const flight = new Flight( req.body.name, req.body.boardId ); + if ( !req.body.unitId || isNaN( req.body.unitId ) ) { + res.status( 400 ).send( "Invalid/missing unitId" ); + } + + const flight = new Flight( req.body.name, req.body.boardId, req.body.unitId ); dataHandler.addFlight( flight ); diff --git a/client/src/atc/atc.ts b/client/src/atc/atc.ts index af8a682e..158a2f1a 100644 --- a/client/src/atc/atc.ts +++ b/client/src/atc/atc.ts @@ -8,6 +8,7 @@ export interface FlightInterface { name : string; status : "unknown"; takeoffTime : number; + unitId : number; } diff --git a/client/src/atc/atcboard.ts b/client/src/atc/atcboard.ts index 5a54fd92..79c066a4 100644 --- a/client/src/atc/atcboard.ts +++ b/client/src/atc/atcboard.ts @@ -1,12 +1,14 @@ -import { Draggable } from "leaflet"; import { Dropdown } from "../controls/dropdown"; -import { generateUUIDv4, zeroAppend } from "../other/utils"; +import { zeroAppend } from "../other/utils"; import { ATC } from "./atc"; +import { Unit } from "../units/unit"; export interface StripBoardStripInterface { "id": string, "element": HTMLElement, - "dropdowns": {[key:string]: Dropdown} + "dropdowns": {[key:string]: Dropdown}, + "isDeleted"?: boolean, + "unitId": number } export abstract class ATCBoard { @@ -15,13 +17,16 @@ export abstract class ATCBoard { #boardId:string = ""; #templates: {[key:string]: string} = {}; + // Elements #boardElement:HTMLElement; #clockElement:HTMLElement; #stripBoardElement:HTMLElement; // Content + #isAddFlightByClickEnabled:boolean = false; #strips:{[key:string]: StripBoardStripInterface} = {}; + #unitIdsBeingMonitored:number[] = []; // Update timing #updateInterval:number|undefined = undefined; @@ -37,6 +42,12 @@ export abstract class ATCBoard { this.#stripBoardElement = this.getBoardElement().querySelector( ".ol-strip-board-strips" ); this.#clockElement = this.getBoardElement().querySelector( ".ol-strip-board-clock" ); + + setInterval( () => { + this.updateClock(); + }, 1000 ); + + if ( this.#boardElement.classList.contains( "ol-draggable" ) ) { let options:any = {}; @@ -50,15 +61,37 @@ export abstract class ATCBoard { } - setInterval( () => { - this.updateClock(); - }, 1000 ); - + this.#setupAddFlight(); } - addFlight( flightName:string ) { + addFlight( unit:Unit ) { + + const baseData = unit.getBaseData(); + + const unitCanBeAdded = () => { + + if ( baseData.category !== "Aircraft" ) { + return false; + } + + if ( baseData.AI === true ) { + // return false; + } + + if ( this.#unitIdsBeingMonitored.includes( unit.ID ) ) { + return false; + } + + return true; + } + + if ( !unitCanBeAdded() ) { + return; + } + + this.#unitIdsBeingMonitored.push( unit.ID ); return fetch( '/api/atc/flight/', { method: 'POST', @@ -68,7 +101,8 @@ export abstract class ATCBoard { }, "body": JSON.stringify({ "boardId" : this.getBoardId(), - "name" : flightName + "name" : baseData.unitName, + "unitId" : unit.ID }) }); @@ -76,7 +110,16 @@ export abstract class ATCBoard { addStrip( strip:StripBoardStripInterface ) { + this.#strips[ strip.id ] = strip; + + strip.element.querySelectorAll( "button.deleteFlight" ).forEach( btn => { + btn.addEventListener( "click", ev => { + ev.preventDefault(); + this.deleteFlight( strip.id ); + }); + }); + } @@ -109,16 +152,37 @@ export abstract class ATCBoard { } - deleteStrip( id:string ) { + deleteStrip( flightId:string ) { + + if ( this.#strips.hasOwnProperty( flightId ) ) { + + this.#strips[ flightId ].element.remove(); + this.#strips[ flightId ].isDeleted = true; + + setTimeout( () => { + delete this.#strips[ flightId ]; + }, 10000 ); - if ( this.#strips.hasOwnProperty( id ) ) { - this.#strips[ id ].element.remove(); - delete this.#strips[ id ]; } } + deleteFlight( flightId:string ) { + + this.deleteStrip( flightId ); + + fetch( '/api/atc/flight/' + flightId, { + method: 'DELETE', + headers: { + 'Accept': '*/*', + 'Content-Type': 'application/json' + } + }); + + } + + getATC() { return this.#atc; } @@ -161,6 +225,67 @@ export abstract class ATCBoard { } + #setupAddFlight() { + + const toggleIsAddFlightByClickEnabled = () => { + this.#isAddFlightByClickEnabled = ( !this.#isAddFlightByClickEnabled ); + this.getBoardElement().classList.toggle( "add-flight-by-click", this.#isAddFlightByClickEnabled ); + } + + + document.addEventListener( "unitSelection", ( ev:CustomEventInit ) => { + + if ( this.#isAddFlightByClickEnabled !== true ) { + return; + } + + this.addFlight( ev.detail ); + + toggleIsAddFlightByClickEnabled(); + + }); + + + this.getBoardElement().querySelectorAll( "form.ol-strip-board-add-flight" ).forEach( form => { + + if ( form instanceof HTMLFormElement ) { + + form.addEventListener( "submit", ev => { + + ev.preventDefault(); + + + if ( ev.target instanceof HTMLFormElement ) { + + const elements = ev.target.elements; + const flightName = elements[1]; + + if ( flightName.value === "" ) { + return; + } + + // this.addFlight( -1, flightName.value ); + + form.reset(); + + } + + }); + + } + + }); + + + this.getBoardElement().querySelectorAll( ".add-flight-by-click" ).forEach( el => { + el.addEventListener( "click", () => { + toggleIsAddFlightByClickEnabled(); + }); + }); + + } + + startUpdates() { this.#updateInterval = setInterval( () => { diff --git a/client/src/atc/board/ground.ts b/client/src/atc/board/ground.ts index 236f01a4..e722e03f 100644 --- a/client/src/atc/board/ground.ts +++ b/client/src/atc/board/ground.ts @@ -9,53 +9,6 @@ export class ATCBoardGround extends ATCBoard { super( atc, element ); - document.addEventListener( "deleteFlightStrip", ( ev:CustomEventInit ) => { - - if ( ev.detail.id ) { - - fetch( '/api/atc/flight/' + ev.detail.id, { - method: 'DELETE', - headers: { - 'Accept': '*/*', - 'Content-Type': 'application/json' - } - }); - - } - - }); - - - this.getBoardElement().querySelectorAll( "form.ol-strip-board-add-flight" ).forEach( form => { - - if ( form instanceof HTMLFormElement ) { - - form.addEventListener( "submit", ev => { - - ev.preventDefault(); - - - if ( ev.target instanceof HTMLFormElement ) { - - const elements = ev.target.elements; - const flightName = elements[0]; - - if ( flightName.value === "" ) { - return; - } - - this.addFlight( flightName.value ); - - form.reset(); - - } - - }); - - } - - }); - } @@ -89,7 +42,7 @@ export class ATCBoardGround extends ATCBoard {
${this.timeToGo( flight.takeoffTime )}
- + `; stripBoard.insertAdjacentHTML( "beforeend", template ); @@ -98,7 +51,8 @@ export class ATCBoardGround extends ATCBoard { strip = { "id": flight.id, "element": stripBoard.lastElementChild, - "dropdowns": {} + "dropdowns": {}, + "unitId": -1 }; strip.element.querySelectorAll( ".ol-select" ).forEach( select => { @@ -217,6 +171,7 @@ export class ATCBoardGround extends ATCBoard { }); + stripBoard.querySelectorAll( `[data-updating]` ).forEach( strip => { this.deleteStrip( strip.getAttribute( "data-flight-id" ) || "" ); }); diff --git a/client/src/panels/unitcontrolpanel.ts b/client/src/panels/unitcontrolpanel.ts index 91550e75..e56c9ca7 100644 --- a/client/src/panels/unitcontrolpanel.ts +++ b/client/src/panels/unitcontrolpanel.ts @@ -115,8 +115,6 @@ export class UnitControlPanel extends Panel { else database = null; // TODO add databases for other unit types - console.log( unit.getBaseData() ); - var button = document.createElement("button"); var callsign = unit.getBaseData().unitName || ""; diff --git a/client/views/atc.ejs b/client/views/atc.ejs index fb007578..3e969e33 100644 --- a/client/views/atc.ejs +++ b/client/views/atc.ejs @@ -1,53 +1,11 @@ -
+<%- include('atc/board.ejs', { + "boardId": "strip-board-tower", + "boardType": "tower", + "headers": [ "Flight", "a-Alt", "alt" ] +}) %> -
- -
-

Tower

-
-
- -
-
-
Flight
-
-
-
- - - -
- - -
- -
- -
-

Ground

-
-
- -
-
-
Flight
-
Status
-
T/O Time
-
TTG
-
-
-
- - - -
\ No newline at end of file +<%- include('atc/board.ejs', { + "boardId": "strip-board-ground", + "boardType": "ground", + "headers": [ "Flight", "Status", "T/O Time", "TTG" ] +}) %> \ No newline at end of file