diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..6f3a291 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "liveServer.settings.port": 5501 +} \ No newline at end of file diff --git a/assets/css/style.scss b/assets/css/style.scss index c7cfb91..6513e37 100644 --- a/assets/css/style.scss +++ b/assets/css/style.scss @@ -62,6 +62,19 @@ body { button { @include button-style($primary-color, $primary-hover-color); margin: 8px 4px; + + &:disabled { + background-color: $background-dark !important; + color: $text-color-light !important; + cursor: not-allowed; + box-shadow: none; + transform: none; + pointer-events: none; + + &:hover { + background-color: $background-dark; + } + } } input, @@ -233,4 +246,65 @@ select { transform: translateX(1000px); opacity: 0; } +} + +#weather-container { + background-color: $background-light; + border: 1px solid $background-dark; + padding: 15px; + @include border-radius($border-radius); + @include box-shadow(0 4px 12px rgba(0, 0, 0, 0.1)); + max-width: 300px; + margin-right: 20px; + margin-bottom: 20px; + font-family: $font-family; + float: left; + display: block; + clear: both; + + label { + display: block; + font-size: $font-size-base; + font-weight: bold; + margin-bottom: 5px; + color: $text-color; + } + + input { + width: calc(100% - 10px); + padding: 8px; + border: 1px solid $background-dark; + margin-bottom: 10px; + @include border-radius($border-radius); + @include transition(all, $transition-time); + font-size: 15px; + box-sizing: border-box; + + &:focus { + border-color: $primary-hover-color; + outline: none; + @include box-shadow(0 0 6px rgba(0, 123, 255, 0.4)); + } + } + + button { + @include button-style($primary-color, $primary-hover-color); + width: 100%; + padding: 8px 15px; + font-size: 16px; + margin-top: 5px; + } + + #weather-message { + color: $secondary-color; + font-size: 14px; + margin-top: 10px; + } + + #weather-info-container { + padding: 10px; + background-color: $background-dark; + @include border-radius($border-radius); + font-size: 14px; + } } \ No newline at end of file diff --git a/src/app.js b/src/app.js index 0a99d9d..8d51057 100644 --- a/src/app.js +++ b/src/app.js @@ -3,7 +3,3 @@ import MainView from './view/mainView.js'; const mainView = new MainView(); const controller = new Controller(mainView); - - - - diff --git a/src/controller/controller.js b/src/controller/controller.js index 62f0ecb..5538ddd 100644 --- a/src/controller/controller.js +++ b/src/controller/controller.js @@ -1,17 +1,25 @@ import PackageService from '../services/packageService.js'; +import WeatherService from '../services/weatherService.js'; +import TruckService from '../services/truckService.js'; import ConveyorBelt from '../model/conveyorBelt.js'; +import LoadingBay from '../model/loadingBay.js'; import ConveyorBeltView from '../view/conveyorBeltView.js'; import Truck from '../model/truck.js'; import TruckView from '../view/truckView.js'; -import LoadingBay from '../model/loadingBay.js'; +import TruckFormView from '../view/truckFormView.js'; +import WeatherFormView from '../view/weatherFormView.js'; + +const API_KEY = 'f3500d75fca4725b12c2978ff64c7538'; export default class Controller { constructor(mainView) { this.mainView = mainView; + this.mainView.setController(this); + this.weatherFormView = new WeatherFormView(); this.packageService = new PackageService(); - this.loadingBays = [new LoadingBay(1), new LoadingBay(2)]; + this.weatherService = new WeatherService(API_KEY); + this.loadingBays = [new LoadingBay(1), new LoadingBay(2)]; this.currentLoadingBayIndex = 0; - this.currentCity = 'Amsterdam'; this.addConveyorBeltButton = document.getElementById('add-conveyor-belt'); this.removeConveyorBeltButton = document.getElementById('remove-conveyor-belt'); @@ -21,6 +29,7 @@ export default class Controller { this.removeConveyorBeltButton.addEventListener('click', this.removeConveyorBelt.bind(this)); this.switchLoadingBayButton.addEventListener('click', this.switchLoadingBay.bind(this)); document.addEventListener('truckFormSubmit', this.handleTruckFormSubmit.bind(this)); + document.addEventListener('weatherFormSubmit', this.handleWeatherFormSubmit.bind(this)); const truckContainer = document.getElementById('truck-container'); @@ -49,6 +58,15 @@ export default class Controller { event.preventDefault(); } + showTruckForm() { + if (!this.currentCity) { + this.mainView.showWeatherMessage('Vul eerst een stad/dorp in.'); + return; + } + const truckFormView = new TruckFormView(this.weatherService, this.weatherData); + document.body.appendChild(truckFormView.getFormContainer()); + } + handleDrop(event) { event.preventDefault(); const packageIndex = parseInt(event.dataTransfer.getData('package-index')); @@ -68,13 +86,11 @@ export default class Controller { truckView.render(); this.renderCurrentLoadingBay(); } else { - console.log("Package did not fit in the truck"); belt.addPackage(droppedPackage); } } } - async addConveyorBelt() { const currentLoadingBay = this.getCurrentLoadingBay(); @@ -110,15 +126,30 @@ export default class Controller { }); }); - console.log("Conveyor belt started."); setInterval(async () => { const randomPackage = await this.packageService.getRandomPackage(); - console.log("Random package added to conveyor belt."); randomPackage.position = { x: 0, y: 0 }; conveyorBelt.addPackage(randomPackage); }, 10000); } + async handleWeatherFormSubmit(event) { + const { city } = event.detail; + if (!city) { + this.mainView.showWeatherMessage('Vul een stad/dorp in.'); + return; + } + + try { + this.currentCity = city; + this.weatherData = await this.weatherService.getWeather(this.currentCity); + this.mainView.showWeatherInfo(this.weatherData); + this.mainView.enableButtons(); + } catch (error) { + this.mainView.showWeatherMessage(`Kon het weer niet ophalen, vul het opnieuw in: ` + error); + } + } + handleTruckFormSubmit(event) { const truckData = event.detail; const currentLoadingBay = this.getCurrentLoadingBay(); diff --git a/src/data/packages.json b/src/data/packages.json index f8bc151..16a92ad 100644 --- a/src/data/packages.json +++ b/src/data/packages.json @@ -8,6 +8,15 @@ {"x": 3, "y": 0} ] }, + { + "type": "I", + "blocks": [ + {"x": 0, "y": 0}, + {"x": 0, "y": 1}, + {"x": 0, "y": 2}, + {"x": 0, "y": 3} + ] + }, { "type": "O", "blocks": [ @@ -26,8 +35,89 @@ {"x": 1, "y": 1} ] }, + { + "type": "T", + "blocks": [ + {"x": 1, "y": 0}, + {"x": 0, "y": 1}, + {"x": 1, "y": 1}, + {"x": 1, "y": 2} + ] + }, + { + "type": "T", + "blocks": [ + {"x": 1, "y": 0}, + {"x": 0, "y": 1}, + {"x": 1, "y": 1}, + {"x": 2, "y": 1} + ] + }, + { + "type": "T", + "blocks": [ + {"x": 0, "y": 0}, + {"x": 0, "y": 1}, + {"x": 0, "y": 2}, + {"x": 1, "y": 1} + ] + }, + { + "type": "L", + "blocks": [ + {"x": 0, "y": 0}, + {"x": 1, "y": 0}, + {"x": 2, "y": 0}, + {"x": 2, "y": 1} + ] + }, + { + "type": "L", + "blocks": [ + {"x": 1, "y": 0}, + {"x": 1, "y": 1}, + {"x": 1, "y": 2}, + {"x": 0, "y": 2} + ] + }, + { + "type": "L", + "blocks": [ + {"x": 0, "y": 0}, + {"x": 0, "y": 1}, + {"x": 1, "y": 1}, + {"x": 2, "y": 1} + ] + }, { "type": "L", + "blocks": [ + {"x": 1, "y": 0}, + {"x": 0, "y": 1}, + {"x": 1, "y": 1}, + {"x": 2, "y": 1} + ] + }, + { + "type": "J", + "blocks": [ + {"x": 0, "y": 0}, + {"x": 1, "y": 0}, + {"x": 2, "y": 0}, + {"x": 0, "y": 1} + ] + }, + { + "type": "J", + "blocks": [ + {"x": 0, "y": 0}, + {"x": 0, "y": 1}, + {"x": 0, "y": 2}, + {"x": 1, "y": 2} + ] + }, + { + "type": "J", "blocks": [ {"x": 0, "y": 0}, {"x": 1, "y": 0}, @@ -35,6 +125,15 @@ {"x": 2, "y": 1} ] }, + { + "type": "J", + "blocks": [ + {"x": 1, "y": 0}, + {"x": 1, "y": 1}, + {"x": 1, "y": 2}, + {"x": 0, "y": 0} + ] + }, { "type": "S", "blocks": [ @@ -43,5 +142,32 @@ {"x": 0, "y": 1}, {"x": 1, "y": 1} ] + }, + { + "type": "S", + "blocks": [ + {"x": 0, "y": 0}, + {"x": 0, "y": 1}, + {"x": 1, "y": 1}, + {"x": 1, "y": 2} + ] + }, + { + "type": "Z", + "blocks": [ + {"x": 0, "y": 0}, + {"x": 1, "y": 0}, + {"x": 1, "y": 1}, + {"x": 2, "y": 1} + ] + }, + { + "type": "Z", + "blocks": [ + {"x": 1, "y": 0}, + {"x": 1, "y": 1}, + {"x": 0, "y": 1}, + {"x": 0, "y": 2} + ] } -] +] \ No newline at end of file diff --git a/src/model/conveyorBelt.js b/src/model/conveyorBelt.js index 1114604..70d51cb 100644 --- a/src/model/conveyorBelt.js +++ b/src/model/conveyorBelt.js @@ -18,10 +18,10 @@ export default class ConveyorBelt { startConveyorBelt(callback) { this.intervalId = setInterval(() => { this.packages.forEach(pkg => { - pkg.position.x += 1; // Adjust movement speed as needed + pkg.position.x += 1; }); - callback(this.packages); // Callback to update the view - }, 1000); // Update every second + callback(this.packages); + }, 1000); } stopConveyorBelt() { diff --git a/src/services/packageService.js b/src/services/packageService.js index 68d855e..aeccb49 100644 --- a/src/services/packageService.js +++ b/src/services/packageService.js @@ -1,26 +1,26 @@ import PackageFactory from '../factory/packageFactory.js'; export default class PackageService { - constructor() { - this.packageData = []; - } - - async fetchPackageData() { - try { - const response = await fetch('src/data/packages.json'); - const data = await response.json(); - this.packageData = data; // Store the raw data - } catch (error) { - console.error('Error fetching packages:', error); + constructor() { + this.packageData = []; } - } - async getRandomPackage() { - if (this.packageData.length === 0) { - await this.fetchPackageData(); + async fetchPackageData() { + try { + const response = await fetch('src/data/packages.json'); + const data = await response.json(); + this.packageData = data; + } catch (error) { + console.error('Error fetching packages:', error); + } + } + + async getRandomPackage() { + if (this.packageData.length === 0) { + await this.fetchPackageData(); + } + const randomIndex = Math.floor(Math.random() * this.packageData.length); + const randomPackageData = this.packageData[randomIndex]; + return PackageFactory.createPackages([randomPackageData])[0]; } - const randomIndex = Math.floor(Math.random() * this.packageData.length); - const randomPackageData = this.packageData[randomIndex]; - return PackageFactory.createPackages([randomPackageData])[0]; // Create a new package instance - } } diff --git a/src/services/truckService.js b/src/services/truckService.js new file mode 100644 index 0000000..4735cfd --- /dev/null +++ b/src/services/truckService.js @@ -0,0 +1,25 @@ +export default class TruckService { + static canTruckDrive(weather, truckType) { + const windSpeed = weather.wind.speed; + const temperature = weather.main.temp; + const weatherCondition = weather.weather[0].main.toLowerCase(); + + if (windSpeed > 40 && truckType.toLowerCase() === 'breekbaar transport') { + return false; + } + if (temperature > 25 && truckType.toLowerCase() === 'koud transport') { + return false; + } + if ((weatherCondition === 'rain' || weatherCondition === 'snow') && truckType.toLowerCase() === 'algemeen transport') { + return false; + } + if (weatherCondition === 'snow' && truckType.toLowerCase() === 'pallets') { + return false; + } + if (weatherCondition === 'storm' && truckType.toLowerCase() === 'snelkoerier') { + return false; + } + + return true; + } +} diff --git a/src/services/weatherService.js b/src/services/weatherService.js new file mode 100644 index 0000000..85b5994 --- /dev/null +++ b/src/services/weatherService.js @@ -0,0 +1,20 @@ +export default class WeatherService { + constructor(apiKey) { + this.apiKey = apiKey; + this.baseUrl = 'https://api.openweathermap.org/data/2.5/weather'; + } + + async getWeather(city) { + try { + const response = await fetch(`${this.baseUrl}?q=${city}&appid=${this.apiKey}&units=metric`); + if (!response.ok) { + throw new Error('Network response was not ok ' + response.statusText); + } + const data = await response.json(); + return data; + } catch (error) { + console.error('Error fetching weather data:', error); + throw error; + } + } +} \ No newline at end of file diff --git a/src/view/conveyorBeltView.js b/src/view/conveyorBeltView.js index a44967b..8db3966 100644 --- a/src/view/conveyorBeltView.js +++ b/src/view/conveyorBeltView.js @@ -1,4 +1,4 @@ -import PackageView from './packageView.js'; +import PackageView from './PackageView.js'; export default class ConveyorBeltView { constructor(index) { @@ -18,12 +18,11 @@ export default class ConveyorBeltView { packageContainer.draggable = true; packageContainer.addEventListener('dragstart', (event) => { event.dataTransfer.setData('package-index', index); - event.dataTransfer.setData('conveyor-index', this.index); // Set the conveyor belt index + event.dataTransfer.setData('conveyor-index', this.index); // Conveyor belt index }); this.container.appendChild(packageContainer); - // Move the package packageContainer.style.transform = `translateX(${pkg.position.x * 20}px)`; }); } diff --git a/src/view/mainView.js b/src/view/mainView.js index 0682a43..7227387 100644 --- a/src/view/mainView.js +++ b/src/view/mainView.js @@ -1,4 +1,4 @@ -import TruckFormView from './truckFormView.js'; +import WeatherFormView from './weatherFormView.js'; export default class MainView { constructor() { @@ -6,14 +6,44 @@ export default class MainView { this.truckContainer = document.getElementById('truck-container'); this.addTruckButton = document.getElementById('add-truck'); this.switchLoadingBayButton = document.getElementById('switch-loading-bay'); + this.addConveyorBeltButton = document.getElementById('add-conveyor-belt'); + this.removeConveyorBeltButton = document.getElementById('remove-conveyor-belt'); + + this.weatherFormView = new WeatherFormView(); + document.body.insertBefore(this.weatherFormView.getWeatherContainer(), document.body.firstChild); + + this.disableButtons(); + } + + setController(controller) { + this.controller = controller; + this.weatherFormView.submitButton.addEventListener('click', () => { + const city = this.weatherFormView.input.value.trim(); + const event = new CustomEvent('weatherFormSubmit', { detail: { city } }); + document.dispatchEvent(event); + }); if (this.addTruckButton) { - this.addTruckButton.addEventListener('click', this.showTruckForm.bind(this)); + this.addTruckButton.addEventListener('click', () => this.controller.showTruckForm()); } else { console.error('Add Truck button not found'); } } + disableButtons() { + this.addTruckButton.disabled = true; + this.switchLoadingBayButton.disabled = true; + this.addConveyorBeltButton.disabled = true; + this.removeConveyorBeltButton.disabled = true; + } + + enableButtons() { + this.addTruckButton.disabled = false; + this.switchLoadingBayButton.disabled = false; + this.addConveyorBeltButton.disabled = false; + this.removeConveyorBeltButton.disabled = false; + } + addConveyorBeltView(conveyorBeltView) { this.conveyorBeltContainer.appendChild(conveyorBeltView.container); } @@ -28,11 +58,6 @@ export default class MainView { this.truckContainer.appendChild(truckView.container); } - showTruckForm() { - const truckFormView = new TruckFormView(); - document.body.appendChild(truckFormView.getFormContainer()); - } - renderLoadingBay(loadingBay) { this.conveyorBeltContainer.innerHTML = ''; this.truckContainer.innerHTML = ''; @@ -45,4 +70,25 @@ export default class MainView { this.addTruckView(view); }); } + + showWeatherMessage(message) { + this.weatherFormView.showMessage(message); + } + + clearWeatherMessage() { + this.weatherFormView.clearMessage(); + } + + showWeatherInfo(data) { + this.clearWeatherMessage(); + const weatherInfoContainer = this.weatherFormView.getWeatherInfoContainer(); + weatherInfoContainer.innerHTML = ''; + weatherInfoContainer.innerHTML = ` +
Weer: ${data.weather[0].description}
+Temperatuur: ${data.main.temp}°C
+Wind snelheid: ${data.wind.speed} m/s
+ `; + } + } diff --git a/src/view/truckFormView.js b/src/view/truckFormView.js index b3b4bc2..9752810 100644 --- a/src/view/truckFormView.js +++ b/src/view/truckFormView.js @@ -1,24 +1,46 @@ +import TruckService from '../services/truckService.js'; + export default class TruckFormView { - constructor() { + constructor(weatherService, weatherData) { + this.weatherService = weatherService; + this.weatherData = weatherData; this.currentStep = 0; this.steps = [ { label: "Voer de lengte van de vrachtwagen in:", inputId: "truck-length" }, { label: "Voer de breedte van de vrachtwagen in:", inputId: "truck-width" }, { label: "Voer het aankomst interval in (in secondes):", inputId: "truck-interval" }, { - label: "Select truck type:", + label: "Selecteer het type vrachtwagen:", inputId: "truck-type", type: "select", - options: ["Koud transport", "Breekbaar transport", "Algemeen transport", "Pallets", "Snelkoerier"] + options: this.getValidTruckTypes() } ]; this.formContainer = document.createElement('div'); this.formContainer.id = 'truck-form-container'; + this.messageContainer = document.createElement('div'); + this.messageContainer.id = 'message'; + this.messageContainer.style.color = 'red'; + this.renderStep(); } + getValidTruckTypes() { + const validTypes = []; + const weather = this.weatherData; + const truckTypes = ["Koud transport", "Breekbaar transport", "Algemeen transport", "Pallets", "Snelkoerier"]; + + truckTypes.forEach(type => { + if (TruckService.canTruckDrive(weather, type)) { + validTypes.push(type); + } + }); + + return validTypes; + } + renderStep() { this.formContainer.innerHTML = ''; @@ -42,7 +64,7 @@ export default class TruckFormView { } const nextButton = document.createElement('button'); - nextButton.textContent = this.currentStep < this.steps.length - 1 ? 'Volgende' : 'Submit'; + nextButton.textContent = this.currentStep < this.steps.length - 1 ? 'Volgende' : 'Versturen'; nextButton.addEventListener('click', () => this.handleNext(input)); this.formContainer.appendChild(label); @@ -55,18 +77,21 @@ export default class TruckFormView { prevButton.addEventListener('click', () => this.handlePrevious()); this.formContainer.appendChild(prevButton); } + + this.formContainer.appendChild(this.messageContainer); + this.showMessage(''); } handleNext(input) { const value = input.value.trim(); if (!value) { - alert("Vul het veld in."); + this.showMessage("Vul het veld in."); return; } if ((input.id === 'truck-length' || input.id === 'truck-width') && (isNaN(value) || value < 2 || value > 20)) { - alert("Voer een geldig getal in, tussen de 2 en de 20."); + this.showMessage("Voer een geldig getal in, tussen de 2 en de 20."); return; } @@ -96,6 +121,10 @@ export default class TruckFormView { this.formContainer.remove(); } + showMessage(message) { + this.messageContainer.innerText = message; + } + getFormContainer() { return this.formContainer; } diff --git a/src/view/truckView.js b/src/view/truckView.js index 4cb5b08..3af8d2e 100644 --- a/src/view/truckView.js +++ b/src/view/truckView.js @@ -18,10 +18,8 @@ export default class TruckView { console.log('rendering truck'); this.container.innerHTML = ''; - // Set the data-truck-index attribute this.container.setAttribute('data-truck-index', this.truck.index); - // Create truck grid const grid = document.createElement('div'); grid.style.display = 'grid'; grid.style.gridTemplateColumns = `repeat(${this.truck.width}, 20px)`; diff --git a/src/view/weatherFormView.js b/src/view/weatherFormView.js new file mode 100644 index 0000000..f1e499f --- /dev/null +++ b/src/view/weatherFormView.js @@ -0,0 +1,44 @@ +export default class WeatherFormView { + constructor() { + this.weatherContainer = document.createElement('div'); + this.weatherContainer.id = 'weather-container'; + + const label = document.createElement('label'); + label.textContent = 'Voer de stad/dorp in voor het weer:'; + + this.input = document.createElement('input'); + this.input.id = 'weather-city'; + + this.submitButton = document.createElement('button'); + this.submitButton.textContent = 'Versturen'; + + this.messageContainer = document.createElement('div'); + this.messageContainer.id = 'weather-message'; + this.messageContainer.style.color = 'red'; + + this.weatherInfoContainer = document.createElement('div'); + this.weatherInfoContainer.id = 'weather-info-container'; + + this.weatherContainer.appendChild(label); + this.weatherContainer.appendChild(this.input); + this.weatherContainer.appendChild(this.submitButton); + this.weatherContainer.appendChild(this.messageContainer); + this.weatherContainer.appendChild(this.weatherInfoContainer); + } + + showMessage(message) { + this.messageContainer.innerText = message; + } + + clearMessage() { + this.messageContainer.innerText = ''; + } + + getWeatherContainer() { + return this.weatherContainer; + } + + getWeatherInfoContainer() { + return this.weatherInfoContainer; + } +} \ No newline at end of file