diff options
| author | Petri Hienonen <petri.hienonen@gmail.com> | 2025-11-14 21:39:29 +0200 |
|---|---|---|
| committer | Petri Hienonen <petri.hienonen@gmail.com> | 2025-11-15 14:03:04 +0200 |
| commit | 64acc82b9634d948517ec5bb2ebe5a33cdf22df6 (patch) | |
| tree | 0c82b618fa398caa2abcebeb573ac85ba29be3ef /app/main.js | |
| parent | 55085dae685305d24c29b60b1c16fc7dc76831af (diff) | |
| download | housing-64acc82b9634d948517ec5bb2ebe5a33cdf22df6.tar.zst | |
Cleanup
Diffstat (limited to 'app/main.js')
| -rw-r--r-- | app/main.js | 307 |
1 files changed, 89 insertions, 218 deletions
diff --git a/app/main.js b/app/main.js index d7fb1b2..6e862c0 100644 --- a/app/main.js +++ b/app/main.js @@ -82,22 +82,14 @@ export class Init { }), ); - this.#showLoadingScreen(); - this.#initialize(); - } - - /** - * Show loading screen - */ - #showLoadingScreen() { document.body.appendChild(this.#loadingElement); - - // Set basic body styles Object.assign(document.body.style, { fontFamily: "Roboto Mono, monospace", margin: "0", padding: "0", }); + + this.#initialize(); } /** @@ -108,17 +100,16 @@ export class Init { // Load collection data this.#collection = await Collection.get(); - // Pre-calculate scores for all houses - this.#precalculateScores(); + const weights = new Weights(); // Default weights for initial calculation + this.#collection.houses.forEach((house) => { + house.scores.current = Math.round(ScoringEngine.calculate(house, weights)); + house.value = house.scores.current; + }); - // Initialize filters with actual data ranges - const filters = this.#initializeFilters(); + const filters = new Filters(this.#collection.houses); - // Apply initial filtering - const filteredHouses = this.#applyInitialFilters(filters); - - // Create app with fully initialized data - this.#createApp(filters, filteredHouses); + this.#loadingElement.remove(); + new App(this.#collection, filters); } catch (error) { console.error("Initialization failed:", error); this.#showError("Failed to load application data. Please refresh the page."); @@ -126,67 +117,10 @@ export class Init { } /** - * Pre-calculate scores for all houses - */ - #precalculateScores() { - if (!this.#collection) return; - - const weights = new Weights(); // Default weights for initial calculation - - this.#collection.houses.forEach((house) => { - house.scores.current = Math.round(ScoringEngine.calculate(house, weights)); - house.value = house.scores.current; - }); - } - - /** - * Initialize filters with data ranges - * @returns {Filters} - */ - #initializeFilters() { - const filters = new Filters(); - - if (this.#collection) { - filters.updateRanges(this.#collection.houses); - } - - return filters; - } - - /** - * Apply initial filters - * @param {Filters} filters - * @returns {House[]} - */ - #applyInitialFilters(filters) { - if (!this.#collection) return []; - - return this.#collection.houses.filter((house) => house.matchesFilters(filters)); - } - - /** - * Create the main application - * @param {Filters} filters - * @param {House[]} filteredHouses - */ - #createApp(filters, filteredHouses) { - if (!this.#collection) return; - - // Remove loading screen - this.#loadingElement.remove(); - - // Create app with fully initialized data - new App(this.#collection, filters, filteredHouses); - } - - /** - * Show error message * @param {string} message */ - #showError(message) { - this.#loadingElement.remove(); - - const errorElement = Dom.div( + static getError(message) { + return Dom.div( new DomOptions({ children: [ Dom.div( @@ -266,7 +200,14 @@ export class Init { }, }), ); + } + /** + * @param {string} message + */ + #showError(message) { + this.#loadingElement.remove(); + const errorElement = Init.getError(message); document.body.appendChild(errorElement); } } @@ -274,8 +215,6 @@ export class Init { export class App { /** @type {Collection} */ #collection; - /** @type {House[]} */ - #filtered; /** @type {Filters} */ #filters; /** @type {Weights} */ @@ -296,48 +235,11 @@ export class App { /** * @param {Collection} collection * @param {Filters} filters - * @param {House[]} filteredHouses */ - constructor(collection, filters, filteredHouses) { + constructor(collection, filters) { this.#collection = collection; this.#filters = filters; - this.#filtered = filteredHouses; - this.#setupLayout(); - - this.#sidebar = new Sidebar( - this.#collection.houses, - this.#filters, - this.#weights, - () => this.#applyFiltersAndScoring(), - (key, value) => this.#handleWeightChange(key, value), - (param) => this.#handleHouseColorChange(param), - (param) => this.#handleAreaColorChange(param), - ); - - // Create map - this.#map = new MapEl({ - onHouseClick: (houseId, persistent) => this.#showHouseModal(houseId, persistent), - onHouseHover: (houseId, hide) => this.#handleHouseHover(houseId, hide), - }); - - // Initialize map with data - this.#map.initialize(this.#collection, this.#houseParameter, this.#areaParameter); - - // Create stats display - this.#stats = this.#createStats(); - - // Assemble the main UI - this.#renderUI(); - - // Update sidebar with current state - this.#sidebar.update(this.#filtered, this.#houseParameter); - } - - /** - * Set up the main application layout - */ - #setupLayout() { Object.assign(document.body.style, { display: "flex", flexDirection: "column", @@ -345,12 +247,55 @@ export class App { height: "100vh", margin: "0", }); - } - /** - * Render the main UI - */ - #renderUI() { + this.#sidebar = new Sidebar({ + allHouses: this.#collection.houses, + areaParam: this.#areaParameter, + filters: this.#filters, + houseParam: this.#houseParameter, + onAreaParamChange: (param) => { + this.#areaParameter = param; + this.#map.updateArea(this.#areaParameter); + }, + onFilterChange: () => { + this.#map.updateHouseVisibility(this.#filters); + + const stats = App.#createStats(this.#collection.houses, this.#filters); + this.#stats.replaceWith(stats); + this.#stats = stats; + }, + onHouseParamChange: (param) => { + this.#houseParameter = param; + this.#map.updateHousesParameter(this.#houseParameter); + }, + onWeightChange: (key, value) => { + if (key in this.#weights) { + this.#weights[/** @type {keyof Weights} */ (key)] = value; + } + + for (const h of this.#collection.houses) { + h.scores.current = Math.round(ScoringEngine.calculate(h, this.#weights)); + h.value = h.scores.current; + } + const stats = App.#createStats(this.#collection.houses, this.#filters); + this.#stats.replaceWith(stats); + this.#stats = stats; + }, + weights: this.#weights, + }); + + this.#map = new MapEl({ + areaParameter: this.#areaParameter, + collection: this.#collection, + houseParameter: this.#houseParameter, + onHouseClick: (houseId, persistent) => this.#showHouseModal(houseId, persistent), + onHouseHover: (houseId, hide) => { + hide ? this.#modal?.hide() : this.#showHouseModal(houseId, false); + }, + }); + + this.#stats = App.#createStats(this.#collection.houses, this.#filters); + document.body.appendChild( Dom.div( new DomOptions({ @@ -381,50 +326,6 @@ export class App { } /** - * Handle weight changes - * @param {string} key - * @param {number} value - */ - #handleWeightChange(key, value) { - if (key in this.#weights) { - this.#weights[/** @type {keyof Weights} */ (key)] = value; - } - this.#applyFiltersAndScoring(); - } - - /** - * Handle house color parameter change - * @param {string} param - */ - #handleHouseColorChange(param) { - this.#houseParameter = param; - this.#map.updateHousesColor(this.#houseParameter); - this.#sidebar.update(this.#filtered, this.#houseParameter); - } - - /** - * Handle area color parameter change - * @param {string} param - */ - #handleAreaColorChange(param) { - this.#areaParameter = param; - this.#map.updateArea(this.#areaParameter); - } - - /** - * Handle house hover events - * @param {string} houseId - * @param {boolean} hide - */ - #handleHouseHover(houseId, hide) { - if (hide) { - this.#modal?.hide(); - } else { - this.#showHouseModal(houseId, false); - } - } - - /** * Show modal with house details * @param {string} houseId * @param {boolean} persistent @@ -434,14 +335,20 @@ export class App { if (!house) return; this.#map.setModalPersistence(persistent); - - // Hide existing modal this.#modal?.hide(); - this.#modal = new Modal( - house, - persistent, - { + this.#modal = new Modal({ + house: house, + onClearMapTimer: () => { + this.#map.clearModalTimer(); + }, + onHide: () => { + this.#modal = null; + this.#map.setModalPersistence(false); + this.#map.clearModalTimer(); + }, + persistent: persistent, + positionStyles: { left: "auto", maxHeight: "80vh", maxWidth: "400px", @@ -450,15 +357,7 @@ export class App { transform: "translateY(-50%)", width: "90%", }, - () => { - this.#modal = null; - this.#map.setModalPersistence(false); - this.#map.clearModalTimer(); - }, - () => { - this.#map.clearModalTimer(); - }, - ); + }); document.body.appendChild(this.#modal.render()); this.#modal.show(); @@ -467,52 +366,24 @@ export class App { /** * Apply filters and recalculate scores */ - #applyFiltersAndScoring() { - // Recalculate all scores with current weights - App.#recalculateScores(this.#collection.houses, this.#weights); - - // Apply filters - this.#filtered = this.#collection.houses.filter((h) => h.matchesFilters(this.#filters)); - - // Update map with filtered houses and new scores - const filteredIds = this.#filtered.map((h) => h.id); - this.#map.updateHouseVisibility(filteredIds); - this.#map.updateHousesColor(this.#houseParameter); - - // Update statistics - const stats = this.#createStats(); - this.#stats.replaceWith(stats); - this.#stats = stats; - - // Update sidebar - this.#sidebar.update(this.#filtered, this.#houseParameter); - } - - /** - * Recalculate scores statically - * @param {House[]} houses - * @param {Weights} weights - */ - static #recalculateScores(houses, weights) { - for (const h of houses) { - h.scores.current = Math.round(ScoringEngine.calculate(h, weights)); - h.value = h.scores.current; - } - } + #applyFiltersAndScoring() {} /** * Create statistics display + * @param {House[]} houses + * @param {Filters} filters * @returns {HTMLElement} */ - #createStats() { - const averageScore = this.#filtered.length - ? Math.round(this.#filtered.reduce((s, h) => s + h.scores.current, 0) / this.#filtered.length) + static #createStats(houses, filters) { + const filtered = houses.filter((h) => h.matchesFilters(filters)); + const averageScore = filtered.length + ? Math.round(filtered.reduce((s, h) => s + h.scores.current, 0) / filtered.length) : 0; return Dom.div( new DomOptions({ children: [ - Dom.strong(this.#filtered.length.toString()), + Dom.strong(`${filtered.length.toString()}/${houses.length}`), Dom.span(" houses shown • Average score: "), Dom.strong(averageScore.toString()), Dom.span(" • Use weights sliders to adjust scoring"), |
