From 60793e09bf3e587fbbdf4df1a9bcecd4be7f9740 Mon Sep 17 00:00:00 2001 From: CoderTobi <77673526+CoderTobi@users.noreply.github.com> Date: Tue, 2 Sep 2025 21:45:54 +0200 Subject: [PATCH 01/27] #63 added simple confirm dialog before plant deletion --- frontend/public/js/detailseite_pflanze.mjs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/frontend/public/js/detailseite_pflanze.mjs b/frontend/public/js/detailseite_pflanze.mjs index 47c5fb5..7851442 100644 --- a/frontend/public/js/detailseite_pflanze.mjs +++ b/frontend/public/js/detailseite_pflanze.mjs @@ -128,6 +128,11 @@ async function onButtonRepotPlantClick(plant) { } async function onButtonDeletePlantClick(plant_id) { + if(!confirm("Willst du die Pflanze wirklich löschen?")) + { + return; + } + try { await backend.deletePlant(plant_id); } catch (error) { From 82706fbe0f800ffac4e6ea4aa45ce743e648a95d Mon Sep 17 00:00:00 2001 From: CoderTobi <77673526+CoderTobi@users.noreply.github.com> Date: Fri, 5 Sep 2025 22:39:59 +0200 Subject: [PATCH 02/27] Updated Backend version and fixed typo --- backend/package.json | 2 +- backend/server.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/package.json b/backend/package.json index 4525d30..1ca65c6 100644 --- a/backend/package.json +++ b/backend/package.json @@ -1,6 +1,6 @@ { "name": "plant_manager", - "version": "0.1.0", + "version": "1.1.0", "description": "Backend for PlantManager with fileupload and sqlite", "main": "server.js", "scripts": { diff --git a/backend/server.js b/backend/server.js index a5f6333..df61ab9 100644 --- a/backend/server.js +++ b/backend/server.js @@ -73,7 +73,7 @@ try // ====== BINDING ENDPOINTS ====== const TOPLEVELPATH = '/api'; let serviceRouter; - console.log('Binding enpoints, top level Path at ' + TOPLEVELPATH); + console.log('Binding endpoints, top level Path at ' + TOPLEVELPATH); serviceRouter = require('./services/plants.js'); app.use(TOPLEVELPATH, serviceRouter); From f516ce40d065cdae50cb0b2a5d557f1d106bd8c3 Mon Sep 17 00:00:00 2001 From: CoderTobi <77673526+CoderTobi@users.noreply.github.com> Date: Fri, 5 Sep 2025 22:41:00 +0200 Subject: [PATCH 03/27] #63 added new column to DB --- backend/dbMigrationTool.js | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/backend/dbMigrationTool.js b/backend/dbMigrationTool.js index 76b7995..66dce3c 100644 --- a/backend/dbMigrationTool.js +++ b/backend/dbMigrationTool.js @@ -4,7 +4,20 @@ * @returns {boolean} false if the DB is fine and true if it needs to be migrated */ module.exports.dbNeedsMigration = function(dbConnection) { - // TODO implement this when necessary + // ============== Check requirement for v1.0.1 ============== + const columnExists = dbConnection.prepare(` + PRAGMA table_info(plants) + `).all().some(col => col.name === 'composted'); + + if(!columnExists) + { + return true; + } + + // ============== Check requirement for v.. ============== + // ... + + // All good return false; } @@ -13,5 +26,21 @@ module.exports.dbNeedsMigration = function(dbConnection) { * @param {*} dbConnection the connection to the DB */ module.exports.migrateDB = function(dbConnection) { - // TODO implement this when necessary + // ============== Upgrade from v1.0.0 to v1.1.0 ============== + // Add 'composted' column to 'plants' table if it does not exist + const columnExists = dbConnection.prepare(` + PRAGMA table_info(plants) + `).all().some(col => col.name === 'composted'); + + if (!columnExists) { + dbConnection.prepare(` + ALTER TABLE plants ADD COLUMN composted DATE DEFAULT NULL + `).run(); + console.log("Column 'composted' added to 'plants' table."); + } else { + console.log("Column 'composted' already exists in 'plants' table."); + } + + // ============== Upgrade from v1.0.0 to v.. ============== + // ... } \ No newline at end of file From 21dc832c08a84f8534f28e327145d9fb3c5476f4 Mon Sep 17 00:00:00 2001 From: CoderTobi <77673526+CoderTobi@users.noreply.github.com> Date: Sat, 6 Sep 2025 00:08:38 +0200 Subject: [PATCH 04/27] #63 updated API --- docs/API.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/docs/API.md b/docs/API.md index 3425f61..bddc23f 100644 --- a/docs/API.md +++ b/docs/API.md @@ -110,7 +110,8 @@ Beispiel: "watering_interval_calculated": 11, "days_since_watering": 10, "days_until_watering": 1, - "repotted": "2023-03-14" + "repotted": "2023-03-14", + "composted": null } ``` ### Attribute @@ -178,6 +179,12 @@ Beispiel: - Durch Backend berechneter Wert - Datum des letzten Umtopfen - Wenn noch nie umgetopft Datum des hinzufügens +- composted + - Typ: Text(Datum) + - Pflichtfeld bei: nie + - Nullbar: ja + - Datum des soften-löschens + - Wenn die Pflanze noch nicht soft-gelöscht wurde null ## Activities - GET From 406ec40b58f26f907e9a5aacd2cc15581ba264c6 Mon Sep 17 00:00:00 2001 From: CoderTobi <77673526+CoderTobi@users.noreply.github.com> Date: Sat, 6 Sep 2025 16:15:19 +0200 Subject: [PATCH 05/27] Added type impots for dbConnection in the backend and removed unused function --- backend/dao/activitiesDao.js | 7 +++---- backend/dao/plantsDao.js | 11 +++++++---- backend/dao/templateDao.js | 7 +++---- backend/dbMigrationTool.js | 4 ++-- 4 files changed, 15 insertions(+), 14 deletions(-) diff --git a/backend/dao/activitiesDao.js b/backend/dao/activitiesDao.js index c63cee1..76e2740 100644 --- a/backend/dao/activitiesDao.js +++ b/backend/dao/activitiesDao.js @@ -4,14 +4,13 @@ const daoHelper = require('./daoHelper.js'); class activitiesDao { + /** + * @param {import('better-sqlite3').Database} dbConnection - The database connection + */ constructor(dbConnection) { this._conn = dbConnection; } - getConnection() { - return this._conn; - } - loadById(id) { var sql = 'SELECT * FROM activities WHERE id=? ORDER BY date DESC'; var statement = this._conn.prepare(sql); diff --git a/backend/dao/plantsDao.js b/backend/dao/plantsDao.js index b3009eb..fccbead 100644 --- a/backend/dao/plantsDao.js +++ b/backend/dao/plantsDao.js @@ -2,16 +2,19 @@ const helper = require('../helper.js'); const daoHelper = require('./daoHelper.js'); + +/** + * Data Access Object for plants + */ class plantsDao { + /** + * @param {import('better-sqlite3').Database} dbConnection - The database connection + */ constructor(dbConnection) { this._conn = dbConnection; } - getConnection() { - return this._conn; - } - loadById(plant_id) { var sql = 'SELECT * FROM plants WHERE plant_id=?'; var statement = this._conn.prepare(sql); diff --git a/backend/dao/templateDao.js b/backend/dao/templateDao.js index 53ee64b..5024c8b 100644 --- a/backend/dao/templateDao.js +++ b/backend/dao/templateDao.js @@ -5,14 +5,13 @@ const helper = require('../helper.js'); class templateDao { + /** + * @param {import('better-sqlite3').Database} dbConnection - The database connection + */ constructor(dbConnection) { this._conn = dbConnection; } - getConnection() { - return this._conn; - } - loadById(id) { // Code goes here diff --git a/backend/dbMigrationTool.js b/backend/dbMigrationTool.js index 66dce3c..defa4c0 100644 --- a/backend/dbMigrationTool.js +++ b/backend/dbMigrationTool.js @@ -1,6 +1,6 @@ /** * Checks if the DB is from an earlier version and needs to be upgraded - * @param {*} dbConnection the connection to the DB + * @param {import('better-sqlite3').Database} dbConnection the connection to the DB * @returns {boolean} false if the DB is fine and true if it needs to be migrated */ module.exports.dbNeedsMigration = function(dbConnection) { @@ -23,7 +23,7 @@ module.exports.dbNeedsMigration = function(dbConnection) { /** * Migrates the DB to the current schema - * @param {*} dbConnection the connection to the DB + * @param {import('better-sqlite3').Database} dbConnection the connection to the DB */ module.exports.migrateDB = function(dbConnection) { // ============== Upgrade from v1.0.0 to v1.1.0 ============== From 5d43e6e9b1f3388dc1a1da328160dd6c87ebf8be Mon Sep 17 00:00:00 2001 From: CoderTobi <77673526+CoderTobi@users.noreply.github.com> Date: Sat, 6 Sep 2025 16:24:49 +0200 Subject: [PATCH 06/27] #63 updated API further --- docs/API.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/API.md b/docs/API.md index bddc23f..792abc5 100644 --- a/docs/API.md +++ b/docs/API.md @@ -79,6 +79,11 @@ Alle HTTP Aufrufe sind englisch und kleingeschrieben! - GET - `…/api/plants/all` - Liefert alle JSON Objekte vom Typ Plant + - Pflanzen die kompostiert wurden werden nicht berücksichtigt +- GET + - `…/api/plants/composted` + - Liefert alle JSON Objekte vom Typ Plant + - Nur Pflanzen die kompostiert wurden werden berücksichtigt - GET - `…/api/plants/exists/[id]` - Prüft nach, ob ein Objekt vom Typ Plant unter dieser [id] existiert From e5b06c4f4128cb2d7396aea632ee5ea90cf617e9 Mon Sep 17 00:00:00 2001 From: CoderTobi <77673526+CoderTobi@users.noreply.github.com> Date: Sat, 6 Sep 2025 16:35:24 +0200 Subject: [PATCH 07/27] #63 implemented API changes into DAO --- backend/dao/plantsDao.js | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/backend/dao/plantsDao.js b/backend/dao/plantsDao.js index fccbead..007d96a 100644 --- a/backend/dao/plantsDao.js +++ b/backend/dao/plantsDao.js @@ -27,8 +27,25 @@ class plantsDao { return result; } + /** + * Loads all plants from the DB + * Ignores plants that have been composted + * @returns {json[]} + */ loadAll() { - var sql = 'SELECT * from plants order by name'; + var sql = 'SELECT * from plants order by name WHERE composted IS NULL'; + var statement = this._conn.prepare(sql); + var result = statement.all(); + var arrayResult = daoHelper.guaranteeArray(result); + return arrayResult; + } + + /** + * Loads all composted plants from the DB + * @returns {json[]} + */ + loadAllComposted() { + var sql = 'SELECT * from plants order by name WHERE composted IS NOT NULL'; var statement = this._conn.prepare(sql); var result = statement.all(); var arrayResult = daoHelper.guaranteeArray(result); @@ -61,10 +78,10 @@ class plantsDao { return this.loadById(result.lastInsertRowid); } - update(plant_id, name, species_name, image, watering_interval, watering_interval_offset) { - var sql = 'UPDATE plants SET name=?, species_name=?, image=?, watering_interval=?, watering_interval_offset=? WHERE plant_id=?'; + update(plant_id, name, species_name, image, watering_interval, watering_interval_offset, composted) { + var sql = 'UPDATE plants SET name=?, species_name=?, image=?, watering_interval=?, watering_interval_offset=?, composted=? WHERE plant_id=?'; var statement = this._conn.prepare(sql); - var params = [name, species_name, image, watering_interval, watering_interval_offset, plant_id]; + var params = [name, species_name, image, watering_interval, watering_interval_offset, composted, plant_id]; var result = statement.run(params); if (result.changes != 1){ From b491163f622a232225c507997aa07b0ca13ca13b Mon Sep 17 00:00:00 2001 From: CoderTobi <77673526+CoderTobi@users.noreply.github.com> Date: Sat, 6 Sep 2025 16:58:33 +0200 Subject: [PATCH 08/27] #63 implemented API changes into plants service --- backend/services/plants.js | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/backend/services/plants.js b/backend/services/plants.js index 567e03c..5bf0686 100644 --- a/backend/services/plants.js +++ b/backend/services/plants.js @@ -104,6 +104,26 @@ serviceRouter.get('/plants/get/:id', function(request, response) { } }); +serviceRouter.get('/plants/composted', function(request, response) { + console.log('Service plants: Client requested all records'); + + const plantDaoInstance = new plantsDao(request.app.locals.dbConnection); + const activitiesDaoInstance = new activitiesDao(request.app.locals.dbConnection); + try { + var plantArr = plantDaoInstance.loadAllComposted(); + // foreach Schleife über alle plant JSON, diese werden dabei erweitert + plantArr.forEach(plant => { + extendPlantJSON(plant,activitiesDaoInstance); + }); + + console.log('Service plants: Records loaded, count= ' + plantArr.length); + response.status(200).json(plantArr); + } catch (ex) { + console.error('Service plants: Error loading all records. Exception occured: ' + ex.message); + response.status(400).json({ 'fehler': true, 'nachricht': ex.message }); + } +}); + serviceRouter.get('/plants/all', function(request, response) { console.log('Service plants: Client requested all records'); @@ -185,6 +205,7 @@ serviceRouter.post('/plants', function(request, response) { serviceRouter.put('/plants', function(request, response) { console.log('Service plants: Client requested update of existing plant'); + // TODO Replace this madness with a validation Framework like express validator const plantDaoInstance = new plantsDao(request.app.locals.dbConnection); var errorMsgs=[]; if (helper.isUndefined(request.body.plant_id)) { @@ -194,29 +215,35 @@ serviceRouter.put('/plants', function(request, response) { } else if (request.body.plant_id <= 0) { errorMsgs.push('plant_id has to be a bigger number than 0'); } + if (helper.isUndefined(request.body.name)) { errorMsgs.push('name missing'); - } + } + if (helper.isUndefined(request.body.species_name)) { errorMsgs.push('species_name missing'); } + if (helper.isUndefined(request.body.watering_interval)) { errorMsgs.push('watering_interval missing'); } else if (!helper.isNumeric(request.body.watering_interval)) { errorMsgs.push('watering_interval has to be a number'); } else if (request.body.watering_interval <= 0) { - errorMsgs.push('watering_interval has to be a bigger number than 0'); + errorMsgs.push('watering_interval has to be a number bigger than 0'); } + if (helper.isUndefined(request.body.watering_interval_offset)) { errorMsgs.push('watering_interval_offset missing'); } else if (!helper.isNumeric(request.body.watering_interval_offset)) { errorMsgs.push('watering_interval has to be a number'); } + if (errorMsgs.length > 0) { console.log('Service plants: Update not possible, data missing or invalid'); response.status(400).json({ 'fehler': true, 'nachricht': 'Function not possible. Missing or invalid data: ' + helper.concatArray(errorMsgs) }); return; } + try { // check if the plant even exists if(!plantDaoInstance.exists(request.body.plant_id)) @@ -230,7 +257,7 @@ serviceRouter.put('/plants', function(request, response) { let image = plantDaoInstance.loadById(request.body.plant_id).image; // update the plant - var obj = plantDaoInstance.update(request.body.plant_id,request.body.name, request.body.species_name,image,request.body.watering_interval,request.body.watering_interval_offset); + var obj = plantDaoInstance.update(request.body.plant_id,request.body.name, request.body.species_name,image,request.body.watering_interval,request.body.watering_interval_offset,request.body.composted); console.log('Service plants: Record updated, plant_id=' + request.body.plant_id); response.status(200).json(obj); } catch (ex) { From 67aab81dbb34510a00a44740fa6b24c57a9657d9 Mon Sep 17 00:00:00 2001 From: CoderTobi <77673526+CoderTobi@users.noreply.github.com> Date: Sat, 6 Sep 2025 17:49:41 +0200 Subject: [PATCH 09/27] #63 tested backend changes and fixed SQL query --- backend/dao/plantsDao.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/dao/plantsDao.js b/backend/dao/plantsDao.js index 007d96a..6e7692a 100644 --- a/backend/dao/plantsDao.js +++ b/backend/dao/plantsDao.js @@ -33,7 +33,7 @@ class plantsDao { * @returns {json[]} */ loadAll() { - var sql = 'SELECT * from plants order by name WHERE composted IS NULL'; + var sql = 'SELECT * FROM plants WHERE composted IS NULL ORDER BY name'; var statement = this._conn.prepare(sql); var result = statement.all(); var arrayResult = daoHelper.guaranteeArray(result); @@ -45,7 +45,7 @@ class plantsDao { * @returns {json[]} */ loadAllComposted() { - var sql = 'SELECT * from plants order by name WHERE composted IS NOT NULL'; + var sql = 'SELECT * FROM plants WHERE composted IS NOT NULL ORDER BY name'; var statement = this._conn.prepare(sql); var result = statement.all(); var arrayResult = daoHelper.guaranteeArray(result); From 23340006e9847d441c81fcb1c08914ca64acb29d Mon Sep 17 00:00:00 2001 From: CoderTobi <77673526+CoderTobi@users.noreply.github.com> Date: Sat, 6 Sep 2025 23:02:57 +0200 Subject: [PATCH 10/27] changed activity functions to only require plant_id --- frontend/public/js/detailseite_pflanze.mjs | 4 ++-- frontend/public/js/index.mjs | 2 +- frontend/public/js/meine_pflanzen.mjs | 4 ++-- frontend/public/mjs/backend_api.mjs | 20 ++++++++++---------- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/frontend/public/js/detailseite_pflanze.mjs b/frontend/public/js/detailseite_pflanze.mjs index 7851442..045db6a 100644 --- a/frontend/public/js/detailseite_pflanze.mjs +++ b/frontend/public/js/detailseite_pflanze.mjs @@ -101,7 +101,7 @@ function createActivityCard(type, date, days_since) { async function onButtonWaterPlantClick(plant) { // call Backend try{ - await backend.waterPlant(plant); + await backend.waterPlant(plant.plant_id); } catch(e) { @@ -115,7 +115,7 @@ async function onButtonWaterPlantClick(plant) { async function onButtonRepotPlantClick(plant) { // call Backend try{ - await backend.repotPlant(plant); + await backend.repotPlant(plant.plant_id); } catch(e) { diff --git a/frontend/public/js/index.mjs b/frontend/public/js/index.mjs index fc090d6..975cf0c 100644 --- a/frontend/public/js/index.mjs +++ b/frontend/public/js/index.mjs @@ -165,7 +165,7 @@ async function buttonWaterClick(plant) { // call Backend try{ - await backend.waterPlant(plant); + await backend.waterPlant(plant.plant_id); } catch(e) { diff --git a/frontend/public/js/meine_pflanzen.mjs b/frontend/public/js/meine_pflanzen.mjs index e976d34..464b77c 100644 --- a/frontend/public/js/meine_pflanzen.mjs +++ b/frontend/public/js/meine_pflanzen.mjs @@ -97,7 +97,7 @@ function createPlantCard(plant) {
`); cardFooter.append(buttonWater); buttonWater.on("click", () => { - // needed to be wraped in this anonymus function + // needed to be wrapped in this anonymous function buttonWaterClick(plant); }); let buttonDetails = $(`
diff --git a/frontend/public/js/detailseite_pflanze.mjs b/frontend/public/js/detailseite_pflanze.mjs index b425c57..e51a57c 100644 --- a/frontend/public/js/detailseite_pflanze.mjs +++ b/frontend/public/js/detailseite_pflanze.mjs @@ -128,7 +128,7 @@ async function onButtonRepotPlantClick(plant) { } async function onButtonDeletePlantClick(plant) { - if(!confirm("Willst du die Pflanze wirklich löschen?")) + if(!confirm("Willst du die Pflanze wirklich kompostieren?")) { return; } diff --git a/frontend_static/detailseite_pflanze.html b/frontend_static/detailseite_pflanze.html index 7ace025..64eb05b 100644 --- a/frontend_static/detailseite_pflanze.html +++ b/frontend_static/detailseite_pflanze.html @@ -55,7 +55,7 @@
Pflanziska
From 41c8c590dd3c217d75ac264c72b19284041ec695 Mon Sep 17 00:00:00 2001 From: CoderTobi <77673526+CoderTobi@users.noreply.github.com> Date: Sun, 7 Sep 2025 00:56:32 +0200 Subject: [PATCH 14/27] #63 Compost page design --- frontend_static/css/style.css | 4 + frontend_static/kompostierte_pflanzen.html | 143 +++++++++++++++++++++ 2 files changed, 147 insertions(+) create mode 100644 frontend_static/kompostierte_pflanzen.html diff --git a/frontend_static/css/style.css b/frontend_static/css/style.css index 8774651..7f35275 100644 --- a/frontend_static/css/style.css +++ b/frontend_static/css/style.css @@ -91,6 +91,10 @@ main { background-color: white !important; } +.list-group-item { + background-color: white !important; +} + .text-water { color: var(--qd-color-water); } diff --git a/frontend_static/kompostierte_pflanzen.html b/frontend_static/kompostierte_pflanzen.html new file mode 100644 index 0000000..a7aec30 --- /dev/null +++ b/frontend_static/kompostierte_pflanzen.html @@ -0,0 +1,143 @@ + + + + + + + + + Plant Manager - Kompost + + + + + + + + + + + + + + + + + + + +
+ + + +
+ +
+ + +
    +
  • +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
  • +
  • +
    +
    + +
    +
    + +
    +
    + +
    +
    +
  • +
  • +
    +
    + +
    +
    + +
    +
    + +
    +
    +
  • +
+ +
+ +
+
+
+ +

Kompost

+ +

+ Die hier aufgeführten Pflanzen wurden von dir auf den Kompost geworfen. + Du kannst Sie entweder endgültig löschen oder wiederherstellen. +

+ + + + +
+
+
+ +
+ +
+ + +
+ + + + + + \ No newline at end of file From dda3921573ee36953bf77032e4cbc84bfdfa27be Mon Sep 17 00:00:00 2001 From: CoderTobi <77673526+CoderTobi@users.noreply.github.com> Date: Sun, 7 Sep 2025 13:47:36 +0200 Subject: [PATCH 15/27] Fixed Typo on details page --- frontend/public/detailseite_pflanze.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/public/detailseite_pflanze.html b/frontend/public/detailseite_pflanze.html index c4bb66b..866f392 100644 --- a/frontend/public/detailseite_pflanze.html +++ b/frontend/public/detailseite_pflanze.html @@ -68,7 +68,7 @@
Loading...
- Lade Planze + Lade Pflanze

-

From 59be8bc14882d222a72f28170cfbd3e18688ab14 Mon Sep 17 00:00:00 2001 From: CoderTobi <77673526+CoderTobi@users.noreply.github.com> Date: Sun, 7 Sep 2025 13:48:29 +0200 Subject: [PATCH 16/27] #63 work on compost page implementation --- frontend/public/css/style.css | 4 + frontend/public/js/kompostierte_pflanzen.mjs | 12 ++ frontend/public/kompostierte_pflanzen.html | 109 +++++++++++++++++++ 3 files changed, 125 insertions(+) create mode 100644 frontend/public/js/kompostierte_pflanzen.mjs create mode 100644 frontend/public/kompostierte_pflanzen.html diff --git a/frontend/public/css/style.css b/frontend/public/css/style.css index 8774651..7f35275 100644 --- a/frontend/public/css/style.css +++ b/frontend/public/css/style.css @@ -91,6 +91,10 @@ main { background-color: white !important; } +.list-group-item { + background-color: white !important; +} + .text-water { color: var(--qd-color-water); } diff --git a/frontend/public/js/kompostierte_pflanzen.mjs b/frontend/public/js/kompostierte_pflanzen.mjs new file mode 100644 index 0000000..cab7622 --- /dev/null +++ b/frontend/public/js/kompostierte_pflanzen.mjs @@ -0,0 +1,12 @@ +import * as ui_helper from "../mjs/ui_helpers.mjs"; +import * as navigation from "../mjs/navigation.mjs"; +import * as alerts from "../mjs/alerts.mjs"; +import * as backend from "../mjs/backend_api.mjs"; +import * as error_handler from "../mjs/error_handler.mjs"; + +function init() +{ + alerts.initializeAlertDisplay(); +} + +document.addEventListener('DOMContentLoaded', init); \ No newline at end of file diff --git a/frontend/public/kompostierte_pflanzen.html b/frontend/public/kompostierte_pflanzen.html new file mode 100644 index 0000000..04268b9 --- /dev/null +++ b/frontend/public/kompostierte_pflanzen.html @@ -0,0 +1,109 @@ + + + + + + + + + Plant Manager - Kompost + + + + + + + + + + + + + + + + + + + +
+ + + +
+ +
+ +
    + + + +
  • +
    +
    +
    + Loading... +
    +
    +
    +

    Lade Pflanzen...

    +
    +
    +
  • + +
+ +
+ +
+
+
+ +

Kompost

+ +

+ Die hier aufgeführten Pflanzen wurden von dir auf den Kompost geworfen. + Du kannst Sie entweder endgültig löschen oder wiederherstellen. +

+ + + + +
+
+
+ +
+ +
+ + +
+ + + + + + \ No newline at end of file From 37b6cc7baa07bc24b24e7159c676b7e44c64c3e2 Mon Sep 17 00:00:00 2001 From: CoderTobi <77673526+CoderTobi@users.noreply.github.com> Date: Sun, 7 Sep 2025 18:15:59 +0200 Subject: [PATCH 17/27] Fixed sqlDate conversion function --- frontend/public/mjs/backend_api.mjs | 2 +- frontend/public/mjs/utils.mjs | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/frontend/public/mjs/backend_api.mjs b/frontend/public/mjs/backend_api.mjs index 99d45e7..c86fc1f 100644 --- a/frontend/public/mjs/backend_api.mjs +++ b/frontend/public/mjs/backend_api.mjs @@ -265,7 +265,7 @@ export async function compostPlant(plant_id) { try { let plant = await fetchPlant(plant_id); - plant.composted = utils.convertJSDateSqlDateFormat(new Date()); + plant.composted = utils.convertJSToDateSqlDateFormat(new Date()); await updatePlant(plant); } catch (exception) diff --git a/frontend/public/mjs/utils.mjs b/frontend/public/mjs/utils.mjs index 946e194..1826799 100644 --- a/frontend/public/mjs/utils.mjs +++ b/frontend/public/mjs/utils.mjs @@ -59,7 +59,7 @@ export function convertSqlDateToGermanFormat(sqlDate) { * @param {Date} jsDate JS Date Obj * @returns {string} string in sql format YYYY-MM-DD */ -export function convertJSDateSqlDateFormat(jsDate) { +export function convertJSToDateSqlDateFormat(jsDate) { // JS logic: month 0-11; days 1-31 // see: https://www.w3schools.com/js/js_date_methods.asp @@ -68,8 +68,10 @@ export function convertJSDateSqlDateFormat(jsDate) { monthString = `0${monthString}`; } - let dayString = String(jsDate.getDay()); - if(jsDate.getDay() < 10){ + // I think .getDay() returns the week day? + // So .getDate() does what you would expect .getDay() to do + let dayString = String(jsDate.getDate()); + if(jsDate.getDate() < 10){ dayString = `0${dayString}`; } From 036a1326336dfdb0ebac8c11555a23ffaafee0e0 Mon Sep 17 00:00:00 2001 From: CoderTobi <77673526+CoderTobi@users.noreply.github.com> Date: Sun, 7 Sep 2025 18:26:19 +0200 Subject: [PATCH 18/27] #63 work on compost page implementation --- frontend/public/js/kompostierte_pflanzen.mjs | 82 +++++++++++++++++++- frontend/public/kompostierte_pflanzen.html | 2 +- frontend/public/mjs/backend_api.mjs | 9 ++- frontend_static/kompostierte_pflanzen.html | 2 +- 4 files changed, 88 insertions(+), 7 deletions(-) diff --git a/frontend/public/js/kompostierte_pflanzen.mjs b/frontend/public/js/kompostierte_pflanzen.mjs index cab7622..7ca3269 100644 --- a/frontend/public/js/kompostierte_pflanzen.mjs +++ b/frontend/public/js/kompostierte_pflanzen.mjs @@ -3,10 +3,86 @@ import * as navigation from "../mjs/navigation.mjs"; import * as alerts from "../mjs/alerts.mjs"; import * as backend from "../mjs/backend_api.mjs"; import * as error_handler from "../mjs/error_handler.mjs"; +import * as utils from "../mjs/utils.mjs"; -function init() -{ - alerts.initializeAlertDisplay(); +async function init() { + alerts.initializeAlertDisplay(); + + console.log('Document ready, loading data from Service'); + await reloadPlants(); +} + +async function reloadPlants() { + let plants = null; + try{ + plants = await backend.fetchPlants(true); + } + catch(e) + { + error_handler.handleError(e); + } + displayPlants(plants); +} + +function displayPlants(plants) { + // Clear the existing content + let plantsContainer = $('#plants') + plantsContainer.empty(); + if(plants == null) + { + //TODO ui_helper.createCenteredIconAndText(plantsContainer, "bi-exclamation-triangle", "Es ist ein Fehler aufgetreten") + return; + } + if(plants.length == 0) + { + //TODO ui_helper.createCenteredIconAndText(plantsContainer, "bi-leaf", "Du hast noch keine Pflanzen") + return; + } + + // Loop through the plants and create cards for each + plants.forEach(function (plant) { + let listItem = createPlantListItem(plant); + plantsContainer.append(listItem); + }); +} + +function createPlantListItem(plant) { + let listItem = $('
  • '); + + // Create base structure + let row = $('
    '); + listItem.append(row); + let startCol = $('
    '); + let middleCol = $('
    '); + let endCol = $('
    '); + row.append(startCol); + row.append(middleCol); + row.append(endCol); + + // Fill startCol + let checkBox = $(''); + checkBox.prop("id","checkbox" + plant.plant_id); + startCol.append(checkBox); + + // Fill middleCol + let label = $('
  • '); + listItem.prop("id","li" + plant.plant_id); // Create base structure let row = $('
    '); @@ -60,9 +110,10 @@ function createPlantListItem(plant) { row.append(endCol); // Fill startCol - let checkBox = $(''); - checkBox.prop("id","checkbox" + plant.plant_id); - startCol.append(checkBox); + let checkbox = $(''); + checkbox.prop("id","checkbox" + plant.plant_id); + checkbox.attr("plant_id",plant.plant_id); + startCol.append(checkbox); // Fill middleCol let label = $('
  • From d53689bc8fc1f7a107897b438a06defe0c075736 Mon Sep 17 00:00:00 2001 From: CoderTobi <77673526+CoderTobi@users.noreply.github.com> Date: Mon, 8 Sep 2025 01:02:10 +0200 Subject: [PATCH 21/27] #63 added restore function --- frontend/public/mjs/backend_api.mjs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/frontend/public/mjs/backend_api.mjs b/frontend/public/mjs/backend_api.mjs index c5e10b9..51d7a69 100644 --- a/frontend/public/mjs/backend_api.mjs +++ b/frontend/public/mjs/backend_api.mjs @@ -280,6 +280,26 @@ export async function compostPlant(plant_id) { } } +/** + * async function to restore a plant from being composted on the backend. + * Throws an exception on error. + * + * @param {int} plant_id + */ +export async function restorePlant(plant_id) { + try + { + let plant = await fetchPlant(plant_id); + plant.composted = null; + await updatePlant(plant); + } + catch (exception) + { + console.error("Error restoring plant:", exception); + throw exception; + } +} + /** * async function to update a plant on the backend. * Throws an exception on error. From 78a8a53c5a828eb8a17bf69aab70f0c73901bd41 Mon Sep 17 00:00:00 2001 From: CoderTobi <77673526+CoderTobi@users.noreply.github.com> Date: Mon, 8 Sep 2025 01:38:36 +0200 Subject: [PATCH 22/27] #63 compost page finishing touch - Loading spinner smaller - Dummy list item for empty list - Dummy list item for errors - Disable both buttons --- frontend/public/js/kompostierte_pflanzen.mjs | 54 ++++++++++++++++---- frontend/public/kompostierte_pflanzen.html | 4 +- 2 files changed, 46 insertions(+), 12 deletions(-) diff --git a/frontend/public/js/kompostierte_pflanzen.mjs b/frontend/public/js/kompostierte_pflanzen.mjs index dbca728..7fbee7b 100644 --- a/frontend/public/js/kompostierte_pflanzen.mjs +++ b/frontend/public/js/kompostierte_pflanzen.mjs @@ -22,8 +22,9 @@ function registerEventHandlers() } async function onButtonClick(button) { - // disable button and change appearance - button.disabled = true; + // disable buttons and change appearance of the clicked + document.getElementById("restore-button").disabled = true; + document.getElementById("delete-button").disabled = true; let originalHTML = button.innerHTML; button.innerHTML = '
    Einen Moment...'; @@ -36,14 +37,15 @@ async function onButtonClick(button) { try { if(button.id == "delete-button"){ - backend.deletePlant(plant_id); + await backend.deletePlant(plant_id); } else if (button.id == "restore-button") { - backend.restorePlant(plant_id); + await backend.restorePlant(plant_id); } removeListItem(plant_id); } catch (error) { - // enable button and change appearance - button.disabled = false; + // enable buttons and change appearance back + document.getElementById("restore-button").disabled = false; + document.getElementById("delete-button").disabled = false; button.innerHTML = originalHTML; // deal with the error error_handler.handleError(error); @@ -51,8 +53,17 @@ async function onButtonClick(button) { } } - // enable button and change appearance - button.disabled = false; + // If there are no more list items, create info + let plantsContainer = document.getElementById("plants"); + if (plantsContainer.innerHTML == "") + { + let infoListItem = createInformationalListItem("bi-check2","Es befinden sich keine Pflanzen mehr auf dem Kompost"); + $('#plants').append(infoListItem); + } + + // enable buttons and change appearance back + document.getElementById("restore-button").disabled = false; + document.getElementById("delete-button").disabled = false; button.innerHTML = originalHTML; } @@ -79,12 +90,14 @@ function displayPlants(plants) { if(plants == null) { - //TODO ui_helper.createCenteredIconAndText(plantsContainer, "bi-exclamation-triangle", "Es ist ein Fehler aufgetreten") + let errorListItem = createInformationalListItem("bi-exclamation-triangle-fill","Pflanzen konnten nicht geladen werden"); + plantsContainer.append(errorListItem); return; } if(plants.length == 0) { - //TODO ui_helper.createCenteredIconAndText(plantsContainer, "bi-leaf", "Du hast noch keine Pflanzen") + let errorListItem = createInformationalListItem("bi-check2","Es befinden sich noch keine Pflanzen auf dem Kompost..."); + plantsContainer.append(errorListItem); return; } @@ -136,4 +149,25 @@ function createPlantListItem(plant) { return listItem; } +function createInformationalListItem(icon, text) { + let listItem = $('
  • '); + + // Create base structure + let row = $('
    '); + listItem.append(row); + let startCol = $('
    '); + let mainCol = $('
    '); + row.append(startCol); + row.append(mainCol); + + // Fill startCol + let iconElement = $(''); + startCol.append(iconElement); + + // Fill mainCol + mainCol.text(text); + + return listItem; +} + document.addEventListener('DOMContentLoaded', init); \ No newline at end of file diff --git a/frontend/public/kompostierte_pflanzen.html b/frontend/public/kompostierte_pflanzen.html index 21d295f..370a78d 100644 --- a/frontend/public/kompostierte_pflanzen.html +++ b/frontend/public/kompostierte_pflanzen.html @@ -63,12 +63,12 @@
  • -
    +
    Loading...
    -

    Lade Pflanzen...

    + Lade Pflanzen...
  • From c62c3d16b108bac09c19988a48a363f8d0f27b09 Mon Sep 17 00:00:00 2001 From: CoderTobi <77673526+CoderTobi@users.noreply.github.com> Date: Mon, 8 Sep 2025 16:40:54 +0200 Subject: [PATCH 23/27] Updated alerts system to also display icons --- frontend/public/mjs/alerts.mjs | 43 +++++++++++++++++++++++++--------- 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/frontend/public/mjs/alerts.mjs b/frontend/public/mjs/alerts.mjs index ce15213..76f2cfa 100644 --- a/frontend/public/mjs/alerts.mjs +++ b/frontend/public/mjs/alerts.mjs @@ -13,24 +13,45 @@ export function initializeAlertDisplay() * Displays a Bootstrap alert in the DOM. * Must be called after initializeAlertDisplay(). * - * @param {string} message a string that will be displayed to the user - * @param {string} type a string to change the apperance based on Bootsrap (e.g. danger) + * @param {string} mainMessage a string that will be displayed to the user + * @param {string} type a string to change the appearance based on Bootsrap (e.g. danger) * @param {string} secondaryMessage (optional) additional information that will be displayed to the user + * @param {string} icon (optional) a Bootstrap icon string (e.g. bi-exclamation-triangle-fill) */ -export function displayAlert(mainMessage, type, secondaryMessage) { +export function displayAlert(mainMessage, type, secondaryMessage, icon=null) { + + let alert = $('
    '); + alert.prop('class', 'alert alert-' + type + ' alert-dismissible'); + + // Create base structure + let row = $('
    '); + alert.append(row); + let startCol = $('
    '); + let mainCol = $('
    '); + row.append(startCol); + row.append(mainCol); + + // Create Icon + if (icon) + { + let iconElement = $(''); + startCol.append(iconElement); + } - let message = mainMessage; - // Append secondary message + // Create Content + let div = $('
    '); + mainCol.append(div); + let strong = $(''); + strong.text(mainMessage); + div.append(strong); if(secondaryMessage != null) { - message = "" + message + ""; - message += "
    " + secondaryMessage; + let secondDiv = $('
    '); + div.append(secondDiv); + secondDiv.text(secondaryMessage); } - let alert = $(' diff --git a/frontend/public/dev/index.html b/frontend/public/dev/index.html index ed38cfb..d122a6b 100644 --- a/frontend/public/dev/index.html +++ b/frontend/public/dev/index.html @@ -42,6 +42,9 @@ +
    diff --git a/frontend/public/index.html b/frontend/public/index.html index 4c57687..6115839 100644 --- a/frontend/public/index.html +++ b/frontend/public/index.html @@ -42,6 +42,9 @@ +
    diff --git a/frontend/public/kompostierte_pflanzen.html b/frontend/public/kompostierte_pflanzen.html index 370a78d..5760fea 100644 --- a/frontend/public/kompostierte_pflanzen.html +++ b/frontend/public/kompostierte_pflanzen.html @@ -42,6 +42,9 @@ +
    diff --git a/frontend/public/meine_pflanzen.html b/frontend/public/meine_pflanzen.html index 5ad1314..efb7e19 100644 --- a/frontend/public/meine_pflanzen.html +++ b/frontend/public/meine_pflanzen.html @@ -42,6 +42,9 @@ +
    diff --git a/frontend/public/pflanze_bearbeiten.html b/frontend/public/pflanze_bearbeiten.html index 65c53a8..b00c07a 100644 --- a/frontend/public/pflanze_bearbeiten.html +++ b/frontend/public/pflanze_bearbeiten.html @@ -41,6 +41,9 @@ +
    From 00140a9042734ea6aa8aaf4f756681b4a90f583d Mon Sep 17 00:00:00 2001 From: CoderTobi <77673526+CoderTobi@users.noreply.github.com> Date: Mon, 8 Sep 2025 16:59:14 +0200 Subject: [PATCH 27/27] #63 changed frontend version to v1.1.0 --- frontend/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/package.json b/frontend/package.json index f88f368..bf2cfb4 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "plant_manager_frontend", - "version": "1.0.0", + "version": "1.1.0", "description": "Frontend for PlantManager", "main": "client.js", "scripts": {