diff --git a/JMayer.Example.ASPReact.Server/JMayer.Example.ASPReact.Server.csproj b/JMayer.Example.ASPReact.Server/JMayer.Example.ASPReact.Server.csproj
index c8372a6..b575935 100644
--- a/JMayer.Example.ASPReact.Server/JMayer.Example.ASPReact.Server.csproj
+++ b/JMayer.Example.ASPReact.Server/JMayer.Example.ASPReact.Server.csproj
@@ -7,16 +7,16 @@
..\jmayer.example.aspreact.client
npm run dev
https://localhost:5173
- 9.0.0
+ 9.0.1
jmayer913
jmayer913
https://github.com/jmayer913/JMayer-Example-ASPReact
-
+
- 9.0.11
+ 9.0.12
diff --git a/README.md b/README.md
index 11f7ecb..73cf8b6 100644
--- a/README.md
+++ b/README.md
@@ -5,14 +5,30 @@ find a flight in the flight schedule to determine what sort destination the bag
On startup, the example project pregenerates a few airlines, gates and sort destinations and then, a flight schedule is pregenerated; a flight every 10 minutes between 4AM and 10PM. The example has two pages, airlines and flight schedule.
-## Airline
+## Airlines Page
+
The airlines page allows the user to add/edit/delete airlines.
-
+
+
+### Add / Edit
+
+On the airlines page, the user can create a new airline or edit an existing airline.
+
+* Name - The friendly name for the airline; required and must be unique.
+* Description - A description about the airline; optional.
+* IATA - The code assigned by the International Air Transport Association to the airline; required and must be two letters or a letter and a number.
+* ICAO - The code assigned by the International Civil Aviation Organization to the airline; required, must be 3 letters and must be unique.
+* Number Code - The number code assigned by the International Air Transport Association to the airline; required, must be 3 digits and must be unique unless 000 (unassigned).
+* Sort Destination - The default sort destination for the airline; required.
+
+
+
+
-
+### Delete
-
+On the airlines page, the user can delete an airline. The user will be required to confirm the deletion or cancel. On confirmation, the airline and its associated flights will be deleted.
@@ -21,14 +37,32 @@ The flight schedule page allows the user to add/edit/delete flights in the sched
+### Add / Edit
+
+On the flight schedule page, the user can create a new flight or edit an existing flight.
+
+* Gate - The gate the flight will be docked at for departure; required.
+* Airline - The airline which owns the plane; required.
+* Flight Number - The number assigned to the flight; required and must be 4 digits or 4 digits and a letter.
+* Destination - The next destination for the flight; required and must be 3 letters.
+* Depart Time - The schedule time the plane will depart from the gate; required.
+* Sort Destination - The sort destination bags will be sorted too for the flight; required.
+
+If the flight's airline, flight number and destination match another flight, the add or edit will be rejected by the server.
+
-
+### Delete
+On the flight schedule page, the user can delete a flight. The user will be required to confirm the deletion or cancel. On confirmation, the flight will be deleted.
+
+## Edit Conflict
+When two users are editing an airline or flight at the same time, whoever submits first will win; the other user will be told to try again.
+
diff --git a/TestProject/TestProject.csproj b/TestProject/TestProject.csproj
index 2ffc64f..dd51fa0 100644
--- a/TestProject/TestProject.csproj
+++ b/TestProject/TestProject.csproj
@@ -7,7 +7,7 @@
false
true
- 9.0.0
+ 9.0.1
@@ -15,7 +15,7 @@
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
diff --git a/jmayer.example.aspreact.client/package-lock.json b/jmayer.example.aspreact.client/package-lock.json
index e92703b..042526a 100644
--- a/jmayer.example.aspreact.client/package-lock.json
+++ b/jmayer.example.aspreact.client/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "jmayer.example.aspreact.client",
- "version": "9.0.0",
+ "version": "9.0.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "jmayer.example.aspreact.client",
- "version": "9.0.0",
+ "version": "9.0.1",
"dependencies": {
"globals": "^16.2.0",
"primeflex": "^4.0.0",
@@ -789,9 +789,9 @@
}
},
"node_modules/@eslint/js": {
- "version": "9.39.1",
- "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.1.tgz",
- "integrity": "sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw==",
+ "version": "9.39.2",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.2.tgz",
+ "integrity": "sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==",
"dev": true,
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -1227,9 +1227,9 @@
"dev": true
},
"node_modules/@types/react": {
- "version": "19.2.7",
- "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.7.tgz",
- "integrity": "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==",
+ "version": "19.2.9",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.9.tgz",
+ "integrity": "sha512-Lpo8kgb/igvMIPeNV2rsYKTgaORYdO1XGVZ4Qz3akwOj0ySGYMPlQWa8BaLn0G63D1aSaAQ5ldR06wCpChQCjA==",
"dependencies": {
"csstype": "^3.2.2"
}
@@ -2060,9 +2060,9 @@
}
},
"node_modules/eslint": {
- "version": "9.39.1",
- "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.1.tgz",
- "integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==",
+ "version": "9.39.2",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.2.tgz",
+ "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==",
"dev": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.8.0",
@@ -2071,7 +2071,7 @@
"@eslint/config-helpers": "^0.4.2",
"@eslint/core": "^0.17.0",
"@eslint/eslintrc": "^3.3.1",
- "@eslint/js": "9.39.1",
+ "@eslint/js": "9.39.2",
"@eslint/plugin-kit": "^0.4.1",
"@humanfs/node": "^0.16.6",
"@humanwhocodes/module-importer": "^1.0.1",
@@ -2163,9 +2163,9 @@
}
},
"node_modules/eslint-plugin-react-refresh": {
- "version": "0.4.24",
- "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.24.tgz",
- "integrity": "sha512-nLHIW7TEq3aLrEYWpVaJ1dRgFR+wLDPN8e8FpYAql/bMV2oBEfC37K0gLEGgv9fy66juNShSMV8OkTqzltcG/w==",
+ "version": "0.4.26",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.26.tgz",
+ "integrity": "sha512-1RETEylht2O6FM/MvgnyvT+8K21wLqDNg4qD51Zj3guhjt433XbnnkVttHMyaVyAFD03QSV4LPS5iE3VQmO7XQ==",
"dev": true,
"peerDependencies": {
"eslint": ">=8.40"
@@ -3548,28 +3548,28 @@
}
},
"node_modules/react": {
- "version": "19.2.0",
- "resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz",
- "integrity": "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==",
+ "version": "19.2.3",
+ "resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz",
+ "integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/react-dom": {
- "version": "19.2.0",
- "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.0.tgz",
- "integrity": "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==",
+ "version": "19.2.3",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.3.tgz",
+ "integrity": "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==",
"dependencies": {
"scheduler": "^0.27.0"
},
"peerDependencies": {
- "react": "^19.2.0"
+ "react": "^19.2.3"
}
},
"node_modules/react-is": {
- "version": "19.2.0",
- "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.0.tgz",
- "integrity": "sha512-x3Ax3kNSMIIkyVYhWPyO09bu0uttcAIoecO/um/rKGQ4EltYWVYtyiGkS/3xMynrbVQdS69Jhlv8FXUEZehlzA=="
+ "version": "19.2.3",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.3.tgz",
+ "integrity": "sha512-qJNJfu81ByyabuG7hPFEbXqNcWSU3+eVus+KJs+0ncpGfMyYdvSmxiJxbWR65lYi1I+/0HBcliO029gc4F+PnA=="
},
"node_modules/react-refresh": {
"version": "0.17.0",
@@ -3581,9 +3581,9 @@
}
},
"node_modules/react-router": {
- "version": "7.9.6",
- "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.9.6.tgz",
- "integrity": "sha512-Y1tUp8clYRXpfPITyuifmSoE2vncSME18uVLgaqyxh9H35JWpIfzHo+9y3Fzh5odk/jxPW29IgLgzcdwxGqyNA==",
+ "version": "7.12.0",
+ "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.12.0.tgz",
+ "integrity": "sha512-kTPDYPFzDVGIIGNLS5VJykK0HfHLY5MF3b+xj0/tTyNYL1gF1qs7u67Z9jEhQk2sQ98SUaHxlG31g1JtF7IfVw==",
"dependencies": {
"cookie": "^1.0.1",
"set-cookie-parser": "^2.6.0"
@@ -3602,11 +3602,11 @@
}
},
"node_modules/react-router-dom": {
- "version": "7.9.6",
- "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.9.6.tgz",
- "integrity": "sha512-2MkC2XSXq6HjGcihnx1s0DBWQETI4mlis4Ux7YTLvP67xnGxCvq+BcCQSO81qQHVUTM1V53tl4iVVaY5sReCOA==",
+ "version": "7.12.0",
+ "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.12.0.tgz",
+ "integrity": "sha512-pfO9fiBcpEfX4Tx+iTYKDtPbrSLLCbwJ5EqP+SPYQu1VYCXdy79GSj0wttR0U4cikVdlImZuEZ/9ZNCgoaxwBA==",
"dependencies": {
- "react-router": "7.9.6"
+ "react-router": "7.12.0"
},
"engines": {
"node": ">=20.0.0"
diff --git a/jmayer.example.aspreact.client/package.json b/jmayer.example.aspreact.client/package.json
index 9e63522..dff1ce0 100644
--- a/jmayer.example.aspreact.client/package.json
+++ b/jmayer.example.aspreact.client/package.json
@@ -1,7 +1,7 @@
{
"name": "jmayer.example.aspreact.client",
"private": true,
- "version": "9.0.0",
+ "version": "9.0.1",
"type": "module",
"scripts": {
"dev": "vite",
diff --git a/jmayer.example.aspreact.client/src/datalayers/AirlineDataLayer.jsx b/jmayer.example.aspreact.client/src/datalayers/AirlineDataLayer.jsx
index 08aef90..d4a81c2 100644
--- a/jmayer.example.aspreact.client/src/datalayers/AirlineDataLayer.jsx
+++ b/jmayer.example.aspreact.client/src/datalayers/AirlineDataLayer.jsx
@@ -23,6 +23,12 @@ export function useAirlineDataLayer() {
const [updateAirlineSuccess, setUpdateAirlineSuccess] = useState(false);
const [updateAirlineValidationProblemDetails, setUpdateAirlineValidationProblemDetails] = useState(null);
+ //Constants for the status codes returned by the server.
+ const BadRequestCode = 400;
+ const NotFoundCode = 404;
+ const ConflictCode = 409;
+ const InternalServerError = 500;
+
//The function adds an airline to the server.
//@param {object} airline The airline to add.
const addAirline = (airline) => {
@@ -39,10 +45,10 @@ export function useAirlineDataLayer() {
if (response.ok) {
setAddAirlineSuccess(true);
}
- else if (response.status === 400) {
+ else if (response.status === BadRequestCode) {
response.json().then(validationProblemDetails => setAddAirlineValidationProblemDetails(validationProblemDetails));
}
- else if (response.status === 500) {
+ else if (response.status === InternalServerError) {
response.json().then(problemDetails => showError(problemDetails.detail));
}
else {
@@ -73,7 +79,7 @@ export function useAirlineDataLayer() {
if (response.ok) {
setDeleteAirlineSuccess(true);
}
- else if (response.status === 500) {
+ else if (response.status === NotFoundCode || response.status === InternalServerError) {
response.json().then(problemDetails => showError(problemDetails.detail));
}
else {
@@ -107,10 +113,10 @@ export function useAirlineDataLayer() {
if (response.ok) {
setUpdateAirlineSuccess(true);
}
- else if (response.status === 400) {
+ else if (response.status === BadRequestCode) {
response.json().then(validationProblemDetails => setUpdateAirlineValidationProblemDetails(validationProblemDetails));
}
- else if (response.status == 409 || response.status === 500) {
+ else if (response.status === NotFoundCode || response.status == ConflictCode || response.status === InternalServerError) {
response.json().then(problemDetails => showError(problemDetails.detail));
}
else {
diff --git a/jmayer.example.aspreact.client/src/datalayers/FlightDataLayer.jsx b/jmayer.example.aspreact.client/src/datalayers/FlightDataLayer.jsx
index a5cdb1a..2d3334d 100644
--- a/jmayer.example.aspreact.client/src/datalayers/FlightDataLayer.jsx
+++ b/jmayer.example.aspreact.client/src/datalayers/FlightDataLayer.jsx
@@ -27,6 +27,12 @@ export function useFlightDataLayer() {
const [updateFlightSuccess, setUpdateFlightSuccess] = useState(false);
const [updateFlightValidationProblemDetails, setUpdateFlightValidationProblemDetails] = useState(null);
+ //Constants for the status codes returned by the server.
+ const BadRequestCode = 400;
+ const NotFoundCode = 404;
+ const ConflictCode = 409;
+ const InternalServerError = 500;
+
//The function adds a flight to the server.
//@param {object} flight The flight to add.
const addFlight = (flight) => {
@@ -45,10 +51,10 @@ export function useFlightDataLayer() {
if (response.ok) {
setAddFlightSuccess(true);
}
- else if (response.status === 400) {
+ else if (response.status === BadRequestCode) {
response.json().then(validationProblemDetails => setAddFlightValidationProblemDetails(validationProblemDetails));
}
- else if (response.status === 500) {
+ else if (response.status === InternalServerError) {
response.json().then(problemDetails => showError(problemDetails.detail));
}
else {
@@ -79,7 +85,7 @@ export function useFlightDataLayer() {
if (response.ok) {
setDeleteFlightSuccess(true);
}
- else if (response.status === 500) {
+ else if (response.status === NotFoundCode || response.status === InternalServerError) {
response.json().then(problemDetails => showError(problemDetails.detail));
}
else {
@@ -125,10 +131,10 @@ export function useFlightDataLayer() {
if (response.ok) {
setUpdateFlightSuccess(true);
}
- else if (response.status === 400) {
+ else if (response.status === BadRequestCode) {
response.json().then(validationProblemDetails => setUpdateFlightValidationProblemDetails(validationProblemDetails));
}
- else if (response.status == 409 || response.status === 500) {
+ else if (response.status === NotFoundCode || response.status == ConflictCode || response.status === InternalServerError) {
response.json().then(problemDetails => showError(problemDetails.detail));
}
else {