The frontend web application for Planty — a plant tracking app that allows users to manage their personal plant collection, monitor plant health stats, and receive care notifications. Built with Nuxt 3 and Vue 3, and deployed to Netlify.
| Package | Purpose |
|---|---|
| Nuxt 3 | Full-stack Vue framework (SSR/SSG/SPA) |
| Vue 3 | UI component framework |
| Pinia | Global state management |
| Tailwind CSS | Utility-first CSS styling |
| PrimeVue | UI component library (toast notifications) |
| Lucide Vue Next | Icon library |
| Vue Router | Client-side routing |
Planty/
├── nuxt.config.ts # Nuxt configuration, runtime config, modules
├── tailwind.config.js # Tailwind theme with custom color tokens
├── package.json
├── .env # Local environment variables (not committed)
├── app/
│ ├── app.vue # Root app component (Navbar, NuxtPage, Toasts)
│ ├── pages/
│ │ ├── index.vue # Landing page (Hero, Features, CTA)
│ │ ├── login.vue # Login / sign-up page
│ │ ├── notifications.vue # Notifications inbox
│ │ └── plants/
│ │ ├── index.vue # Plant collection list
│ │ └── [id].vue # Individual plant detail page
│ ├── components/
│ │ ├── Navbar.vue # Top navigation bar with auth state
│ │ ├── Hero.vue # Landing page hero section
│ │ ├── Features.vue # Landing page features section
│ │ ├── CallToAction.vue # Landing page CTA section
│ │ ├── Footer.vue # Page footer
│ │ ├── Modal.vue # Reusable modal wrapper
│ │ ├── AddPlantModal.vue # Modal form for adding a new plant
│ │ ├── EditPlantModal.vue # Modal form for editing a plant
│ │ └── ToastNotifications.vue # PrimeVue toast notification renderer
│ ├── stores/
│ │ ├── auth.ts # Auth store: user, token, login/signup/logout
│ │ ├── plants.ts # Plants store: CRUD, API sync, watering mapping
│ │ └── notifications.ts # Notifications store: add, dismiss, mark read
│ ├── composables/
│ │ └── useDemoNotifications.ts # Demo notification scheduler (currently disabled)
│ ├── data/
│ │ └── demo-notifications.json # Sample notification data for demo mode
│ └── plugins/
│ └── primevue-toast.ts # Registers PrimeVue ToastService globally
└── server/
└── api/
└── plant-stats.get.ts # Nuxt server route: returns randomised plant stat data
| Route | Description |
|---|---|
/ |
Public landing page with Hero, Features, and Call to Action sections |
/login |
Combined login and sign-up form, toggled by a mode switch |
/plants |
Authenticated plant collection — fetches plants from the API on mount |
/plants/:id |
Individual plant detail — reads from the Pinia store, falls back to fetching if store is empty |
/notifications |
Inbox of autonomous and requirement-based plant notifications |
Manages the authenticated user session.
user— the logged-in user object (id,name,email)token— JWT token used for API requestslogin(email, password)— POSTs to the API, stores user and token insessionStoragesignup(name, email, password)— registers, then auto-callsloginlogout()— clears user, token, sessionStorage, and callsclearPlants()restoreSession()— rehydrates user and token fromsessionStorageon app load
Manages the user's plant collection and syncs with the backend API.
fetchPlants()—GET /plants/user?user_id=:id— loads all plants for the current useraddPlant(...)—POST /plants/— creates a plant and adds it to the storeupdatePlant(id, data)—PUT /plants/:id— updates plant details in the API and storeremovePlant(id)—DELETE /plants/:id— deletes from the API and removes from the storeclearPlants()— empties the store (called on logout)- Watering frequency is stored in the API as an integer number of days, and mapped to/from human-readable labels (e.g.
7↔"Weekly") within the store
Manages in-app notifications.
- Two types:
autonomous(system-triggered) andrequirement(care actions needed) addNotification(...),dismiss(id),acknowledge(id),markCompleted(id),clearAll(),markAllRead()
- User signs up or logs in via
/login - The API returns a JWT token and user info
- Both are saved in
sessionStorageand the Pinia auth store - All subsequent API requests include
Authorization: Bearer <token> - On app load,
restoreSession()rehydrates the auth state fromsessionStorage - On logout, the store and
sessionStorageare both cleared
Nuxt maps env vars to runtimeConfig automatically using the NUXT_PUBLIC_ prefix convention. No code changes are needed to switch environments — just set the variable in your deployment platform.
| Variable | Maps to | Description |
|---|---|---|
NUXT_PUBLIC_API_BASE_URL |
runtimeConfig.public.apiBaseUrl |
Base URL of the Planty REST API |
Local development defaults to http://localhost:8080 if the env var is not set (configured in nuxt.config.ts).
To override locally, create a .env file:
NUXT_PUBLIC_API_BASE_URL=http://localhost:8080# Install dependencies
yarn install
# Start the development server
yarn devThe app will run at http://localhost:3000 by default.
# Build for production
yarn build
# Preview the production build
yarn previewPlanty includes a lightweight Nuxt server route for plant stats simulation:
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/plant-stats |
Returns randomised sensor data (humidity, temp, soil moisture, light level, health score) |
This is a placeholder for future real sensor/IoT integration.
| Service | Platform | URL |
|---|---|---|
| Frontend (this app) | Netlify | https://plantyv1.netlify.app |
| Backend API | Railway | https://planty-api-production.up.railway.app |
| Database | Railway (MySQL) | Managed by Railway, connected to the API |
In Netlify's environment variable settings, set:
NUXT_PUBLIC_API_BASE_URL = https://planty-api-production.up.railway.app
Nuxt will automatically pick this up at build time and inject it into the client bundle.