Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
e6d37e7
Started the project with structure in the backend. Started also with …
Lillebrorgroda Aug 6, 2025
0750029
Auth and loging workingin Postman. Created a nwe model for plants an…
Lillebrorgroda Aug 7, 2025
46927a9
started with some mockup plantdata and fetching from API and combine…
Lillebrorgroda Aug 7, 2025
70b9cad
Created login and signup in the frontend. Not working as it schould
Lillebrorgroda Aug 11, 2025
082e76b
Login, Signup working. added some styling
Lillebrorgroda Aug 11, 2025
3fadfdf
Deleting files that was duplicated. Debugging why the seeding not wor…
Lillebrorgroda Aug 12, 2025
29f6fb6
Now seeding works again. Added test to test different files and also …
Lillebrorgroda Aug 12, 2025
f5f3b3b
Working on the search function. It works with the combined data from …
Lillebrorgroda Aug 13, 2025
462e3c7
Trying to fix seeding and search. It works but not the way I want it.…
Lillebrorgroda Aug 19, 2025
3d9a9a2
Calender with event magegment done
Lillebrorgroda Aug 19, 2025
6a76693
Change alot of the styling and added a new page for all saved plants.
Lillebrorgroda Aug 21, 2025
990a839
Added a new page and cleaned the code
Lillebrorgroda Aug 22, 2025
aea4af0
Function to save plants to user and show them works
Lillebrorgroda Aug 22, 2025
563d11f
Added a footer component with function to handle accsess trought icon
Lillebrorgroda Aug 23, 2025
b6f5efa
Fixed functionality on home button
Lillebrorgroda Aug 24, 2025
b6b2bdc
Adding a toml file trying to make the deployment to work
Lillebrorgroda Aug 24, 2025
f5f9172
Change URL path in files to connect to de deplyed
Lillebrorgroda Aug 24, 2025
914d016
Took away dotenv in frontend
Lillebrorgroda Aug 24, 2025
d459fea
Added VITE to the API env
Lillebrorgroda Aug 24, 2025
f18b55c
Added specified info to CORS
Lillebrorgroda Aug 24, 2025
953fd7b
Added a new functionality to the calender. Not working jet
Lillebrorgroda Aug 25, 2025
40d2215
Added eventroutes i server.js and cleand up the code
Lillebrorgroda Aug 25, 2025
9beda98
Error handling
Lillebrorgroda Aug 25, 2025
7b96477
Error handling
Lillebrorgroda Aug 25, 2025
7f44944
Error handling
Lillebrorgroda Aug 25, 2025
594f7b1
Change routes in event.js
Lillebrorgroda Aug 25, 2025
cb11721
Change authorization
Lillebrorgroda Aug 25, 2025
e947ef0
Change path from calender to events
Lillebrorgroda Aug 25, 2025
f80229b
Change path from calender to events
Lillebrorgroda Aug 25, 2025
9c78168
change a path to the different files
Lillebrorgroda Aug 25, 2025
3f75d60
Styling on calender
Lillebrorgroda Aug 26, 2025
3993742
Small fix in calender
Lillebrorgroda Aug 26, 2025
e55d635
Added token to calenderpage
Lillebrorgroda Aug 26, 2025
b546cc3
added consol.logs to trubbleshoot
Lillebrorgroda Aug 26, 2025
124d193
Fixed calender function in week view
Lillebrorgroda Aug 26, 2025
9b990ac
Demo ready styling and function
Lillebrorgroda Aug 28, 2025
d9098a9
Added token to Accountpage to make the calender work
Lillebrorgroda Aug 29, 2025
611fc69
Fixed toggleTodo
Lillebrorgroda Aug 29, 2025
34c41f8
Change background-color
Lillebrorgroda Aug 29, 2025
e342755
added aria-label and change color to get better A11y
Lillebrorgroda Aug 30, 2025
5bdb2a8
Cleaned code in stylecomponents
Lillebrorgroda Aug 31, 2025
f5f8731
Deleted testfiles, cleaned in backend files
Lillebrorgroda Aug 31, 2025
0fbb287
Refactor Plantpart of the app
Lillebrorgroda Aug 31, 2025
c4b85d8
Refactoring stylecomponents into smaller files
Lillebrorgroda Aug 31, 2025
0a6988e
Added text to the ReadMe-file
Lillebrorgroda Aug 31, 2025
a2ef0a8
Update README.md
Lillebrorgroda Aug 31, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 77 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,85 @@
# Final Project
A project to get all knowledge together after a fast paced bootcamp, from zero to Hero, and challenge us to explore more of the world of tech.

