A comprehensive travel booking platform focused on sustainable transportation options
Built with Phoenix LiveView & Elixir
Team 12: Mohoshena Ahkter, Hashim Ali, Jamal Muradov, Tobias Rothe
- About the Project
- Key Features
- Technology Stack
- Architecture
- Getting Started
- Development Workflow
- Testing
- Deployment
- Project Structure
- Database Schema
- API & Routes
- Contributing
- License
EarthScanner is a modern web application designed to help travelers find and book sustainable transportation options across Europe. The platform connects travelers with eco-friendly trip options while providing comprehensive tools for trip management, search functionality, and personalized recommendations.
- Sustainability Focus: Help users make environmentally conscious travel decisions by displaying CO2 emissions for each trip
- User Experience: Provide intuitive search, filtering, and booking capabilities with real-time updates
- Admin Efficiency: Enable admins to easily manage trips, track performance metrics, and monitor bookings
- Personalization: Offer personalized recommendations, search history, and favorite routes
- Real-time trip availability and booking management
- Smart recommendation engine (fastest, cheapest, least polluting)
- Comprehensive search history tracking
- Similar trip suggestions
- Performance analytics for administrators
- Role-based access control (Admin/User)
- Search trips by source/destination city
- Filter by transport type (plane, bus, train)
- Filter by maximum CO2 emissions
- View only future trips
- Access last 5 search queries for quick repeat searches
- Fastest Trip: Shortest travel duration
- Cheapest Trip: Best price option
- Least Polluting: Lowest CO2 emissions
- Highlighted section above search results
- Real-time seat availability
- Instant booking confirmation
- View all active and past bookings
- Cancel bookings for future trips
- Automatic booking status updates (active β completed)
- Save favorite routes for quick access
- Rate completed trips (1-5 stars)
- View trip ratings from other users
- Search history automatically saved
- Similar trip suggestions on trip detail pages
- Secure user registration and authentication
- Password hashing with PBKDF2
- Update profile information
- Change password securely
- Delete account with data cleanup
- Create new trips with multiple dates
- Upload trip images
- Edit trip details and pricing
- Delete trips (with confirmation)
- Bulk date management
- Total bookings per trip
- Revenue tracking
- Seat occupancy rates
- Past vs. Future trip filtering
- Comprehensive trip dashboards
- View all trips at a glance
- Filter by time (all, future, past)
- Quick access to trip details
- Performance metrics visualization
- Elixir 1.15+: Functional programming language for scalability
- Phoenix 1.8: Modern web framework
- Phoenix LiveView: Real-time, server-rendered UI updates
- Ecto: Database wrapper and query generator
- PostgreSQL 17: Relational database
- Phoenix LiveView: Server-rendered reactive components
- Tailwind CSS 4.0: Utility-first styling
- Heroicons: Beautiful SVG icons
- Alpine.js (via Phoenix hooks): Lightweight JavaScript
- Guardian: JWT-based authentication
- Pbkdf2: Password hashing
- CSRF Protection: Built-in Phoenix security
- Credo: Static code analysis
- ExUnit: Unit testing framework
- White Bread: BDD testing with Gherkin
- Hound: Browser automation for integration tests
- Docker: Containerized development environment
- Mix: Build tool and task runner
- esbuild: JavaScript bundler
- Docker Compose: Local development orchestration
Phoenix Context Pattern: Business logic is organized into contexts:
Accounts: User authentication and managementTrips: Trip and trip date managementBookings: Booking lifecycle and cancellationsFavorites: User favorite routesTripSearch: Search and filtering logic
LiveView Components: Real-time UI with server-side rendering
- No need for REST APIs for most interactions
- WebSocket-based state management
- Automatic DOM diffing and patching
Database Design
- UUID primary keys for security
- Enum types for status fields
- Proper foreign key constraints
- Cascading deletes where appropriate
- Unique constraints for data integrity
- Server-Side Rendering: Phoenix LiveView eliminates the need for a separate frontend framework
- Real-Time Updates: WebSocket connections keep UI in sync without polling
- Role-Based Authorization: Middleware pipeline for route protection
- UTC Timestamps: All dates stored in UTC for consistency
- Image Storage: Binary image data stored in database with content type
- Elixir 1.15+ and Erlang/OTP 26+
- PostgreSQL 17 (or use Docker)
- Node.js 18+ (for asset compilation)
- Git
-
Clone the repository
git clone https://github.com/yourusername/earthscanner-group12.git cd earthscanner-group12 -
Install dependencies
mix setup
This runs:
mix deps.get- Install Elixir dependenciesmix ecto.setup- Create and migrate databasemix assets.setup- Install frontend dependenciesmix assets.build- Compile assets
-
Start PostgreSQL (if using Docker)
docker-compose up -d
-
Seed the database (optional)
mix run priv/repo/seeds.exs
-
Start the Phoenix server
mix phx.server
-
Visit the application
Create a .env file or configure the following in config/dev.exs:
config :earthscanner_group12, EarthScannerGroup12.Repo,
username: "postgres",
password: "postgres",
hostname: "localhost",
database: "earthscanner_group12_dev",
port: 5432# Start with live reload
mix phx.server
# Start with IEx console
iex -S mix phx.server# Create database
mix ecto.create
# Run migrations
mix ecto.migrate
# Rollback migration
mix ecto.rollback
# Reset database
mix ecto.reset
# Seed data
mix run priv/repo/seeds.exs# Run pre-commit checks (recommended before committing)
mix precommitThis runs:
- Compilation with warnings as errors
- Dependency cleanup
- Code formatting
- All tests
# Format code
mix format
# Run Credo linter
mix credo
# Check for unused dependencies
mix deps.unlock --unusedThe project uses multiple testing approaches:
- Unit Tests: ExUnit for context and schema testing
- Integration Tests: Controller and LiveView testing
- BDD Tests: White Bread with Gherkin feature files
# Run all tests
mix test
# Run specific test file
mix test test/earthscanner_group12/trips_test.exs
# Run tests with coverage
mix test --cover
# Run BDD feature tests
mix white_bread.run
# Run failed tests only
mix test --failed
# Run tests in watch mode (requires external tool)
mix test.watchtest/
βββ earthscanner_group12/ # Context tests
β βββ accounts_test.exs
β βββ bookings_test.exs
β βββ trips_test.exs
β βββ ...
βββ earthscanner_group12_web/ # Controller/LiveView tests
β βββ controllers/
βββ features/ # BDD feature tests
β βββ booking_test.exs
β βββ trips_search_test.exs
β βββ ...
βββ support/ # Test helpers
βββ conn_case.ex
βββ data_case.ex
βββ fixtures/
Unit Test Example:
test "creates booking with valid data" do
user = user_fixture()
trip_date = trip_date_fixture()
assert {:ok, booking} = Bookings.create_booking(user, trip_date)
assert booking.status == :active
endBDD Feature Example:
Feature: Trip search
Scenario: User searches for trips
Given I am logged in as a user
When I search for trips with city "Tallinn"
Then I should see text "Tallinn"
And I should see text "Trips found: 1"The application is deployed to a production server with the following setup:
- Server: Ubuntu Linux
- IP: 193.40.11.123
- Application Path:
/home/ubuntu/earthscanner_group12 - Process Manager: systemd
- Web Server: Cowboy (built-in)
-
Automated deployment script:
./scripts/deploy.sh
-
Manual deployment:
# Build release MIX_ENV=prod mix release # Copy to server scp -r _build/prod/rel/earthscanner_group12 user@server:/path/ # Start application /path/earthscanner_group12/bin/earthscanner_group12 start
-
Database migrations on production:
/path/bin/earthscanner_group12 eval "EarthScannerGroup12.Release.migrate()"
Set the following in production:
export DATABASE_URL="ecto://user:pass@localhost/db"
export SECRET_KEY_BASE="your-secret-key"
export PHX_HOST="yourdomain.com"
export PORT=4000For detailed deployment instructions, see DEPLOYMENT.md.
earthscanner-group12-main/
βββ assets/ # Frontend assets
β βββ css/ # Stylesheets
β β βββ app.css # Main CSS with Tailwind
β βββ js/ # JavaScript
β β βββ app.js # Main JS entry point
β βββ vendor/ # Third-party assets
β
βββ config/ # Configuration files
β βββ config.exs # Base configuration
β βββ dev.exs # Development config
β βββ prod.exs # Production config
β βββ runtime.exs # Runtime configuration
β βββ test.exs # Test configuration
β
βββ docs/ # Documentation
β βββ diagrams/
β βββ erd.puml # Entity relationship diagram
β
βββ features/ # BDD feature files (Gherkin)
β βββ booking.feature
β βββ trips.feature
β βββ trip_search.feature
β βββ favorites.feature
β βββ ratings.feature
β βββ ... (20+ feature files)
β
βββ lib/
β βββ earthscanner_group12/ # Core business logic
β β βββ accounts/ # User management
β β β βββ user.ex
β β β βββ password.ex
β β βββ auth/ # Authentication
β β β βββ auth_pipeline.ex
β β βββ bookings/ # Booking management
β β β βββ booking.ex
β β βββ favorites/ # Favorites management
β β β βββ favorite.ex
β β βββ trips/ # Trip management
β β β βββ trip.ex
β β β βββ trip_date.ex
β β β βββ trips.ex
β β β βββ rating.ex
β β β βββ trip_search.ex
β β βββ accounts.ex # Accounts context
β β βββ bookings.ex # Bookings context
β β βββ favorites.ex # Favorites context
β β βββ application.ex # OTP application
β β βββ repo.ex # Database repository
β β βββ mailer.ex # Email functionality
β β
β βββ earthscanner_group12_web/ # Web interface
β βββ controllers/ # HTTP controllers
β β βββ session_controller.ex
β β βββ user_controller.ex
β β βββ trip_controller.ex
β β βββ booking_controller.ex
β β βββ ...
β βββ live/ # LiveView modules
β β βββ trips_live.ex
β β βββ admin_trips_live.ex
β β βββ bookings_live.ex
β β βββ ...
β βββ components/ # Reusable components
β β βββ core_components.ex
β βββ plugs/ # Custom plugs
β βββ endpoint.ex # Phoenix endpoint
β βββ router.ex # Route definitions
β βββ gettext.ex # Internationalization
β
βββ priv/
β βββ repo/
β β βββ migrations/ # Database migrations
β β βββ seeds.exs # Seed data
β βββ static/ # Static files
β βββ gettext/ # Translations
β
βββ test/ # Test suite
β βββ earthscanner_group12/ # Context tests
β βββ earthscanner_group12_web/ # Web tests
β βββ features/ # BDD tests
β βββ support/ # Test helpers
β
βββ scripts/ # Deployment scripts
β βββ deploy.sh
β βββ setup_server.sh
β βββ earthscanner_group12.service
β
βββ mix.exs # Project configuration
βββ docker-compose.yml # Docker setup
βββ README.md # This file
βββ DEPLOYMENT.md # Deployment guide
βββ AGENTS.md # AI coding guidelines
- id (UUID, PK)
- username (string, unique)
- email (string, unique)
- password_hash (string)
- phone_number (string)
- role (enum: admin, user)
- first_name, last_name (string)
- birthdate (date)
- nationality (string)
- residence_address (string)
- timestamps- id (UUID, PK)
- admin_id (UUID, FK β users)
- transport_type (enum: plane, bus, train)
- source_city (string)
- destination_city (string)
- duration (interval)
- price (decimal)
- co2_pollution (decimal)
- available_tickets (integer)
- departure_time (utc_datetime)
- arrival_time (utc_datetime)
- image (binary)
- image_content_type (string)
- timestamps- id (UUID, PK)
- trip_id (UUID, FK β trips, cascade delete)
- seats_total (integer)
- seats_available (integer)
- departure_datetime (utc_datetime)
- arrival_datetime (utc_datetime)
- travel_date (date)
- active (boolean)
- timestamps- id (UUID, PK)
- user_id (UUID, FK β users, cascade delete)
- trip_date_id (UUID, FK β trip_dates, cascade delete)
- status (enum: active, completed, cancelled_by_user, cancelled_by_admin)
- price_paid_cents (integer)
- returned_at (utc_datetime)
- timestamps
UNIQUE CONSTRAINT: user_id + trip_date_id (for active bookings)- id (UUID, PK)
- user_id (UUID, FK β users, cascade delete)
- trip_id (UUID, FK β trips, cascade delete)
- timestamps
UNIQUE CONSTRAINT: user_id + trip_id- id (UUID, PK)
- user_id (UUID, FK β users, cascade delete)
- trip_id (UUID, FK β trips, cascade delete)
- rating (integer, 1-5)
- timestamps
UNIQUE CONSTRAINT: user_id + trip_id- id (UUID, PK)
- user_id (UUID, FK β users, cascade delete)
- source_city (string)
- destination_city (string)
- search_params (jsonb)
- inserted_at (utc_datetime)
LIMIT: Last 5 searches per user- One Admin β Many Trips
- One Trip β Many Trip Dates
- One Trip Date β Many Bookings
- One User β Many Bookings
- One User β Many Favorites
- One User β Many Ratings
- One User β Many Searches (max 5)
GET / # Home page
GET /sessions/new # Login page
POST /sessions # Login action
GET /users/new # Registration page
POST /users # Registration action# User Management
GET /users/me # Current user profile
GET /users/:id/edit # Edit profile
PUT /users/:id # Update profile
GET /users/:id/change_password # Change password form
PUT /users/:id/update_password # Update password
DELETE /users/:id # Delete account
# Trip Browsing
GET /tripdates # List all available trips
GET /tripdates/:id # Trip date details
GET /trips/:id # Trip details (LiveView)
LIVE /trips # Browse trips (LiveView)
# Bookings
GET /bookings/new # New booking form
POST /bookings # Create booking
GET /bookings/:id # Booking details
GET /bookings/:id/cancel # Cancel booking
LIVE /bookings # User bookings list
# Favorites
POST /favorites # Add favorite
POST /favorites/:trip_id # Add favorite (alt)
DELETE /favorites/:trip_id # Remove favorite
# Ratings
POST /ratings # Create rating
DELETE /ratings/:id # Delete ratingLIVE /admin/trips # Admin trips dashboard
LIVE /admin/trips/:id # Admin trip details
LIVE /trips/new # Create new trip (LiveView)
# Trip Management
GET /trips/new # New trip form
POST /trips # Create trip
GET /trips/:id/edit # Edit trip form
PUT /trips/:id # Update trip
DELETE /trips/:id # Delete trip:require_authenticated- Must be logged in:require_user- Must be logged in as regular user:require_admin- Must be logged in as admin:require_no_admin- Must be logged in as non-admin
- Follow the Elixir Style Guide
- Run
mix formatbefore committing - Run
mix credoto check code quality - Use descriptive commit messages
- Create a feature branch:
git checkout -b feature/my-feature - Make your changes
- Run tests:
mix test - Run pre-commit checks:
mix precommit - Commit:
git commit -m "Add my feature" - Push:
git push origin feature/my-feature - Create a Pull Request
- Write feature tests first (BDD)
- Implement the feature
- Add unit tests
- Update documentation
- Run full test suite
The docs/diagrams folder contains PlantUML source files for architectural diagrams:
erd.puml- Entity Relationship Diagram
Install PlantUML (requires Java 8+):
macOS:
brew install plantumlWindows:
choco install plantumlLinux (Ubuntu/Debian):
sudo apt install plantuml graphvizGenerate:
plantuml docs/diagrams/*.pumlThis project was developed as part of an academic course assignment. All rights reserved to Team 12.
- Phoenix Framework Team - For the excellent web framework
- Elixir Community - For the supportive ecosystem
- TailwindCSS - For the utility-first CSS framework
- Course Instructors - For guidance and support
Team 12
- Hashim Ali
- Jamal Muradov
- Tobias Rothe
For questions or issues, please create an issue in the repository.
Built with β€οΈ using Elixir & Phoenix LiveView
Making sustainable travel accessible to everyone
We suggest using Docker:
-
Make sure the Docker Daemon is running
-
Setup up postgres database with docker-compose.yml.
docker compose up -d
You can also setup the postgres database locally.
-
We keep the canonical database model in
docs/diagrams/erd.puml. Any schema change starts there. -
Generate a migration shell so Ecto registers the timestamped file:
mix ecto.gen.migration create_earthscanner_core_tables
-
Edit the generated file (e.g.
priv/repo/migrations/20251107111215_create_earthscanner_core_tables.exs) to match the ERD. It is normal for the generator to create an emptychange/0; we replace it with explicitup/0/down/0functions that:- create the
user_role,transport_type, andbooking_statusenums - define all tables (
users,trips,trip_dates,bookings,user_searches,favorites,trip_ratings,trip_recommendation) - add indexes, constraints, and foreign keys exactly as shown in the ERD
- create the
-
Apply the migration locally:
mix ecto.migrate
-
Every migration is reviewed before commit to ensure the schema and ERD stay in sync.
-
Set the
MIX_ENV$env:MIX_ENV="dev"
-
Run
mix setupto install and setup dependencies -
Start Phoenix endpoint with
mix phx.serveror inside IEx withiex -S mix phx.server
Now you can visit localhost:4000 from your browser.
UI shows that there are unapplied migrations
- Delete
_build/dev/lib/earthscanner_group12/priv/repo/migrations/directory. This directory remembers migrations that were deleted and wants them to be applied. - Run
mix ecto.reset. This should now only build the current migrations to the mentioned directory. - Restart the development server with
mix phx.server.
The CI in .gitlab-ci.yml checks these automatically.
mix format [--check-formatted]mix credo-
Set the
MIX_ENV$env:MIX_ENV="test"
-
Run the tests
mix test
-
Set the
MIX_ENV$env:MIX_ENV="test"
-
Compile the dependencies for the
testenv.mix deps.get mix deps.compile mix compile
-
Start the chromedriver (follow the lecture notes to install it on your device)
chromedriver --port=55591
-
Run all BDD tests
mix white_bread.run --suite "All"
Frequent errors
If the dependencies don't compile and some Gherkin errors are thrown, this may be caused by misaligned package versions from old dependency installs. Delete your build/ and deps/ directories and restart from step 2.
this is the project repository for EarthScanner - Sustainable Travel Booking Platform