aboutsummaryrefslogtreecommitdiffstats
path: root/app/main.js
diff options
context:
space:
mode:
authorPetri Hienonen <petri.hienonen@gmail.com>2025-11-14 21:39:29 +0200
committerPetri Hienonen <petri.hienonen@gmail.com>2025-11-15 14:03:04 +0200
commit64acc82b9634d948517ec5bb2ebe5a33cdf22df6 (patch)
tree0c82b618fa398caa2abcebeb573ac85ba29be3ef /app/main.js
parent55085dae685305d24c29b60b1c16fc7dc76831af (diff)
downloadhousing-64acc82b9634d948517ec5bb2ebe5a33cdf22df6.tar.zst
Cleanup
Diffstat (limited to 'app/main.js')
-rw-r--r--app/main.js307
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"),