Replace this readme with your own information about your project.
## The problem
Problem Approach:
I identified a gap in existing garden management applications - they were either too complex and overwhelming, or lacked essential functionality. My approach was to conduct a competitive analysis of available apps to understand what worked well and what didn't. I focused on creating a minimal viable product that prioritized core features while maintaining simplicity and user-friendliness.
Planning Process:

Start by briefly describing the assignment in a sentence or two. Keep it short and to the point.
Research Phase: Analyzed existing garden apps to identify pain points and essential features
Feature Prioritization: Defined must-have functionality vs. nice-to-have features
Architecture Design: Planned a full-stack solution with clear separation of concerns
Technology Selection: Chose modern, well-supported technologies that would allow for future scalability

## The problem
Technologies & Tools Used:
Frontend:

React with Vite for fast development and optimized builds
React Router for navigation management
Zustand for lightweight state management
Styled Components for component-based styling
React Hot Toast for user notifications
React Icons for consistent iconography
Axios for API communication

Backend:

Node.js with Express for the REST API
MongoDB with Mongoose for data persistence and modeling
bcrypt for secure password hashing
CORS for cross-origin request handling
dotenv for environment variable management

Data & Integration:

Plant API for comprehensive plant information
CSV processing with Papaparse for data import/export
Cheerio for web scraping plant data when needed
node-fetch for external API calls
crypto for generating secure tokens

Development Tools:

Nodemon for automatic server restarts during development

Add seasonal planning tools

Drag and drop to create your own garden

Possibility to connect it to an offline calender

Seasonal Planning Tools:

Plant Calendar Integration: Implement a dynamic planting calendar that suggests optimal sowing, transplanting, and harvesting dates based on user location and last frost dates

Describe how you approached to problem, and what tools and techniques you used to solve it. How did you plan? What technologies did you use? If you had more time, what would be next?
Seasonal Task Management: Create automated reminders for seasonal garden tasks (pruning, fertilizing, pest control) with customizable scheduling

Crop Rotation Planning: Build a multi-year planning system that suggests crop rotation patterns to maintain soil health

Weather Integration: Connect with weather APIs to provide frost warnings and adjust planting recommendations based on seasonal forecasts

Drag and Drop Garden Designer:

Interactive Garden Layout: Implement a drag-and-drop interface using libraries like React DnD or React Beautiful DnD for visual garden bed planning


Offline Calendar Integration:

iCal/Google Calendar Sync: Implement calendar export functionality using iCal format for seamless integration with existing calendar apps


Technical Implementation Considerations:

Use React DnD for drag-and-drop functionality with touch support for mobile devices

Integrate date-fns or moment.js for robust date calculations and timezone handling

Consider Canvas API or SVG for more advanced garden visualization tools

These features would transform the app from a basic plant tracker into a comprehensive garden management and planning platform.

## View it live

Every project should be deployed somewhere. Be sure to include the link to the deployed project so that the viewer can click around and see what it's all about.
Frontend: https://lillebrorgrodafinalproject.netlify.app/

Backend: https://garden-backend-r6x2.onrender.com
91 changes: 91 additions & 0 deletions backend/data/plants.csv

Large diffs are not rendered by default.

20 changes: 20 additions & 0 deletions backend/models/Event.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import mongoose from "mongoose"

const eventSchema = new mongoose.Schema({
userId: { type: mongoose.Schema.Types.ObjectId, ref: "User", required: true },
date: { type: Date, required: true },
time: { type: String },
text: { type: String, required: true },
done: { type: Boolean, default: false }
}, {
toJSON: {
transform: function (doc, ret) {
ret.id = ret._id
delete ret._id
delete ret.__v
return ret
}
}
})

export default mongoose.model("Event", eventSchema)
117 changes: 117 additions & 0 deletions backend/models/plant.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import mongoose from "mongoose";

