Compare commits

...

2 Commits

Author SHA1 Message Date
eb2fe007ab Test 2025-03-30 16:16:12 +00:00
f33d9fdf36 More zones 2025-03-30 14:31:41 +00:00
2 changed files with 317 additions and 21 deletions

View File

@@ -11,15 +11,17 @@ body {
} }
.container { .container {
display: flex; display: grid;
gap: 20px; grid-template-columns: repeat(6, 1fr); /* 4 zones in the top row */
padding: 20px; grid-template-rows: auto auto; /* Two rows */
justify-content: center; gap: 5px; /* Reduced gap between zones */
padding: 5px;
justify-items: center;
} }
.dropzone { .dropzone {
width: 300px; width: 200px;
padding: 20px; padding: 10px;
border: 2px dashed #aaa; border: 2px dashed #aaa;
background-color: #ffffff; background-color: #ffffff;
border-radius: 10px; border-radius: 10px;
@@ -36,7 +38,7 @@ body {
} }
.dropzone.runway { .dropzone.runway {
width: 300px; width: 200px;
height: 150px; height: 150px;
background-color: #ffebee; /* Light red */ background-color: #ffebee; /* Light red */
} }
@@ -49,7 +51,20 @@ body {
background-color: #fff3e0; /* Light orange */ background-color: #fff3e0; /* Light orange */
} }
.dropzone.ppr {
background-color: #fff3e0; /* Light orange */
}
.dropzone.inbound {
/* grid-column: 1 / 2; /* Place in the first column of the bottom row */
background-color: #ede7f6; /* Light purple */
}
.card { .card {
display: flex;
flex-direction: column; /* Stack items vertically */
justify-content: space-between;
align-items: flex-start; /* Align items to the left */
padding: 15px; padding: 15px;
margin: 10px 0; margin: 10px 0;
background-color: #ffffff; background-color: #ffffff;
@@ -60,7 +75,103 @@ body {
transition: transform 0.2s ease, box-shadow 0.2s ease; transition: transform 0.2s ease, box-shadow 0.2s ease;
} }
.card-header {
display: flex;
justify-content: space-between; /* Align registration left and type right */
width: 100%;
font-size: 1.2em; /* Make the registration larger */
font-weight: bold; /* Optional: make the registration bold */
}
.card-type {
align-self: flex-end; /* Align type to the right on the bottom row */
font-size: 0.9em; /* Optional: slightly smaller font for type */
color: #666; /* Optional: subtle color for type */
}
.card-bottom {
display: flex;
justify-content: space-between; /* Align POB to the left and type to the right */
width: 100%;
}
.card:active { .card:active {
transform: scale(1.02); transform: scale(1.02);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
} }
.card-note {
text-align: center; /* Center the note text */
margin: 10px 0; /* Add spacing around the note */
font-style: italic; /* Optional: make the note italic for emphasis */
color: #555; /* Optional: subtle color for the note */
width: 100%; /* Ensure the note spans the full width of the card */
}
.popup {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5); /* Semi-transparent background */
display: flex;
justify-content: center;
align-items: center;
z-index: 1000; /* Ensure it appears above other elements */
}
.popup-content {
background-color: #ffffff;
padding: 20px;
border-radius: 10px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
width: 300px;
max-width: 90%;
text-align: center;
}
.popup-content h3 {
margin-bottom: 20px;
color: #333;
}
.popup-content label {
display: block;
margin-bottom: 10px;
text-align: left;
font-weight: bold;
color: #555;
}
.popup-content input {
width: 100%;
padding: 8px;
margin-bottom: 15px;
border: 1px solid #ccc;
border-radius: 5px;
box-sizing: border-box;
}
.popup-content button {
padding: 10px 20px;
margin: 5px;
border: none;
border-radius: 5px;
cursor: pointer;
font-size: 1em;
}
.popup-content button:first-of-type {
background-color: #4caf50; /* Green for Add */
color: white;
}
.popup-content button:last-of-type {
background-color: #f44336; /* Red for Cancel */
color: white;
}
.popup-content button:hover {
opacity: 0.9;
}

View File

