diff --git a/backend/database/gardentrack.db b/backend/database/gardentrack.db index fb5fd06..6318f49 100644 Binary files a/backend/database/gardentrack.db and b/backend/database/gardentrack.db differ diff --git a/backend/server.js b/backend/server.js index a04a48e..58f5c3f 100644 --- a/backend/server.js +++ b/backend/server.js @@ -137,6 +137,7 @@ function initializeDatabase() { ) `); console.log('Chemicals table ready'); + // Plant observations table db.exec(` CREATE TABLE IF NOT EXISTS plant_observations ( @@ -156,6 +157,42 @@ function initializeDatabase() { `); console.log('Plant observations table ready'); + // Harvest processing table + db.exec(` + CREATE TABLE IF NOT EXISTS harvest_processing ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + harvest_id INTEGER NOT NULL, + process_type TEXT NOT NULL CHECK(process_type IN ('fresh', 'frozen', 'jam', 'dried', 'canned', 'juice', 'sauce', 'pickled', 'other')), + quantity REAL NOT NULL, + unit TEXT NOT NULL, + process_date DATE NOT NULL, + expiry_date DATE, + storage_location TEXT, + notes TEXT, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (harvest_id) REFERENCES harvest_records (id) ON DELETE CASCADE + ) + `); + console.log('Harvest processing table ready'); + + // Harvest stock table + db.exec(` + CREATE TABLE IF NOT EXISTS harvest_stock ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + processing_id INTEGER NOT NULL, + current_quantity REAL NOT NULL, + unit TEXT NOT NULL, + last_updated DATE NOT NULL, + status TEXT DEFAULT 'available' CHECK(status IN ('available', 'consumed', 'expired', 'spoiled')), + notes TEXT, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (processing_id) REFERENCES harvest_processing (id) ON DELETE CASCADE + ) + `); + console.log('Harvest stock table ready'); + // Plant history table db.exec(` CREATE TABLE IF NOT EXISTS plant_history ( @@ -784,7 +821,7 @@ app.post('/api/harvests', (req, res) => { const result = stmt.run(plantId, date, quantity, unit, notes); - const getStmt = db.prepare('SELECT * FROM harvest_records WHERE id = ?'); + const getStmt = db.prepare('SELECT * FROM harvest_records WHERE id = ? ORDER BY date DESC'); const row = getStmt.get(result.lastInsertRowid); res.json({ @@ -802,6 +839,156 @@ app.post('/api/harvests', (req, res) => { } }); +// Harvest Processing +app.get('/api/harvest-processing', (req, res) => { + try { + const stmt = db.prepare(` + SELECT hp.*, hr.plant_id, hr.date as harvest_date + FROM harvest_processing hp + JOIN harvest_records hr ON hp.harvest_id = hr.id + ORDER BY hp.process_date DESC + `); + const rows = stmt.all(); + + res.json(rows.map(row => ({ + id: row.id, + harvestId: row.harvest_id, + plantId: row.plant_id, + harvestDate: row.harvest_date, + processType: row.process_type, + quantity: row.quantity, + unit: row.unit, + processDate: row.process_date, + expiryDate: row.expiry_date, + storageLocation: row.storage_location, + notes: row.notes, + createdAt: row.created_at, + updatedAt: row.updated_at + }))); + } catch (err) { + res.status(500).json({ error: err.message }); + } +}); + +app.post('/api/harvest-processing', (req, res) => { + try { + const { harvestId, processType, quantity, unit, processDate, expiryDate, storageLocation, notes } = req.body; + + const stmt = db.prepare(` + INSERT INTO harvest_processing (harvest_id, process_type, quantity, unit, process_date, expiry_date, storage_location, notes) + VALUES (?, ?, ?, ?, ?, ?, ?, ?) + `); + + const result = stmt.run(harvestId, processType, quantity, unit, processDate, expiryDate, storageLocation, notes); + + // Create corresponding stock entry + const stockStmt = db.prepare(` + INSERT INTO harvest_stock (processing_id, current_quantity, unit, last_updated, status) + VALUES (?, ?, ?, ?, 'available') + `); + stockStmt.run(result.lastInsertRowid, quantity, unit, processDate); + + const getStmt = db.prepare(` + SELECT hp.*, hr.plant_id, hr.date as harvest_date + FROM harvest_processing hp + JOIN harvest_records hr ON hp.harvest_id = hr.id + WHERE hp.id = ? + `); + const row = getStmt.get(result.lastInsertRowid); + + res.json({ + id: row.id, + harvestId: row.harvest_id, + plantId: row.plant_id, + harvestDate: row.harvest_date, + processType: row.process_type, + quantity: row.quantity, + unit: row.unit, + processDate: row.process_date, + expiryDate: row.expiry_date, + storageLocation: row.storage_location, + notes: row.notes, + createdAt: row.created_at, + updatedAt: row.updated_at + }); + } catch (err) { + res.status(400).json({ error: err.message }); + } +}); + +// Harvest Stock +app.get('/api/harvest-stock', (req, res) => { + try { + const stmt = db.prepare(` + SELECT hs.*, hp.process_type, hp.harvest_id, hr.plant_id, hr.date as harvest_date + FROM harvest_stock hs + JOIN harvest_processing hp ON hs.processing_id = hp.id + JOIN harvest_records hr ON hp.harvest_id = hr.id + ORDER BY hs.last_updated DESC + `); + const rows = stmt.all(); + + res.json(rows.map(row => ({ + id: row.id, + processingId: row.processing_id, + harvestId: row.harvest_id, + plantId: row.plant_id, + harvestDate: row.harvest_date, + processType: row.process_type, + currentQuantity: row.current_quantity, + unit: row.unit, + lastUpdated: row.last_updated, + status: row.status, + notes: row.notes, + createdAt: row.created_at, + updatedAt: row.updated_at + }))); + } catch (err) { + res.status(500).json({ error: err.message }); + } +}); + +app.put('/api/harvest-stock/:id', (req, res) => { + try { + const { currentQuantity, status, notes } = req.body; + + const stmt = db.prepare(` + UPDATE harvest_stock SET current_quantity = ?, status = ?, notes = ?, + last_updated = date('now'), updated_at = CURRENT_TIMESTAMP + WHERE id = ? + `); + + stmt.run(currentQuantity, status, notes, req.params.id); + + const getStmt = db.prepare(` + SELECT hs.*, hp.process_type, hp.harvest_id, hr.plant_id, hr.date as harvest_date + FROM harvest_stock hs + JOIN harvest_processing hp ON hs.processing_id = hp.id + JOIN harvest_records hr ON hp.harvest_id = hr.id + WHERE hs.id = ? + `); + const row = getStmt.get(req.params.id); + + res.json({ + id: row.id, + processingId: row.processing_id, + harvestId: row.harvest_id, + plantId: row.plant_id, + harvestDate: row.harvest_date, + processType: row.process_type, + currentQuantity: row.current_quantity, + unit: row.unit, + lastUpdated: row.last_updated, + status: row.status, + notes: row.notes, + createdAt: row.created_at, + updatedAt: row.updated_at + }); + } catch (err) { + res.status(400).json({ error: err.message }); + } +}); + // Health check app.get('/api/health', (req, res) => { res.json({ status: 'OK', message: 'GardenTrack API is running' }); diff --git a/backend/uploads/photo_5247041613312685274_y.jpg b/backend/uploads/photo_5247041613312685274_y.jpg new file mode 100644 index 0000000..dc12251 Binary files /dev/null and b/backend/uploads/photo_5247041613312685274_y.jpg differ diff --git a/backend/uploads/photo_5247041613312685275_y.jpg b/backend/uploads/photo_5247041613312685275_y.jpg new file mode 100644 index 0000000..62bacbe Binary files /dev/null and b/backend/uploads/photo_5247041613312685275_y.jpg differ diff --git a/backend/uploads/photo_5271893247570083300_y.jpg b/backend/uploads/photo_5271893247570083300_y.jpg new file mode 100644 index 0000000..9f710a6 Binary files /dev/null and b/backend/uploads/photo_5271893247570083300_y.jpg differ diff --git a/backend/uploads/plant-1753960452405-724160168.png b/backend/uploads/plant-1753960452405-724160168.png new file mode 100644 index 0000000..393297a Binary files /dev/null and b/backend/uploads/plant-1753960452405-724160168.png differ diff --git a/backend/uploads/plant-1753994802271-447475482.png b/backend/uploads/plant-1753994802271-447475482.png new file mode 100644 index 0000000..393297a Binary files /dev/null and b/backend/uploads/plant-1753994802271-447475482.png differ diff --git a/backend/uploads/plant-1754921088782-970283539.png b/backend/uploads/plant-1754921088782-970283539.png new file mode 100644 index 0000000..7ddb0ae Binary files /dev/null and b/backend/uploads/plant-1754921088782-970283539.png differ diff --git a/backend/uploads/plant-1756392282774-773870385.jpg b/backend/uploads/plant-1756392282774-773870385.jpg new file mode 100644 index 0000000..83c9f01 Binary files /dev/null and b/backend/uploads/plant-1756392282774-773870385.jpg differ diff --git a/backend/uploads/plant-1756392409168-924448707.jpg b/backend/uploads/plant-1756392409168-924448707.jpg new file mode 100644 index 0000000..a293f59 Binary files /dev/null and b/backend/uploads/plant-1756392409168-924448707.jpg differ diff --git a/backend/uploads/plant-1756400176200-257499030.png b/backend/uploads/plant-1756400176200-257499030.png new file mode 100644 index 0000000..21e481a Binary files /dev/null and b/backend/uploads/plant-1756400176200-257499030.png differ diff --git a/backend/uploads/plant-1756401137797-166836683.png b/backend/uploads/plant-1756401137797-166836683.png new file mode 100644 index 0000000..21e481a Binary files /dev/null and b/backend/uploads/plant-1756401137797-166836683.png differ diff --git a/backend/uploads/plant-1756927577450-332932988.jpg b/backend/uploads/plant-1756927577450-332932988.jpg new file mode 100644 index 0000000..bab2c20 Binary files /dev/null and b/backend/uploads/plant-1756927577450-332932988.jpg differ diff --git a/package-lock.json b/package-lock.json index 77f4ab0..eb2e37f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,13 +1,14 @@ { "name": "vite-react-typescript-starter", - "version": "0.0.0", + "version": "0.2.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "vite-react-typescript-starter", - "version": "0.0.0", + "version": "0.2.0", "dependencies": { + "framer-motion": "^12.23.12", "lucide-react": "^0.344.0", "react": "^18.3.1", "react-dom": "^18.3.1" @@ -18,12 +19,10 @@ "@types/react-dom": "^18.3.0", "@vitejs/plugin-react": "^4.3.1", "autoprefixer": "^10.4.18", - "concurrently": "^8.2.2", "eslint": "^9.9.1", "eslint-plugin-react-hooks": "^5.1.0-rc.0", "eslint-plugin-react-refresh": "^0.4.11", "globals": "^15.9.0", - "nodemon": "^3.0.2", "postcss": "^8.4.35", "tailwindcss": "^3.4.1", "typescript": "^5.5.3", @@ -292,16 +291,6 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/runtime": { - "version": "7.28.2", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.2.tgz", - "integrity": "sha512-KHp2IflsnGywDjBWDkR9iEqiWSpc8GIi0lgTT3mOElT0PP1tG26P4tmFI2YvAdzgq9RGyoHZQEIEdZy6Ec5xCA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/template": { "version": "7.25.7", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.7.tgz", @@ -1831,120 +1820,6 @@ "node": ">= 6" } }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/cliui/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/cliui/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/cliui/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, - "node_modules/cliui/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/cliui/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, "node_modules/color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -1975,126 +1850,6 @@ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, - "node_modules/concurrently": { - "version": "8.2.2", - "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-8.2.2.tgz", - "integrity": "sha512-1dP4gpXFhei8IOtlXRE/T/4H88ElHgTiUzh71YUmtjTEHMSRS2Z/fgOxHSxxusGHogsRfxNq1vyAwxSC+EVyDg==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.1.2", - "date-fns": "^2.30.0", - "lodash": "^4.17.21", - "rxjs": "^7.8.1", - "shell-quote": "^1.8.1", - "spawn-command": "0.0.2", - "supports-color": "^8.1.1", - "tree-kill": "^1.2.2", - "yargs": "^17.7.2" - }, - "bin": { - "conc": "dist/bin/concurrently.js", - "concurrently": "dist/bin/concurrently.js" - }, - "engines": { - "node": "^14.13.0 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/open-cli-tools/concurrently?sponsor=1" - } - }, - "node_modules/concurrently/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/concurrently/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/concurrently/node_modules/chalk/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/concurrently/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/concurrently/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, - "node_modules/concurrently/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/concurrently/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", @@ -2133,23 +1888,6 @@ "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", "dev": true }, - "node_modules/date-fns": { - "version": "2.30.0", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", - "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.21.0" - }, - "engines": { - "node": ">=0.11" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/date-fns" - } - }, "node_modules/debug": { "version": "4.3.7", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", @@ -2652,6 +2390,33 @@ "url": "https://github.com/sponsors/rawify" } }, + "node_modules/framer-motion": { + "version": "12.23.12", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.23.12.tgz", + "integrity": "sha512-6e78rdVtnBvlEVgu6eFEAgG9v3wLnYEboM8I5O5EXvfKC8gxGQB8wXJdhkMy10iVcn05jl6CNw7/HTsTCfwcWg==", + "license": "MIT", + "dependencies": { + "motion-dom": "^12.23.12", + "motion-utils": "^12.23.6", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "@emotion/is-prop-valid": "*", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/is-prop-valid": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -2684,16 +2449,6 @@ "node": ">=6.9.0" } }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, - "license": "ISC", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, "node_modules/glob": { "version": "10.4.5", "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", @@ -2798,13 +2553,6 @@ "node": ">= 4" } }, - "node_modules/ignore-by-default": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", - "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", - "dev": true, - "license": "ISC" - }, "node_modules/import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", @@ -3040,13 +2788,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true, - "license": "MIT" - }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -3124,6 +2865,21 @@ "node": ">=16 || 14 >=14.17" } }, + "node_modules/motion-dom": { + "version": "12.23.12", + "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.23.12.tgz", + "integrity": "sha512-RcR4fvMCTESQBD/uKQe49D5RUeDOokkGRmz4ceaJKDBgHYtZtntC/s2vLvY38gqGaytinij/yi3hMcWVcEF5Kw==", + "license": "MIT", + "dependencies": { + "motion-utils": "^12.23.6" + } + }, + "node_modules/motion-utils": { + "version": "12.23.6", + "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.23.6.tgz", + "integrity": "sha512-eAWoPgr4eFEOFfg2WjIsMoqJTW6Z8MTUCgn/GZ3VRpClWBdnbjryiA3ZSNLyxCTmCQx4RmYX6jX1iWHbenUPNQ==", + "license": "MIT" + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -3171,48 +2927,6 @@ "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", "dev": true }, - "node_modules/nodemon": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.10.tgz", - "integrity": "sha512-WDjw3pJ0/0jMFmyNDp3gvY2YizjLmmOUQo6DEBY+JgdvW/yQ9mEeSw6H5ythl5Ny2ytb7f9C2nIbjSxMNzbJXw==", - "dev": true, - "license": "MIT", - "dependencies": { - "chokidar": "^3.5.2", - "debug": "^4", - "ignore-by-default": "^1.0.1", - "minimatch": "^3.1.2", - "pstree.remy": "^1.1.8", - "semver": "^7.5.3", - "simple-update-notifier": "^2.0.0", - "supports-color": "^5.5.0", - "touch": "^3.1.0", - "undefsafe": "^2.0.5" - }, - "bin": { - "nodemon": "bin/nodemon.js" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/nodemon" - } - }, - "node_modules/nodemon/node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -3548,13 +3262,6 @@ "node": ">= 0.8.0" } }, - "node_modules/pstree.remy": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", - "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", - "dev": true, - "license": "MIT" - }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -3637,16 +3344,6 @@ "node": ">=8.10.0" } }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/resolve": { "version": "1.22.8", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", @@ -3741,16 +3438,6 @@ "queue-microtask": "^1.2.2" } }, - "node_modules/rxjs": { - "version": "7.8.2", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", - "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.1.0" - } - }, "node_modules/scheduler": { "version": "0.23.2", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", @@ -3789,19 +3476,6 @@ "node": ">=8" } }, - "node_modules/shell-quote": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz", - "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/signal-exit": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", @@ -3814,32 +3488,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/simple-update-notifier": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", - "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "semver": "^7.5.3" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/simple-update-notifier/node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -3849,12 +3497,6 @@ "node": ">=0.10.0" } }, - "node_modules/spawn-command": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2.tgz", - "integrity": "sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ==", - "dev": true - }, "node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", @@ -4094,26 +3736,6 @@ "node": ">=8.0" } }, - "node_modules/touch": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz", - "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==", - "dev": true, - "license": "ISC", - "bin": { - "nodetouch": "bin/nodetouch.js" - } - }, - "node_modules/tree-kill": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", - "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", - "dev": true, - "license": "MIT", - "bin": { - "tree-kill": "cli.js" - } - }, "node_modules/ts-api-utils": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", @@ -4136,7 +3758,6 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "dev": true, "license": "0BSD" }, "node_modules/type-check": { @@ -4187,13 +3808,6 @@ } } }, - "node_modules/undefsafe": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", - "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", - "dev": true, - "license": "MIT" - }, "node_modules/update-browserslist-db": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", @@ -4443,16 +4057,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=10" - } - }, "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", @@ -4471,80 +4075,6 @@ "node": ">= 14" } }, - "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/yargs/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index ac7a059..7117958 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "vite-react-typescript-starter", "private": true, - "version": "0.2.0", + "version": "0.3.0", "type": "module", "scripts": { "dev": "vite --mode development", @@ -10,6 +10,7 @@ "preview": "vite preview" }, "dependencies": { + "framer-motion": "^12.23.12", "lucide-react": "^0.344.0", "react": "^18.3.1", "react-dom": "^18.3.1" diff --git a/src/App.tsx b/src/App.tsx index 72d7c42..5e34ac7 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,5 +1,5 @@ import { useState, useEffect } from 'react'; -import {Sprout, Calendar, CheckSquare, Activity, BookOpen, Beaker, FlaskConical} from 'lucide-react'; +import { Sprout, Calendar, CheckSquare, Activity, BookOpen, Beaker, FlaskConical, Archive } from 'lucide-react'; import { Plant, Task, MaintenanceRecord, HarvestRecord, PlantObservation } from './types'; import Dashboard from './components/Dashboard'; import PlantRegistry from './components/PlantRegistry'; @@ -8,6 +8,7 @@ import MaintenanceLog from './components/MaintenanceLog'; import ObservationJournal from './components/ObservationJournal'; import FertilizerRegistry from './components/FertilizerRegistry'; import ChemicalRegistry from './components/ChemicalRegistry'; +import HarvestStockManager from './components/HarvestStockManager'; import { apiService } from './services/api'; function App() { @@ -56,10 +57,11 @@ function App() { { id: 'dashboard', label: 'Панель', icon: Activity }, { id: 'plants', label: 'Реестр растений', icon: Sprout }, { id: 'tasks', label: 'План работ', icon: CheckSquare }, - { id: 'maintenance', label: 'Журнал', icon: Calendar }, + { id: 'maintenance', label: 'Выполненные работы', icon: Calendar }, { id: 'observations', label: 'Дневник', icon: BookOpen }, { id: 'fertilizers', label: 'Удобрения', icon: FlaskConical }, { id: 'chemicals', label: 'Химикаты', icon: Beaker }, + { id: 'stock', label: 'Заготовки', icon: Archive }, ]; if (loading) { @@ -74,109 +76,129 @@ function App() { } return ( -
-
-
-
-
+
+ {/* Left Sidebar */} +
+
+ {/* Logo */} +
-

GardenTrack

+

GardenTrack

-
-
-
- {activeTab === 'dashboard' && ( - - )} - {activeTab === 'plants' && ( - - )} - {activeTab === 'tasks' && ( - - )} - {activeTab === 'maintenance' && ( - - )} - {activeTab === 'observations' && ( - - )} - {activeTab === 'fertilizers' && ( - - )} - {activeTab === 'chemicals' && ( - - )} -
+ {/* Main Content */} +
+ {/* Mobile Header */} +
+
+
+ +

GardenTrack

+
+
+
- {/* Mobile Navigation */} - -
+ + {/* Mobile Navigation */} + +
); } + export default App; \ No newline at end of file diff --git a/src/components/ChemicalRegistry.tsx b/src/components/ChemicalRegistry.tsx index 92f3c05..fbd80e2 100644 --- a/src/components/ChemicalRegistry.tsx +++ b/src/components/ChemicalRegistry.tsx @@ -231,7 +231,7 @@ const ChemicalRegistry: React.FC = () => { onClick={() => setShowForm(true)} className="mt-4 text-green-600 hover:text-green-700 transition-colors" > - Add your first chemical → + Добавте первую запись о химикате → )} diff --git a/src/components/FertilizerRegistry.tsx b/src/components/FertilizerRegistry.tsx index d42bdf8..ee9de46 100644 --- a/src/components/FertilizerRegistry.tsx +++ b/src/components/FertilizerRegistry.tsx @@ -231,7 +231,7 @@ const FertilizerRegistry: React.FC = () => { onClick={() => setShowForm(true)} className="mt-4 text-green-600 hover:text-green-700 transition-colors" > - Add your first fertilizer → + Добавте первую запись о удобрении → )} diff --git a/src/components/HarvestProcessingModal.tsx b/src/components/HarvestProcessingModal.tsx new file mode 100644 index 0000000..a1a879a --- /dev/null +++ b/src/components/HarvestProcessingModal.tsx @@ -0,0 +1,246 @@ +import React, { useState } from 'react'; +import { HarvestRecord, Plant, HarvestProcessing } from '../types'; +import { X } from 'lucide-react'; +import { apiService } from '../services/api'; + +interface HarvestProcessingModalProps { + isOpen: boolean; + harvest: HarvestRecord; + plant: Plant; + onClose: () => void; + onProcessingAdded: () => void; +} + +export const processTypes = [ + { value: 'fresh', label: 'Fresh/Raw', color: 'bg-green-100 text-green-800' }, + { value: 'frozen', label: 'Frozen', color: 'bg-blue-100 text-blue-800' }, + { value: 'jam', label: 'Jam/Preserves', color: 'bg-purple-100 text-purple-800' }, + { value: 'dried', label: 'Dried', color: 'bg-yellow-100 text-yellow-800' }, + { value: 'canned', label: 'Canned', color: 'bg-orange-100 text-orange-800' }, + { value: 'juice', label: 'Juice', color: 'bg-pink-100 text-pink-800' }, + { value: 'sauce', label: 'Sauce', color: 'bg-red-100 text-red-800' }, + { value: 'pickled', label: 'Pickled', color: 'bg-indigo-100 text-indigo-800' }, + { value: 'other', label: 'Other', color: 'bg-gray-100 text-gray-800' } +]; + +export default function HarvestProcessingModal({ + isOpen, + harvest, + plant, + onClose, + onProcessingAdded +}: HarvestProcessingModalProps) { + const [formData, setFormData] = useState({ + processType: 'fresh' as HarvestProcessing['processType'], + quantity: harvest.quantity.toString(), + unit: harvest.unit, + processDate: new Date().toISOString().split('T')[0], + expiryDate: '', + storageLocation: '', + notes: '' + }); + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + + try { + await apiService.createHarvestProcessing({ + harvestId: harvest.id, + processType: formData.processType, + quantity: parseFloat(formData.quantity), + unit: formData.unit, + processDate: formData.processDate, + expiryDate: formData.expiryDate || undefined, + storageLocation: formData.storageLocation || undefined, + notes: formData.notes || undefined + }); + + onProcessingAdded(); + onClose(); + } catch (error) { + console.error('Error creating harvest processing:', error); + alert('Failed to create harvest processing record'); + } + }; + + const handleChange = (e: React.ChangeEvent) => { + const { name, value } = e.target; + + if (name === 'unit') { + // Check if the new unit requires integer values + const countableUnits = ['pieces', 'jars', 'bottles', 'bags']; + const requiresInteger = countableUnits.includes(value); + + setFormData(prev => ({ + ...prev, + [name]: value, + // Convert quantity to integer if switching to a countable unit + quantity: requiresInteger ? Math.floor(parseFloat(prev.quantity) || 0).toString() : prev.quantity + })); + } else { + setFormData(prev => ({ ...prev, [name]: value })); + } + }; + + if (!isOpen) return null; + + // Determine if current unit requires integer values + const countableUnits = ['pieces', 'jars', 'bottles', 'bags']; + const requiresInteger = countableUnits.includes(formData.unit); + + return ( +
+
+
+
+

Обработка урожая

+

+ {plant.variety} - {harvest.quantity} {harvest.unit} собрано {new Date(harvest.date).toLocaleDateString()} +

+
+ +
+ +
+
+
+ + +
+ +
+ + +
+ +
+ + + {requiresInteger && ( +

+ Для этого типа требуются только целые числа +

+ )} +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+
+ +
+ +