const plantSchema = new mongoose.Schema({

scientificName: {
type: [String],
required: true,
},
swedishName: {
type: String,
},
commonName: {
type: String, // for the english name
},
description: {
type: String,
},
imageUrl: {
type: String,
},
watering: {
type: [String],
default: [],
},
sunlight: {
type: [String],
default: [],
},
soil: {
type: [String],
default: [],
},
sowingPeriod: {
type: [String], // Ex: ["March", "April"]
default: [],
},
harvestPeriod: {
type: [String],
default: [],
},
sowingMonths: {
type: [Number], // Ex: [3, 4, 5] för mars-maj
default: [],
},
harvestMonths: {
type: [Number],
default: [],
},
companionPlants: [{
type: mongoose.Schema.Types.ObjectId,
ref: "Plant",
}],
companionPlantNames: {
type: [String],
default: [],
},
edibleParts: {
type: [String], // Ex: ["leaves", "fruit", "root"]
default: [],
},
isEdible: {
type: Boolean,
default: false,
},

redListStatus: {
type: String, // Ex: "LC", "NT", "EN", "CR", etc.
},
tags: {
type: [String], // Ex: ["indoors", "perennial", "climber"]
default: [],
},

// API-data
perenualId: {
type: Number, // ID from Perenual API
},
source: {
type: String,
enum: ["csv", "api", "csv_and_api", "manual"],
default: "manual",
},
createdBy: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
},
createdAt: {
type: Date,
default: Date.now,
},
updatedAt: {
type: Date,
default: Date.now,
},
})

// Index
plantSchema.index({ scientificName: 1 });
plantSchema.index({ swedishName: 1 });
plantSchema.index({ commonName: 1 });
plantSchema.index({ sunlight: 1 });
plantSchema.index({ watering: 1 });
plantSchema.index({ sowingMonths: 1 });


plantSchema.virtual('fullName').get(function () {
return `${this.swedishName} (${this.scientificName})`;
});

// Middleware for updatedAt
plantSchema.pre('findOneAndUpdate', function () {
this.set({ updatedAt: new Date() });
});

const Plant = mongoose.model("Plant", plantSchema);

export default Plant;
62 changes: 62 additions & 0 deletions backend/models/user.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import mongoose from "mongoose"
import crypto from "crypto"

const userSchema = new mongoose.Schema({
username: {
type: String,
required: true,
unique: true,
minlength: 3,
maxlength: 50,
trim: true,
},
password: {
type: String,
required: true,
minlength: 6,
maxlength: 100,
},
email: {
type: String,
required: true,
unique: true,
lowercase: true,
trim: true,
match: [/.+\@.+\..+/, 'Please enter a valid email address'],
},
savedPlants: [{
plant: {
type: mongoose.Schema.Types.ObjectId,
ref: "Plant"
},
savedAt: {
type: Date,
default: Date.now
},
notes: {
type: String,
maxlength: 500
}
}],
accessToken: {
type: String,
default: () => crypto.randomBytes(128).toString("hex"),
},
createdAt: {
type: Date,
default: Date.now,
},
lastLogin: {
type: Date,
}
}, {
timestamps: true //
})

// Index
//userSchema.index({ email: 1 });
userSchema.index({ accessToken: 1 });

const User = mongoose.model("User", userSchema)

export default User
15 changes: 13 additions & 2 deletions backend/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"name": "project-final-backend",
"version": "1.0.0",
"type": "module",
"description": "Server part of final project",
"scripts": {
"start": "babel-node server.js",
Expand All @@ -12,9 +13,19 @@
"@babel/core": "^7.17.9",
"@babel/node": "^7.16.8",
"@babel/preset-env": "^7.16.11",
"axios": "^1.11.0",
"bcrypt": "^6.0.0",
"cheerio": "^1.1.2",
"cors": "^2.8.5",
"crypto": "^1.0.1",
"dotenv": "^17.2.1",
"express": "^4.17.3",
"mongoose": "^8.4.0",
"nodemon": "^3.0.1"
"express-list-endpoints": "^7.1.1",
"jsonwebtoken": "^9.0.2",
"mongodb": "^6.18.0",
"mongoose": "^8.17.0",
"node-fetch": "^3.3.2",
"nodemon": "^3.0.1",
"papaparse": "^5.5.3"
}
}
Loading