@@ -1,19 +1,39 @@
import React, { useState } from "react"; import React, { useState, useEffect, useRef } from "react";
import "./App.css"; import "./App.css";
const App = () => { const App = () => {
const [zones, setZones] = useState({ const [zones, setZones] = useState({
awaitingDepart: [ awaitingDepart: [
{ id: "1", registration: "G-ARYK", type: "C172", pob: 1 }, { id: "1", registration: "G-ARYK", type: "C172", pob: 1, note: "SLEAP" },
{ id: "2", registration: "G-BIBT", type: "AA5", pob: 2 }, { id: "2", registration: "G-BIBT", type: "AA5", pob: 2, note: "EGBP" },
{ id: "3", registration: "G-BAMC", type: "C150", pob: 2 }, { id: "3", registration: "G-BAMC", type: "C152", pob: 2, note: "LCL" },
{ id: "4", registration: "G-GOHI", type: "C208", pob: 12 }, { id: "4", registration: "G-GOHI", type: "C208", pob: 12, note: "Here" },
], ],
runway: [], runway: [],
local: [], local: [],
circuit: [], circuit: [],
inbound: [],
ppr: [],
aog: [],
}); });
const [showPopup, setShowPopup] = useState(false);
const [newCard, setNewCard] = useState({
registration: "",
type: "",
pob: "",
note: "",
});
const [editCard, setEditCard] = useState(null);
const registrationInputRef = useRef(null);
useEffect(() => {
if (showPopup) {
registrationInputRef.current?.focus();
}
}, [showPopup]);
const handleDragStart = (e, card, sourceZone) => { const handleDragStart = (e, card, sourceZone) => {
e.dataTransfer.setData("card", JSON.stringify(card)); e.dataTransfer.setData("card", JSON.stringify(card));
e.dataTransfer.setData("sourceZone", sourceZone); e.dataTransfer.setData("sourceZone", sourceZone);
@@ -61,17 +81,95 @@ const App = () => {
className="card" className="card"
draggable draggable
onDragStart={(e) => handleDragStart(e, card, sourceZone)} onDragStart={(e) => handleDragStart(e, card, sourceZone)}
onClick={() => handleCardClick(card, sourceZone)}
> >
{card.registration} <div className="card-header">
<br /> <span>{card.registration}</span>
Type: {card.type} </div>
<br /> <div className="card-note">{card.note}</div>
{card.pob} POB <div className="card-bottom">
<span>{card.pob} POB</span>
<span>{card.type}</span>
</div>
</div> </div>
)); ));
const handleInputChange = (e) => {
const { name, value } = e.target;
setNewCard((prev) => ({
...prev,
[name]: name === "registration" ? value.toUpperCase() : value,
}));
};
const handleAddCard = () => {
if (
!newCard.registration ||
!newCard.type ||
isNaN(parseInt(newCard.pob, 10))
) {
alert("All fields are required and POB must be a number.");
return;
}
const card = {
id: Date.now().toString(),
registration: newCard.registration,
type: newCard.type,
pob: parseInt(newCard.pob, 10),
note: newCard.note,
};
setZones((prevZones) => ({
...prevZones,
awaitingDepart: [...prevZones.awaitingDepart, card],
}));
setShowPopup(false);
setNewCard({ registration: "", type: "", pob: "", note: "" });
};
const handleCardClick = (card, sourceZone) => {
setEditCard({ ...card, sourceZone });
setNewCard(card);
setShowPopup(true);
};
const handleSaveEdit = () => {
if (
!newCard.registration ||
!newCard.type ||
isNaN(parseInt(newCard.pob, 10))
) {
alert("All fields are required and POB must be a number.");
return;
}
setZones((prevZones) => {
const updatedSourceZone = prevZones[editCard.sourceZone].map((item) =>
item.id === editCard.id ? { ...newCard, id: editCard.id } : item
);
return {
...prevZones,
[editCard.sourceZone]: updatedSourceZone,
};
});
setShowPopup(false);
setEditCard(null);
setNewCard({ registration: "", type: "", pob: "", note: "" });
};
const handleKeyDown = (e) => {
if (e.key === "Enter") {
editCard ? handleSaveEdit() : handleAddCard();
}
};
return ( return (
<div className="container"> <div className="container">
{/* Top row */}
<div <div
className="dropzone awaitingDepart" className="dropzone awaitingDepart"
onDragOver={handleDragOver} onDragOver={handleDragOver}
@@ -88,6 +186,14 @@ const App = () => {
<h3>Active Runway</h3> <h3>Active Runway</h3>
{renderCards(zones.runway, "runway")} {renderCards(zones.runway, "runway")}
</div> </div>
<div
className="dropzone circuit"
onDragOver={handleDragOver}
onDrop={(e) => handleDrop(e, "circuit")}
>
<h3>Circuit</h3>
{renderCards(zones.circuit, "circuit")}
</div>
<div <div
className="dropzone local" className="dropzone local"
onDragOver={handleDragOver} onDragOver={handleDragOver}
@@ -97,13 +203,92 @@ const App = () => {
{renderCards(zones.local, "local")} {renderCards(zones.local, "local")}
</div> </div>
<div <div
className="dropzone circuit" className="dropzone inbound"
onDragOver={handleDragOver} onDragOver={handleDragOver}
onDrop={(e) => handleDrop(e, "circuit")} onDrop={(e) => handleDrop(e, "inbound")}
> >
<h3>Circuit</h3> <h3>Inbound</h3>
{renderCards(zones.circuit, "circuit")} {renderCards(zones.inbound, "inbound")}
</div> </div>
<div
className="dropzone ppr"
onDragOver={handleDragOver}
onDrop={(e) => handleDrop(e, "ppr")}
>
<h3>PPRs</h3>
{renderCards(zones.ppr, "ppr")}
</div>
<div
className="dropzone aog"
onDragOver={handleDragOver}
onDrop={(e) => handleDrop(e, "aog")}
>
<h3>A/C on Ground</h3>
{renderCards(zones.aog, "aog")}
</div>
<div className="bottom-container">
<button onClick={() => setShowPopup(true)}>Add New Card</button>
</div>
{showPopup && (
<div className="popup">
<div className="popup-content">
<h3>{editCard ? "Edit Card" : "Add New Card"}</h3>
<label>
Registration:
<input
type="text"
name="registration"
value={newCard.registration}
onChange={handleInputChange}
ref={registrationInputRef}
onKeyDown={handleKeyDown}
/>
</label>
<label>
Type:
<input
type="text"
name="type"
value={newCard.type}
onChange={handleInputChange}
onKeyDown={handleKeyDown}
/>
</label>
<label>
POB:
<input
type="number"
name="pob"
value={newCard.pob}
onChange={handleInputChange}
onKeyDown={handleKeyDown}
/>
</label>
<label>
Note:
<input
type="text"
name="note"
value={newCard.note}
onChange={handleInputChange}
onKeyDown={handleKeyDown}
/>
</label>
<button onClick={editCard ? handleSaveEdit : handleAddCard}>
{editCard ? "Save" : "Add"}
</button>
<button
onClick={() => {
setShowPopup(false);
setEditCard(null);
setNewCard({ registration: "", type: "", pob: "", note: "" });
}}
>
Cancel
</button>
</div>
</div>
)}
</div> </div>
); );
}; };