Skip to content

agh-cs-imbeciles/train-transport-management-system

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Trainly

Train transport management system

Authors

Introduction

The train transport management system, which provides basic rail routes listing and reservations.

Used technologies and tools

  • React
  • SCSS
  • ExpressJS
  • MongoDB

Documentation:

Backend

Express backend application

Build on machine with

Node.js: 18.15.0

npm: 9.6.6

Table of contents

  1. Instructions how to run
  2. Database
    1. Collections
      1. Users
      2. Places
      3. Stops
      4. Trains
      5. Rail routes
      6. Reservations
  3. Backend application
    1. API endpoints
      1. Account
        1. Sign-up
        2. Login
      2. Places
      3. Stops
      4. Trains
      5. Rail routes
      6. Reservations
  4. Frontend application
    1. Components
      1. Base
      2. login_register
        1. Login
        2. Register
      3. cockpit
        1. Cockpit
        2. cockpit_views
          1. ListElement
          2. ReservationList
          3. Reservations
          4. SearchPanel
      4. ReservationPanel

Instructions how to run

  1. Clone the repository
    [email protected]:agh-cs-imbeciles/train-transport-management-system.git
    
  2. Go the backend directory
    cd backend
    
  3. Install node packages
    npm install
    npm install --save-dev
    
  4. Run development server
    npm run dev
    

Database

The database is document-oriented, runned on MongoDB, more precisely MongoDB Atlas.

Collections

Contains X collections.

Users collection

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
            }
        }
    }

Places collection

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
        }
    }

Stops collection

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']
        }
    }

Trains collection

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']
            }
        }
    }

Rail routes collection

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
    }
    Example of the extended RailRouteSchema:
    {
        "_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"
    }

Reservations collection

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
    }

Backend application

API Endpoints

Account

Sign-up

Source code:

Login

  • URL: /login,
  • Method: POST,
  • Required body:
    string email - email
    string password - plain password
{ "email": "", "password": "" }
Source code:

Places

Source code

Insert a new place

Get a place by its ID

  • URL: /places/id/:id,
  • Method: GET,
  • Required body: none
  • Returns: PlaceSchema
    string :id - 24-character id of place

Get all places

  • URL: /places/all,
  • Method: GET,
  • Required body: none
  • Returns: [PlaceSchema]

Get a place by its name

  • URL: /places/name/:name,
  • Method: GET,
  • Required body: none
  • Returns: PlaceSchema (best matched)
    string :name - name of place

Get all places by their province name

  • URL: /places/province/:name,
  • Method: GET,
  • Required body: none
  • Returns: [PlaceSchema]
    string :name - name of province

Stops

Source code

Insert a new stop

Get a stop by its ID

  • URL: /rail/stops/id/:id,
  • Method: GET,
  • Required body: none
  • Returns: StopSchema
    string :id - 24-character id of stop

Get all stops

  • URL: /rail/stops/all,
  • Method: GET,
  • Required body: none
  • Returns: [StopSchema]

Get a stop by its name

  • URL: /rail/stop/name/:name,
  • Method: GET,
  • Required body: none
  • Returns: StopSchema (best matched)
    string :name - name of stop

Get all stops by their place

  • 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

Rail routes

Source code

Insert a new rail stop

  • 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"
            }
        ]
    }

Get a rail routes by its ID

  • URL: /rail/routes/id/:id,
  • Method: GET,
  • Required body: none
  • Returns: RailRouteSchema
    string :id - 24-character id of stop

Get all rail routes by departure or arrival date and stop IDs

  • 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]

Trains

Source code

Insert a new train

Get a train by its ID

  • URL: /trains/:id,
  • Method: GET,
  • Required body: none
  • Returns: TrainSchema

Get a seats by its type

  • URL: /trains/:id/seats/:listOfSeatTypes, seperated by ,,
  • Method: GET,
  • Required body: none
  • Returns: List of TrainSeatSchema

Reservations

Source code

Insert a new reservation

Get a reservation by its ID

  • URL: /reservation/:id,
  • Method: GET,
  • Required body: none
  • Returns: ReservationSchema
    string :id - 24-character id of reservation

Get all reservations by user ID

  • URL: /reservation/user/:id,
  • Method: GET,
  • Required body: none
  • Returns: ReservationSchema
    string :id - 24-character id of user

Frontend Application

We based our solution on React library and its functional components.

Base

Responsible for displaying navigation bar and path protection. Screenshot Screenshot

login_register

This folder contains components that allow user to log in or register to application.

Login

Screenshot

Register

Screenshot

cockpit

Folder contains components that display basic information about reservations, allows to search for new connections.

Cockpit Component

Screenshot

Cockpit Views

ListElement

Displays basic data about connection. After being clicked, allows user to book seats certain connection. Screenshot

ReservationList

Displays extended data about already booked connenctions. Screenshot

SearchPanel

Allows user to find new connections. User is able to filter connections by arrival/departure time,start/stop places. Screenshot

ReservationPanel

Allows user to choose how many places he would like to book. Displays additional data about connection. Screenshot