| .gitea/workflows | ||
| .github | ||
| .vscode | ||
| backend | ||
| docs | ||
| frontend | ||
| .copilotignore | ||
| .editorconfig | ||
| .gitignore | ||
| dev-rebuild.sh | ||
| docker | ||
| docker-compose.dev.yml | ||
| docker-compose.new.yml | ||
| docker-compose.yml | ||
| package-lock.json | ||
| package.json | ||
| README.md | ||
| run-migration.bat | ||
| run-migration.sh | ||
Costco Grocery List
A full-stack web application for managing grocery shopping lists with role-based access control, image support, and intelligent item classification.
📋 Table of Contents
- Overview
- System Architecture
- Key Features
- Technology Stack
- Data Flow
- Role-Based Access Control
- Getting Started
- API Documentation
- Project Structure
- Development Workflow
- Deployment
🎯 Overview
The Costco Grocery List application provides a collaborative platform for managing household grocery shopping. Users can add items with photos, track quantities, mark items as purchased, and organize items by store zones. The system supports multiple users with different permission levels, making it ideal for families or shared households.
Live Demo: https://costco.nicosaya.com
🏗️ System Architecture
graph TB
subgraph "Client Layer"
A[React 19 SPA]
B[Vite Dev Server]
end
subgraph "Application Layer"
C[Express 5 API]
D[JWT Auth Middleware]
E[RBAC Middleware]
F[Image Processing]
end
subgraph "Data Layer"
G[(PostgreSQL)]
H[grocery_list]
I[users]
J[item_classification]
K[grocery_history]
end
A -->|HTTP/REST| C
C --> D
D --> E
E --> F
C --> G
G --> H
G --> I
G --> J
G --> K
style A fill:#61dafb
style C fill:#259dff
style G fill:#336791
Architecture Layers
-
Presentation Layer (Frontend)
- React 19 with modern hooks (useState, useEffect, useContext)
- Component-based architecture organized by feature
- CSS custom properties for theming
- Axios for HTTP requests with interceptors
-
Business Logic Layer (Backend)
- Express.js REST API
- JWT-based authentication
- Role-based access control (RBAC)
- Image optimization middleware (Sharp)
- Centralized error handling
-
Data Persistence Layer
- PostgreSQL relational database
- Normalized schema with foreign key constraints
- Junction table for item history tracking
- Binary storage for optimized images
✨ Key Features
🔐 Authentication & Authorization
- JWT token-based authentication (1 year expiration)
- Three-tier role system (Viewer, Editor, Admin)
- Secure password hashing with bcrypt
- Token-based session management
📝 Grocery List Management
- Add items with optional images
- Update item quantities
- Mark items as bought/unbought
- View recently bought items (24-hour window)
- Long-press to edit items (mobile-friendly)
🖼️ Image Support
- Upload product images
- Automatic image optimization (800x800px, JPEG 85%)
- Base64 encoding for efficient storage
- 5MB maximum file size
- Support for JPEG, PNG, GIF, WebP
🏪 Smart Organization
- Item classification system (type, group, zone)
- 13 predefined store zones
- Sort by zone, alphabetically, or quantity
- Visual grouping by store location
- Intelligent item suggestions
🔍 Search & Suggestions
- Real-time autocomplete suggestions
- Fuzzy string matching (80% similarity threshold)
- Substring detection for partial matches
- Case-insensitive search
👥 User Management (Admin)
- View all registered users
- Update user roles
- Delete user accounts
- User activity tracking
🛠️ Technology Stack
Frontend
| Technology | Version | Purpose |
|---|---|---|
| React | 19.2.0 | UI framework |
| React Router | 7.9.6 | Client-side routing |
| Axios | 1.13.2 | HTTP client |
| Vite | 7.2.2 | Build tool & dev server |
| TypeScript | 5.9.3 | Type safety |
Backend
| Technology | Version | Purpose |
|---|---|---|
| Node.js | 20.x | Runtime environment |
| Express | 5.1.0 | Web framework |
| PostgreSQL | 8.16.0 | Database |
| JWT | 9.0.2 | Authentication |
| Bcrypt | 3.0.3 | Password hashing |
| Sharp | 0.34.5 | Image processing |
| Multer | 2.0.2 | File upload handling |
DevOps
| Technology | Purpose |
|---|---|
| Docker | Containerization |
| Docker Compose | Multi-container orchestration |
| Gitea Actions | CI/CD pipeline |
| Nginx | Production static file serving |
| Nodemon | Development hot-reload |
🔄 Data Flow
Adding an Item
sequenceDiagram
participant User
participant Frontend
participant API
participant Auth
participant RBAC
participant Database
User->>Frontend: Enter item name & quantity
User->>Frontend: Upload image (optional)
Frontend->>API: POST /list/add (FormData)
API->>Auth: Verify JWT token
Auth->>RBAC: Check role (Editor/Admin)
RBAC->>API: Authorization granted
API->>API: Process & optimize image
API->>Database: Check if item exists
alt Item exists & unbought
Database-->>API: Return existing item
API->>Database: UPDATE quantity
else Item exists & bought
Database-->>API: Return existing item
API->>Database: SET bought=false, UPDATE quantity
else Item doesn't exist
API->>Database: INSERT new item
end
API->>Database: INSERT grocery_history record
Database-->>API: Success
API-->>Frontend: 200 OK {message, addedBy}
Frontend-->>User: Show success message
Frontend->>API: GET /list (refresh)
API->>Database: SELECT unbought items
Database-->>API: Return items
API-->>Frontend: Item list
Frontend-->>User: Update UI
Authentication Flow
sequenceDiagram
participant User
participant Frontend
participant API
participant Database
User->>Frontend: Enter credentials
Frontend->>API: POST /auth/login
API->>Database: SELECT user WHERE username
Database-->>API: User record
API->>API: Compare password hash
alt Valid credentials
API->>API: Generate JWT token
API-->>Frontend: {token, role, username}
Frontend->>Frontend: Store token in localStorage
Frontend->>Frontend: Store role in AuthContext
Frontend->>API: GET /list (with token)
API->>API: Verify token
API-->>Frontend: Protected data
else Invalid credentials
API-->>Frontend: 401 Unauthorized
Frontend-->>User: Show error message
end
Item Classification Flow
graph LR
A[Add/Edit Item] --> B{Classification Exists?}
B -->|Yes| C[Display Existing]
B -->|No| D[Show Empty Form]
C --> E[User Edits]
D --> E
E --> F{Validate Classification}
F -->|Valid| G[Upsert to DB]
F -->|Invalid| H[Show Error]
G --> I[confidence=1.0, source='user']
I --> J[Update Item List]
H --> E
style G fill:#90EE90
style H fill:#FFB6C1
🔒 Role-Based Access Control
Role Hierarchy
Admin (Full Access)
├── User Management
├── Item Management
└── View Access
Editor (Modify Access)
├── Add Items
├── Edit Items
├── Mark as Bought
└── View Access
Viewer (Read-Only)
└── View Lists Only
Permission Matrix
| Feature | Viewer | Editor | Admin |
|---|---|---|---|
| View grocery list | ✅ | ✅ | ✅ |
| View recently bought | ✅ | ✅ | ✅ |
| Get suggestions | ✅ | ✅ | ✅ |
| View classifications | ✅ | ✅ | ✅ |
| Add items | ❌ | ✅ | ✅ |
| Edit items | ❌ | ✅ | ✅ |
| Upload images | ❌ | ✅ | ✅ |
| Mark items bought | ❌ | ✅ | ✅ |
| Update classifications | ❌ | ✅ | ✅ |
| View all users | ❌ | ❌ | ✅ |
| Update user roles | ❌ | ❌ | ✅ |
| Delete users | ❌ | ❌ | ✅ |
Middleware Chain
Protected routes use a middleware chain pattern:
router.post("/add",
auth, // Verify JWT token
requireRole(ROLES.EDITOR, ROLES.ADMIN), // Check role
upload.single("image"), // Handle file upload
processImage, // Optimize image
controller.addItem // Execute business logic
);
🚀 Getting Started
Prerequisites
- Node.js 20.x or higher
- PostgreSQL 8.x or higher
- Docker (optional, recommended)
- Git for version control
Local Development Setup
-
Clone the repository
git clone https://github.com/your-org/costco-grocery-list.git cd costco-grocery-list -
Configure environment variables
Create
backend/.env:# Database Configuration DB_HOST=localhost DB_USER=postgres DB_PASSWORD=your_password DB_DATABASE=grocery_list DB_PORT=5432 # Authentication JWT_SECRET=your_secret_key_here # CORS Configuration ALLOWED_ORIGINS=http://localhost:3000,http://localhost:5173 # Server PORT=5000 -
Start with Docker (Recommended)
# Development mode with hot-reload docker-compose -f docker-compose.dev.yml up # Access application: # Frontend: http://localhost:3000 # Backend: http://localhost:5000 -
Manual Setup (Alternative)
Backend:
cd backend npm install npm run devFrontend:
cd frontend npm install npm run dev -
Database Setup
Create PostgreSQL database and tables:
CREATE DATABASE grocery_list; -- See backend/models/*.js for table schemas
First User Registration
The first registered user should be manually promoted to admin:
UPDATE users SET role = 'admin' WHERE username = 'your_username';
📚 API Documentation
For detailed API documentation including all endpoints, request/response formats, and examples, see API_DOCUMENTATION.md.
Quick Reference
Base URL: http://localhost:5000/api
Common Endpoints:
POST /auth/register- Register new userPOST /auth/login- Authenticate userGET /list- Get all unbought itemsPOST /list/add- Add or update itemPOST /list/mark-bought- Mark item as boughtGET /list/recently-bought- Get items bought in last 24hGET /admin/users- Get all users (Admin only)
Authentication: All protected endpoints require a JWT token in the Authorization header:
Authorization: Bearer <your_jwt_token>
📁 Project Structure
costco-grocery-list/
├── .gitea/
│ └── workflows/
│ └── deploy.yml # CI/CD pipeline configuration
├── backend/
│ ├── constants/
│ │ └── classifications.js # Item type/zone definitions
│ ├── controllers/
│ │ ├── auth.controller.js # Authentication logic
│ │ ├── lists.controller.js # Grocery list logic
│ │ └── users.controller.js # User management logic
│ ├── db/
│ │ └── pool.js # PostgreSQL connection pool
│ ├── middleware/
│ │ ├── auth.js # JWT verification
│ │ ├── rbac.js # Role-based access control
│ │ └── image.js # Image upload & processing
│ ├── models/
│ │ ├── list.model.js # Grocery list database queries
│ │ └── user.model.js # User database queries
│ ├── routes/
│ │ ├── auth.routes.js # Authentication routes
│ │ ├── list.routes.js # Grocery list routes
│ │ ├── admin.routes.js # Admin routes
│ │ └── users.routes.js # User routes
│ ├── app.js # Express app configuration
│ ├── server.js # Server entry point
│ ├── Dockerfile # Backend container config
│ └── package.json
├── frontend/
│ ├── public/ # Static assets
│ ├── src/
│ │ ├── api/
│ │ │ ├── axios.js # Axios instance with interceptors
│ │ │ ├── auth.js # Auth API calls
│ │ │ ├── list.js # List API calls
│ │ │ └── users.js # User API calls
│ │ ├── components/
│ │ │ ├── common/ # Reusable components
│ │ │ ├── forms/ # Form components
│ │ │ ├── items/ # Item-related components
│ │ │ ├── layout/ # Layout components
│ │ │ └── modals/ # Modal dialogs
│ │ ├── constants/
│ │ │ └── roles.js # Role constants
│ │ ├── context/
│ │ │ └── AuthContext.jsx # Authentication context
│ │ ├── pages/
│ │ │ ├── AdminPanel.jsx # Admin user management
│ │ │ ├── GroceryList.jsx # Main grocery list page
│ │ │ ├── Login.jsx # Login page
│ │ │ └── Register.jsx # Registration page
│ │ ├── styles/
│ │ │ ├── theme.css # CSS custom properties
│ │ │ ├── components/ # Component-specific styles
│ │ │ └── pages/ # Page-specific styles
│ │ ├── utils/
│ │ │ ├── PrivateRoute.jsx # Route protection
│ │ │ ├── RoleGuard.jsx # Role-based component guard
│ │ │ └── stringSimilarity.js # Fuzzy matching algorithm
│ │ ├── App.jsx # Root component with routing
│ │ └── main.tsx # Application entry point
│ ├── Dockerfile # Production build (nginx)
│ ├── Dockerfile.dev # Development build (Vite)
│ └── package.json
├── docker-compose.yml # Production compose file
├── docker-compose.dev.yml # Development compose file
├── docker-compose.prod.yml # Local production testing
├── API_DOCUMENTATION.md # Detailed API reference
└── README.md # This file
Key Directories
backend/constants/- Classification definitions (item types, groups, zones)backend/middleware/- Authentication, authorization, and image processingfrontend/src/components/- Organized into 5 categories (common, forms, items, layout, modals)frontend/src/styles/- Theme system with CSS custom properties.gitea/workflows/- CI/CD pipeline for automated deployment
🔨 Development Workflow
Component Organization
Frontend components are organized by feature:
components/
├── common/ # Reusable UI components
│ ├── ErrorMessage.jsx
│ ├── FloatingActionButton.jsx
│ ├── FormInput.jsx
│ ├── SortDropdown.jsx
│ └── UserRoleCard.jsx
├── forms/ # Form components
│ ├── AddItemForm.jsx
│ ├── ClassificationSection.jsx
│ └── ImageUploadSection.jsx
├── items/ # Item-related components
│ ├── GroceryItem.tsx
│ ├── GroceryListItem.jsx
│ └── SuggestionList.tsx
├── layout/ # Layout components
│ ├── AppLayout.jsx
│ └── Navbar.jsx
└── modals/ # Modal dialogs
├── AddImageModal.jsx
├── AddItemWithDetailsModal.jsx
├── ConfirmBuyModal.jsx
├── EditItemModal.jsx
├── ImageModal.jsx
├── ImageUploadModal.jsx
├── ItemClassificationModal.jsx
└── SimilarItemModal.jsx
Each subdirectory has an index.js barrel export for cleaner imports.
Theme System
The application uses CSS custom properties for consistent theming:
/* Theme variables defined in frontend/src/styles/theme.css */
:root {
/* Colors */
--color-primary: #0066cc;
--color-secondary: #6c757d;
--color-success: #28a745;
--color-danger: #dc3545;
/* Spacing */
--spacing-xs: 0.25rem;
--spacing-sm: 0.5rem;
--spacing-md: 1rem;
--spacing-lg: 1.5rem;
/* Typography */
--font-family-base: -apple-system, BlinkMacSystemFont, 'Segoe UI', ...;
--font-size-base: 1rem;
/* And many more... */
}
Code Standards
- Backend: CommonJS modules, async/await for asynchronous operations
- Frontend: ES6 modules, functional components with hooks
- TypeScript: Used for type-safe components (gradually migrating)
- Naming: camelCase for functions/variables, PascalCase for components
- File Structure: Feature-based organization over type-based
Testing
Currently, the project uses manual testing. Automated testing infrastructure is planned for future development.
🚢 Deployment
CI/CD Pipeline
The application uses Gitea Actions for automated deployment:
# .gitea/workflows/deploy.yml
Workflow:
1. Build Stage:
- Install dependencies
- Run tests (if present)
- Build Docker images
- Tag with :latest and :<commit-sha>
- Push to private registry
2. Deploy Stage:
- SSH to production server
- Upload docker-compose.yml
- Pull latest images
- Restart containers
- Prune old images
3. Notify Stage:
- Send deployment status via webhook
Production Architecture
Production Server
├── Nginx Reverse Proxy (Port 80/443)
│ ├── /api → Backend Container (Port 5000)
│ └── /* → Frontend Container (Port 3000)
├── Docker Compose
│ ├── backend:latest (from registry)
│ └── frontend:latest (from registry)
└── PostgreSQL (External, not containerized)
Environment Configuration
Production (docker-compose.yml):
- Pulls pre-built images from registry
- Uses external PostgreSQL database
- Environment configured via
backend.envandfrontend.env - Automatic restart on failure
Development (docker-compose.dev.yml):
- Builds images locally
- Volume mounts for hot-reload
- Uses local
.envfiles - Nodemon for backend, Vite HMR for frontend
Deployment Process
-
Commit and push to
mainbranchgit add . git commit -m "Your commit message" git push origin main -
CI/CD automatically:
- Runs tests
- Builds Docker images
- Pushes to registry
- Deploys to production
-
Manual deployment (if needed):
ssh user@production-server cd /opt/costco-app docker compose pull docker compose up -d --remove-orphans
🗄️ Database Schema
Entity Relationship Diagram
erDiagram
users ||--o{ grocery_list : creates
users ||--o{ grocery_history : adds
grocery_list ||--o{ grocery_history : tracks
grocery_list ||--o| item_classification : has
users {
int id PK
string username UK
string password
string name
string role
}
grocery_list {
int id PK
string item_name
int quantity
boolean bought
bytea item_image
string image_mime_type
int added_by FK
timestamp modified_on
}
item_classification {
int id PK,FK
string item_type
string item_group
string zone
float confidence
string source
}
grocery_history {
int id PK
int list_item_id FK
int quantity
int added_by FK
timestamp added_on
}
Key Relationships
- users → grocery_list: One-to-many (creator relationship)
- users → grocery_history: One-to-many (contributor relationship)
- grocery_list → grocery_history: One-to-many (tracks all additions/modifications)
- grocery_list → item_classification: One-to-one (optional classification data)
🤝 Contributing
Contributions are welcome! Please follow these guidelines:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Development Guidelines
- Follow existing code style and structure
- Add comments for complex logic
- Update documentation for new features
- Test thoroughly before submitting PR
📄 License
This project is licensed under the MIT License - see the LICENSE file for details.
👤 Author
Nico Saya
- Repository: git.nicosaya.com/nalalangan/costco-grocery-list
🙏 Acknowledgments
- React team for the excellent framework
- Express.js community for robust server framework
- PostgreSQL for reliable data persistence
- Sharp for efficient image processing
- All contributors and users of this application
📞 Support
For issues, questions, or feature requests, please:
- Check existing issues in the repository
- Create a new issue with detailed description
- Include steps to reproduce (for bugs)
- Tag appropriately (bug, enhancement, question, etc.)
Last Updated: January 4, 2026