Train transport management system
The train transport management system, which provides basic rail routes listing and reservations.
React
SCSS
ExpressJS
MongoDB
Documentation:
Express
backend application
Node.js
: 18.15.0
npm
: 9.6.6
- Instructions how to run
- Database
- Backend application
- Frontend application
- Clone the repository
[email protected]:agh-cs-imbeciles/train-transport-management-system.git
- Go the backend directory
cd backend
- Install node packages
npm install npm install --save-dev
- Run development server
npm run dev
The database is document-oriented, runned on MongoDB
, more precisely MongoDB Atlas.
Contains X collections.
Defines users of the application, clients and staff but without checking their roles.
- Source code: user.js
- Source code preview:
UserSchema
{ firstName: { type: String, required: [true, 'First name is required'], minLength: [2, 'First name is too short'], maxLength: [32, 'First name is too long'], match: [/^\p{Lu}\p{Ll}+$/u, 'First name must be at least 2 characters long, start with uppercase followed by lowercase'], trim: true }, lastName: { type: String, required: [true, 'Last name is required'], minLength: [2, 'Last name is too short'], maxLength: [32, 'Last name is too long'], match: [/^\p{Lu}\p{Ll}+$/u, 'Last name must be at least 2 characters long, start with uppercase followed by lowercase'], trim: true }, email: { type: String, required: [true, 'Email is required'], minLength: [5, 'Email is too short'], maxLength: [128, 'Email name is too long'], match: [/^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/, 'Email is not valid'], trim: true }, password: { type: String, required: [true, 'Password is required'] }, address: { street: { name: { type: String, required: [true, 'Street name is required'], minLength: [2, 'Street name is too short'], maxLength: [64, 'Street name is too long'], trim: true }, houseNumber: { type: String, required: [true, 'House number is required'], minLength: [1, 'House number is too short'], maxLength: [10, 'House number is too long'], trim: true }, apartmentNumber: { type: String, required: [true, 'Apartment number is required'], minLength: [1, 'Apartment number is too short'], maxLength: [10, 'Apartment number is too long'], trim: true } }, city: { type: String, required: [true, 'City is required'], minLength: [2, 'City is too short'], maxLength: [32, 'City is too long'], trim: true }, zipCode: { type: String, required: [true, 'Zip code is required'], minLength: [1, 'Zip code is too short'], maxLength: [10, 'Zip code is too long'], trim: true } } }
Defines places - cities, towns and villages.
- Source code: place.js
- Source code preview:
PlaceSchema
{ name: { type: String, required: [true, 'Name of the place is required'], minLength: [2, 'Name of the place is too short'], maxLength: [48, 'Name of the place is too long'], trim: true }, province: { type: String, required: [true, 'Province of the place is required'], minLength: [2, 'Province of the place is too short'], maxLength: [48, 'Province of the place is too long'], trim: true } }
Defines stops, contains placeId
, so that it's combined with Place
collection.
- Source code: stops.js
- Source code preview:
StopSchema
{ name: { type: String, required: [true, 'Name of the stop is required'], minLength: [2, 'Name of the stop is too short'], maxLength: [48, 'Name of the stop is too long'], trim: true }, placeId: { type: mongoose.ObjectId, required: [true, 'Place id of the stop is required'] } }
Defines all currently active trains and their seats.
-
Source code: train.js
-
Source code preview:
TrainSchema
{ name: { type: String, required: [true, 'Name is required'], minLength: [1, 'Name is too short'], maxLength: [48, 'Name is too long'], trim: true }, types: { type: Map, required: [true, 'Type map is required'], of: Number }, manufacturerInfo: { manufacturer: { type: String, required: [true, 'Manufacturer name is required'], minLength: [1, 'Manufacturer name is too short'], maxLength: [48, 'Manufacturer name is too long'], trim: true }, model: { type: String, required: [true, 'Model is required'], minLength: [1, 'Model is too short'], maxLength: [32, 'Model is too long'], trim: true }, createdAtYear: { type: Number, min: [1804, 'Created at year is lower than 1804'], max: [new Date().getFullYear(), 'Created at year is greater than current year'] } }, obtainedAtYear: { type: Number, required: [true, 'Obtained at year is required'], min: [2023, 'Obtained at year is lower than 2023'], max: [new Date().getFullYear(), 'Obtained at year is greater than current year'] }, inspections: [ { year: { type: Number, required: [true, 'Inspection year is required'], min: [1804, 'Inspection year is lower than 1804'], max: [new Date().getFullYear(), 'Inspection year is greater than current year'] } } ], seats: { type: Map, of: TrainSeatSchema, required: [true, 'Seats map is required'] } }
TrainSeatSchema
{ seatId: { type: String, required: [true, 'Seat ID is required'], minLength: [1, 'Seat ID is too short'], maxLength: [16, 'Seat ID is too long'], trim: true }, types: { type: Map, of: Number }, position: { row: { type: Number, required: [true, 'Seat row is required'], min: [1, 'Seat row is too low'], max: [512, 'Seat row is too high'] }, column: { type: Number, required: [true, 'Seat column is required'], min: [1, 'Seat column is too low'], max: [32, 'Seat column is too high'] } } }
Defines all currently on- or furthergoing rail routes.
-
Source code: railRoute.js
-
Source code preview:
RailRouteSchema
{ trainId: { type: mongoose.ObjectId, required: [true, 'Train ID of the rail route is required'] }, ticketsCost: { type: Map, of: Number, required: [true, 'Tickets cost map of the rail route is required'] }, departure: { stopId: { type: mongoose.ObjectId, required: [true, 'Departure stop ID of the rail route is required'] }, date: { type: Date, required: [true, 'Departure stop of the rail is required'] } }, arrival: { stopId: { type: mongoose.ObjectId, required: [true, 'Arrival stop ID of the rail route is required'] }, date: { type: Date, required: [true, 'Arrival date of the rail is required'] } }, stops: [{ stopId: { type: mongoose.ObjectId, required: [true, 'Stop ID of the rail route is required'] }, date: { type: Date, required: [true, 'Stop date of the rail is required'] } }] }, { timestamps: true }
{ "_id": "6487e6faecffa599c1d815ce", "trainId": "6485f91a69d92892eb4b0965", "ticketsCost": { "firstClass": 400, "standard": 20 }, "departure": { "stopId": "6485e52712372c03b8747898", "date": "2023-06-17T08:30:00.000Z", "stop": [ { "_id": "6485e52712372c03b8747898", "name": "Zakopane", "place": { "_id": "6485063716691550652486e6", "name": "Zakopane" } } ] }, "arrival": { "stopId": "6485e34012372c03b874786e", "date": "2023-06-17T10:04:00.000Z", "stop": [ { "_id": "6485e34012372c03b874786e", "name": "Kraków Główny", "place": { "_id": "648503a7221f629a17f924b8", "name": "Kraków" } } ] }, "stops": { "stop": [ { "_id": "6485e37512372c03b8747876", "name": "Kraków Łagiewniki", "place": { "_id": "648503a7221f629a17f924b8", "name": "Kraków" } }, { "_id": "6485e41012372c03b8747882", "name": "Radziszów", "place": { "_id": "648504db16691550652486ae", "name": "Radziszów" } }, { "_id": "6485e4d112372c03b874788e", "name": "Skawa Środkowa", "place": { "_id": "6485058816691550652486ce", "name": "Skawa" } }, { "_id": "6485e51012372c03b8747894", "name": "Nowy Targ", "place": { "_id": "648505f316691550652486de", "name": "Nowy Targ" } } ] }, "createdAt": "2023-06-13T03:48:10.171Z", "updatedAt": "2023-06-13T03:48:10.171Z" }
Defines all currently active reservations, grouped by userId
.
- Source code: reservation.js
- Source code preview:
ReservationSchema
{ userId: { type: mongoose.ObjectId, required: [true, 'User ID is required'] }, railRouteId: { type: mongoose.ObjectId, required: [true, 'Train route ID is required'] }, seats: [ { seatId: { type: String, required: [true, 'Seat ID is required'] } } ] }, { timestamps: true }
- URL:
/signup
, - Method:
PUT
, - Required body: full user schema
- URL:
/login
, - Method:
POST
, - Required body:
string email
- email
string password
- plain password
{ "email": "", "password": "" }
Source code
- URL:
/places
, - Method:
PUT
, - Required body: full place schema
- URL:
/places/id/:id
, - Method:
GET
, - Required body: none
- Returns:
PlaceSchema
string :id
- 24-character id of place
- URL:
/places/all
, - Method:
GET
, - Required body: none
- Returns:
[PlaceSchema]
- URL:
/places/name/:name
, - Method:
GET
, - Required body: none
- Returns:
PlaceSchema
(best matched)
string :name
- name of place
- URL:
/places/province/:name
, - Method:
GET
, - Required body: none
- Returns:
[PlaceSchema]
string :name
- name of province
Source code
- URL:
/rail/stops
, - Method:
PUT
, - Required body: full stop schema
- URL:
/rail/stops/id/:id
, - Method:
GET
, - Required body: none
- Returns:
StopSchema
string :id
- 24-character id of stop
- URL:
/rail/stops/all
, - Method:
GET
, - Required body: none
- Returns:
[StopSchema]
- URL:
/rail/stop/name/:name
, - Method:
GET
, - Required body: none
- Returns:
StopSchema
(best matched)
string :name
- name of stop
- URL:
/rail/stops/place
, - Method:
GET
, - Required body: place and/or province names
string placeName
- name of the place, through which a filter be applied
string provinceName
- name of the province, through which a filter be applied - Returns:
[StopSchema]
Example request:
/rail/stops/place?placeName=Mszana&provinceName=polskie
Source code
- URL:
/rail/routes
, - Method:
PUT
, - Required body: full rail route schema
Example body:{ "trainId": "6485f91a69d92892eb4b0965", "ticketsCost": { "firstClass": 250, "standard": 150 }, "departure": { "stopId": "648508346ebe5f779de65375", "date": "2023-06-13T03:33+02:00" }, "arrival": { "stopId": "6485e34012372c03b874786e", "date": "2023-06-13T04:45+02:00" }, "stops": [ { "stopId": "6485e4fc12372c03b8747892", "date": "2023-06-13T03:52+02:00" }, { "stopId": "6485e49f12372c03b8747888", "date": "2023-06-13T04:02+02:00" }, { "stopId": "6485e37512372c03b8747876", "date": "2023-06-13T04:36+02:00" } ] }
- URL:
/rail/routes/id/:id
, - Method:
GET
, - Required body: none
- Returns:
RailRouteSchema
string :id
- 24-character id of stop
-
URL:
/rail/routes/query
, -
Method:
POST
, -
Required body:
| string departureDate
- string date format of minimum departure date (one of them required)
| string arrivalDate
- string date format of minimum arrival date (one of them required)
string departureStopId
- 24-character long ID of the departure stop (required)
string arrivalStopId
- 24-character long ID of the arrival stop (required)Example body:
{ "departureDate": "2023-06-08T04:00+02:00", "arrivalDate": "2023-06-20T04:00+02:00", "departureStopId": "648508346ebe5f779de65375", "arrivalStopId": "6485e3c012372c03b8747878" }
-
Returns: extended
[RailRouteSchema]
Source code
- URL:
/trains
, - Method:
POST
, - Required body: full trains schema
- URL:
/trains/:id
, - Method:
GET
, - Required body: none
- Returns:
TrainSchema
- URL:
/trains/:id/seats/:listOfSeatTypes
, seperated by,
, - Method:
GET
, - Required body: none
- Returns: List of
TrainSeatSchema
Source code
- URL:
/reservation
, - Method:
POST
, - Required body: full reservation schema
- URL:
/reservation/:id
, - Method:
GET
, - Required body: none
- Returns:
ReservationSchema
string :id
- 24-character id of reservation
- URL:
/reservation/user/:id
, - Method:
GET
, - Required body: none
- Returns:
ReservationSchema
string :id
- 24-character id of user
We based our solution on React
library and its functional components.
Responsible for displaying navigation bar and path protection.
This folder contains components that allow user to log in or register to application.
Folder contains components that display basic information about reservations, allows to search for new connections.
Displays basic data about connection. After being clicked, allows user to book seats certain connection.
Displays extended data about already booked connenctions.
Allows user to find new connections. User is able to filter connections by arrival/departure time,start/stop places.
Allows user to choose how many places he would like to book. Displays additional data about connection.