Building a Farmers Platform with Admin Dashboard & Data Export
I built a Farmers Platform — an agricultural management system that helps administrators track farmer data, visualize analytics, and export reports. The entire stack is React + Node.js + Express + MongoDB.
Key Functionalities
- Admin Dashboard — Overview with charts showing farmer registrations, crop distribution, and regional data
- Farmer Management — Full CRUD for farmer records with detailed profiles
- Role-Based Access Control — Admins can manage everything; regular users have read-only access to relevant data
- Data Export — Export farmer data and reports as CSV or PDF for offline analysis
- Analytics & Charts — Interactive charts built with Chart.js for data visualization
- Search & Pagination — Efficient data browsing with server-side pagination and filters
Problem: Role-Based Access at the API Level
Initially, I only checked roles on the frontend (hiding admin buttons for regular users). This was insecure — anyone could call admin endpoints directly. I added an authorize middleware that verifies the user's role from the JWT payload before processing the request. Each route now explicitly declares which roles can access it: authorize('admin') or authorize('admin', 'manager').
Problem: CSV Export with Arabic Characters
Farmer names and locations contained Arabic text. When exporting to CSV, Excel displayed garbled characters because it defaulted to ANSI encoding. The fix was adding a UTF-8 BOM (Byte Order Mark) at the start of the CSV file: \uFEFF. This tells Excel to read the file as UTF-8, and all Arabic text rendered correctly.
Problem: Chart.js Re-rendering on Data Updates
Every time the dashboard data refreshed, Chart.js would create a new canvas instance without destroying the old one, causing memory leaks and visual glitches (charts stacking on top of each other). I fixed this by properly destroying the chart instance in the React useEffect cleanup function before re-creating it with new data.
Problem: Server-Side Pagination with Filters
Combining pagination with dynamic filters (by region, crop type, date range) was complex. If a user applied a filter on page 3, they'd get empty results because the offset was wrong for the filtered dataset. I reset the page to 1 whenever a filter changed, and built the MongoDB aggregation pipeline to apply filters first, then $skip and $limit for pagination. This ensured consistent results.
Key Takeaways
Never trust the frontend for access control — always enforce it server-side. Data export seems simple until you hit encoding issues across different languages. And Chart.js in React requires careful lifecycle management to avoid memory leaks.
See the live platform or explore the source code.