Farmfolio Project Assistant
Expert guidance for working with the Farmfolio codebase - a React SPA that connects food producers with conscious consumers through digital storytelling and marketplace discovery.
What This Skill Does
Provides comprehensive context and best practices for developing features in the Farmfolio application, including:
Architecture patterns (React Query, context-based auth, template system)API communication patterns and error handlingTemplate-driven content creation (stories & producer profiles)Common pitfalls and defensive coding patternsDevelopment workflow and deployment contextInstructions
When working with the Farmfolio codebase, follow these guidelines:
1. Understand the Core Architecture
**Tech Stack:**
React 18 + Vite (port 3000)React Router for client-side routingTailwindCSS with custom farmfolio theme (green, darkgreen, cream, brown)React Query (@tanstack/react-query) for server state (retry=2, staleTime=5min)Axios with global interceptorsFormik + Yup for formsFramer Motion for animations**Key Files:**
`src/App.jsx` - Route definitions`src/main.jsx` - React Query + AuthProvider setup`src/context/AuthContext.jsx` - Authentication context`src/api/axiosConfig.js` - Centralized axios instance with interceptors`src/utils/constants.js` - All routes, endpoints, error messages, env vars2. Authentication Patterns
**Token Management:**
Tokens stored in localStorage using `STORAGE_KEYS` constantsAuth interceptor attaches `Authorization: Bearer <token>` to all requestsDefensive handling of multiple API response shapes: `{ token, user }`, `{ data: { token, user } }`, or JWT decode fallbackSee `src/context/AuthContext.jsx` lines 56-110 for robust implementation**Protected Routes:**
Use `<PrivateRoute>` wrapper componentRedirects unauthenticated users to `/login`401 responses automatically clear auth and redirect (prevent retry loops with `_retry` flag)**Common Pitfall:** Backend may return inconsistent response shapes. Always handle multiple possibilities or decode JWT as fallback.
3. API Communication Pattern
**Service Module Pattern:**
```javascript
// Create small service modules in src/api/
import axiosInstance from './axiosConfig';
export const login = async (credentials) => {
const response = await axiosInstance.post(API_ENDPOINTS.LOGIN, credentials);
return response.data; // Caller handles multiple shapes
};
```
**Error Handling:**
API errors follow shape: `{ message, errors?, originalError }`Use constants from `ERROR_MESSAGES`Toast notifications via `react-hot-toast`**File Uploads:**
Always use `createFormData` helper from `src/api/axiosConfig.js`Properly handles arrays (e.g., `CategoryIds`) and filesDon't manually set `Content-Type` - interceptor handles it4. Template System (Core Feature)
**Story Templates:**
Predefined layouts for farm stories, artisan products, sustainability narrativesConfig: `src/templates/templateConfig.js`Backend templates: `src/templates/storyTemplatesForBackend.js`Flow: TemplateSelector → StoryEditor → QRCodeGenerator**Producer Profile Templates:**
Similar system for marketplace profilesBackend templates: `src/templates/producerProfileTemplatesForBackend.js`Inline editing feature available**Admin Upload:**
`src/pages/SimpleAdminPage.jsx` provides template upload interface5. Routing & Pages
**Route Constants:**
All routes defined in `src/utils/constants.js` under `ROUTES`:
Public: `/marketplace`, `/producer/:id`, `/story/:id`Auth: `/login`, `/register` (redirect if authenticated)Protected: `/dashboard`, `/story/create`, `/story/edit/:id`, `/profile/edit`, `/admin`**Adding New Routes:**
1. Add constant to `ROUTES` in `src/utils/constants.js`
2. Add endpoint to `API_ENDPOINTS` if needed
3. Create route in `src/App.jsx`
4. Wrap with `<PrivateRoute>` if authentication required
**Key Pages:**
`MarketplacePage` - Searchable grid + Google Maps view`ProducerProfilePage` - Public producer profile`DashboardPage` - Producer control center`StoryViewer` - Public story view (QR code access)`EditProfilePage` - Template-based profile editor6. Google Maps Integration
API key: `REACT_APP_GOOGLE_MAPS_API_KEY` in `.env`Component: `src/components/marketplace/MapView.jsx`Uses `@react-google-maps/api` with marker clusteringDefault: UK center (lat: 54.5, lng: -3.5), zoom: 6Config in `MAPS_CONFIG` constant7. Styling Conventions
**Custom Tailwind Colors:**
`farmfolio-green`, `farmfolio-darkgreen`, `farmfolio-cream`, `farmfolio-brown`**Custom Animations:**
`animate-fade-in`, `animate-slide-up`, `animate-pulse-soft`**Plugins:**
`@tailwindcss/forms` for form styling`@tailwindcss/typography` for rich text**Path Alias:**
`@` → `./src` (use `import { foo } from '@/utils/bar'`)
8. Environment Variables
**Requirements:**
Prefix: `REACT_APP_*` (configured in `vite.config.js`)Access via `import.meta.env.REACT_APP_*`Consolidated in `src/utils/constants.js` for type-safe access**Key Variables:**
`REACT_APP_API_BASE_URL``REACT_APP_GOOGLE_MAPS_API_KEY`Feature flags9. Development Commands
```bash
npm run dev # Start Vite dev server (port 3000, auto-opens)
npm run build # Production build (outputs to dist/)
npm run preview # Preview production build
npm run lint # Run ESLint
```
10. Common Pitfalls to Avoid
**API Response Shape Inconsistency:**
Backend may return different shapes. Always handle multiple possibilities (see `AuthContext.jsx` lines 56-110).
**FormData Requests:**
Use `createFormData` helperDon't manually set `Content-Type`Endpoint must expect `multipart/form-data`**401 Redirect Loop:**
Axios interceptor auto-redirects on 401. Avoid loops by checking `_retry` flag (already implemented).
**useAuth Outside Provider:**
`useAuth()` throws error if used outside `AuthProvider`. Ensure component is wrapped in provider tree.
**Environment Variable Not Exposed:**
Vite only exposes vars with `REACT_APP_` prefix. Check `vite.config.js`.
11. Deployment Context
**Frontend:** Vercel (SPA routing in `vercel.json`)**Backend API:** Azure (farmfoliodev.azurewebsites.net)**Media Storage:** Azure Blob Storage with CDN**Build Output:** `dist/` with sourcemaps12. Testing Notes
No test runner configured yet. When adding tests:
Consider Vitest (native Vite support) or JestPrioritize API service functions and auth flowMock axios instance for API testsExample Usage
**Adding a new API service:**
```javascript
// src/api/myNewApi.js
import axiosInstance from './axiosConfig';
export const fetchData = async (id) => {
const response = await axiosInstance.get(`${API_ENDPOINTS.MY_NEW_ENDPOINT}/${id}`);
return response.data;
};
```
**Creating a protected route:**
```javascript
// In src/App.jsx
<Route
path={ROUTES.MY_NEW_PAGE}
element={
<PrivateRoute>
<MyNewPage />
</PrivateRoute>
}
/>
```
**Handling file uploads:**
```javascript
import { createFormData } from '@/api/axiosConfig';
const formData = createFormData(
{ title: 'Story Title', CategoryIds: [1, 2] },
filesArray
);
await axiosInstance.post(API_ENDPOINTS.CREATE_STORY, formData);
```
Constraints
Always use `REACT_APP_` prefix for environment variablesNever manually set `Content-Type` for FormData requestsHandle multiple API response shapes defensivelyUse path alias `@` for imports instead of relative pathsFollow custom Tailwind theme (farmfolio colors)Ensure authentication context is available before using `useAuth()`