create plan for multi household
This commit is contained in:
parent
1281c91c28
commit
fc887bdc65
865
docs/multi-household-architecture-plan.md
Normal file
865
docs/multi-household-architecture-plan.md
Normal file
@ -0,0 +1,865 @@
|
||||
# Multi-Household & Multi-Store Architecture Plan
|
||||
|
||||
## Executive Summary
|
||||
|
||||
This document outlines the architecture and implementation strategy for extending the application to support:
|
||||
1. **Multiple Households** - Users can belong to multiple households (families, roommates, etc.)
|
||||
2. **Multiple Stores** - Households can manage lists for different store types (Costco, Target, Walmart, etc.)
|
||||
|
||||
## Current Architecture Analysis
|
||||
|
||||
### Existing Schema
|
||||
```sql
|
||||
users (id, username, password, name, role, display_name)
|
||||
grocery_list (id, item_name, quantity, bought, item_image, image_mime_type, added_by, modified_on)
|
||||
grocery_history (id, list_item_id, quantity, added_by, added_on)
|
||||
item_classification (id, item_type, item_group, zone, confidence, source)
|
||||
```
|
||||
|
||||
### Current Limitations
|
||||
- **Single global list** - All users share one grocery list
|
||||
- **No household concept** - Cannot separate different families' items
|
||||
- **Store-specific zones** - Classification system assumes Costco layout
|
||||
- **Single-level roles** - User has same role everywhere (cannot be admin in one household, viewer in another)
|
||||
|
||||
---
|
||||
|
||||
## Design Considerations & Trade-offs
|
||||
|
||||
### Key Questions to Resolve
|
||||
|
||||
#### 1. Item Management Strategy
|
||||
|
||||
**Option A: Shared Item Master (Recommended)**
|
||||
- ✅ **Pro**: Single source of truth for item definitions (name, default image, common classification)
|
||||
- ✅ **Pro**: Consistent item naming across households
|
||||
- ✅ **Pro**: Can build item recommendation system across all households
|
||||
- ✅ **Pro**: Easier to implement smart features (price tracking, common items)
|
||||
- ❌ **Con**: Requires careful privacy controls (who can see which items)
|
||||
- ❌ **Con**: Different households may classify items differently
|
||||
|
||||
**Option B: Per-Household Items**
|
||||
- ✅ **Pro**: Complete household isolation
|
||||
- ✅ **Pro**: Each household fully controls item definitions
|
||||
- ✅ **Pro**: No privacy concerns about item names
|
||||
- ❌ **Con**: Duplicate data across households
|
||||
- ❌ **Con**: Cannot leverage cross-household intelligence
|
||||
- ❌ **Con**: More complex to implement suggestions
|
||||
|
||||
**Option C: Hybrid Approach (RECOMMENDED)**
|
||||
- ✅ **Pro**: Best of both worlds
|
||||
- ✅ **Pro**: Shared item catalog with household-specific classifications
|
||||
- ✅ **Pro**: Privacy-preserving (only households share item usage, not personal data)
|
||||
- **How it works**:
|
||||
- Global `items` table (id, name, default_image, created_at)
|
||||
- Household-specific `household_list` table references item + household
|
||||
- Each household can override classifications per store
|
||||
|
||||
---
|
||||
|
||||
## Proposed Schema Design
|
||||
|
||||
### New Tables
|
||||
|
||||
```sql
|
||||
-- Households (e.g., "Smith Family", "Apartment 5B")
|
||||
CREATE TABLE households (
|
||||
id SERIAL PRIMARY KEY,
|
||||
name VARCHAR(100) NOT NULL,
|
||||
created_at TIMESTAMP DEFAULT NOW(),
|
||||
created_by INTEGER REFERENCES users(id),
|
||||
invite_code VARCHAR(20) UNIQUE NOT NULL, -- Random code for inviting users
|
||||
code_expires_at TIMESTAMP -- Optional expiration
|
||||
);
|
||||
|
||||
-- Store Types (e.g., "Costco", "Target", "Walmart")
|
||||
CREATE TABLE stores (
|
||||
id SERIAL PRIMARY KEY,
|
||||
name VARCHAR(50) NOT NULL UNIQUE,
|
||||
default_zones JSONB, -- Store-specific zone layout
|
||||
created_at TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- User-Household Membership with per-household roles
|
||||
CREATE TABLE household_members (
|
||||
id SERIAL PRIMARY KEY,
|
||||
household_id INTEGER REFERENCES households(id) ON DELETE CASCADE,
|
||||
user_id INTEGER REFERENCES users(id) ON DELETE CASCADE,
|
||||
role VARCHAR(20) NOT NULL CHECK (role IN ('admin', 'user')),
|
||||
joined_at TIMESTAMP DEFAULT NOW(),
|
||||
UNIQUE(household_id, user_id)
|
||||
);
|
||||
|
||||
-- Household-Store Relationship (which stores does this household shop at?)
|
||||
CREATE TABLE household_stores (
|
||||
id SERIAL PRIMARY KEY,
|
||||
household_id INTEGER REFERENCES households(id) ON DELETE CASCADE,
|
||||
store_id INTEGER REFERENCES stores(id) ON DELETE CASCADE,
|
||||
is_default BOOLEAN DEFAULT FALSE, -- Default store for this household
|
||||
UNIQUE(household_id, store_id)
|
||||
);
|
||||
|
||||
-- Master Item Catalog (shared across all households)
|
||||
CREATE TABLE items (
|
||||
id SERIAL PRIMARY KEY,
|
||||
name VARCHAR(255) NOT NULL UNIQUE,
|
||||
default_image BYTEA,
|
||||
default_image_mime_type VARCHAR(50),
|
||||
created_at TIMESTAMP DEFAULT NOW(),
|
||||
usage_count INTEGER DEFAULT 0 -- For popularity tracking
|
||||
);
|
||||
|
||||
-- Household-specific grocery lists (per store)
|
||||
CREATE TABLE household_lists (
|
||||
id SERIAL PRIMARY KEY,
|
||||
household_id INTEGER REFERENCES households(id) ON DELETE CASCADE,
|
||||
store_id INTEGER REFERENCES stores(id) ON DELETE CASCADE,
|
||||
item_id INTEGER REFERENCES items(id) ON DELETE CASCADE,
|
||||
quantity INTEGER NOT NULL DEFAULT 1,
|
||||
bought BOOLEAN DEFAULT FALSE,
|
||||
custom_image BYTEA, -- Household can override item image
|
||||
custom_image_mime_type VARCHAR(50),
|
||||
added_by INTEGER REFERENCES users(id),
|
||||
modified_on TIMESTAMP DEFAULT NOW(),
|
||||
UNIQUE(household_id, store_id, item_id) -- One item per household+store combo
|
||||
);
|
||||
|
||||
-- Household-specific item classifications (per store)
|
||||
CREATE TABLE household_item_classifications (
|
||||
id SERIAL PRIMARY KEY,
|
||||
household_id INTEGER REFERENCES households(id) ON DELETE CASCADE,
|
||||
store_id INTEGER REFERENCES stores(id) ON DELETE CASCADE,
|
||||
item_id INTEGER REFERENCES items(id) ON DELETE CASCADE,
|
||||
item_type VARCHAR(50),
|
||||
item_group VARCHAR(100),
|
||||
zone VARCHAR(100),
|
||||
confidence DECIMAL(3,2) DEFAULT 1.0,
|
||||
source VARCHAR(20) DEFAULT 'user',
|
||||
created_at TIMESTAMP DEFAULT NOW(),
|
||||
updated_at TIMESTAMP DEFAULT NOW(),
|
||||
UNIQUE(household_id, store_id, item_id)
|
||||
);
|
||||
|
||||
-- History tracking (who added what, when, to which household+store list)
|
||||
CREATE TABLE household_list_history (
|
||||
id SERIAL PRIMARY KEY,
|
||||
household_list_id INTEGER REFERENCES household_lists(id) ON DELETE CASCADE,
|
||||
quantity INTEGER NOT NULL,
|
||||
added_by INTEGER REFERENCES users(id),
|
||||
added_on TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
```
|
||||
|
||||
### Indexes for Performance
|
||||
|
||||
```sql
|
||||
-- Household member lookups
|
||||
CREATE INDEX idx_household_members_user ON household_members(user_id);
|
||||
CREATE INDEX idx_household_members_household ON household_members(household_id);
|
||||
|
||||
-- List queries (most common operations)
|
||||
CREATE INDEX idx_household_lists_household_store ON household_lists(household_id, store_id);
|
||||
CREATE INDEX idx_household_lists_bought ON household_lists(household_id, store_id, bought);
|
||||
|
||||
-- Item search
|
||||
CREATE INDEX idx_items_name ON items(name);
|
||||
CREATE INDEX idx_items_usage_count ON items(usage_count DESC);
|
||||
|
||||
-- Classification lookups
|
||||
CREATE INDEX idx_household_classifications ON household_item_classifications(household_id, store_id);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Role System Redesign
|
||||
|
||||
### Dual-Role Hierarchy: System-Wide + Household-Scoped
|
||||
|
||||
```typescript
|
||||
// System-wide roles (app administration)
|
||||
users {
|
||||
id, username, password, name, display_name,
|
||||
role: 'system_admin' | 'user' // Kept for app-wide controls
|
||||
}
|
||||
|
||||
// Household-scoped roles (per-household permissions)
|
||||
household_members {
|
||||
household_id, user_id,
|
||||
role: 'admin' | 'user'
|
||||
}
|
||||
```
|
||||
|
||||
### System-Wide Role Definitions
|
||||
|
||||
| Role | Permissions |
|
||||
|------|-------------|
|
||||
| **system_admin** | Create/delete stores globally, view all households (moderation), manage global item catalog, access system metrics, promote users to system_admin |
|
||||
| **user** | Standard user - can create households, join households via invite, manage own profile |
|
||||
|
||||
### Household-Scoped Role Definitions
|
||||
|
||||
| Role | Permissions |
|
||||
|------|-------------|
|
||||
| **admin** | Full household control: delete household, invite/remove members, change member roles, manage stores, add/edit/delete items, mark bought, upload images, update classifications |
|
||||
| **user** | Standard member: add/edit/delete items, mark bought, upload images, update classifications, view all lists |
|
||||
|
||||
### Role Transition Plan
|
||||
|
||||
**Migration Strategy:**
|
||||
1. Create default household "Main Household"
|
||||
2. Migrate all existing users → household_members (old admins become household admins, others become users)
|
||||
3. Keep existing `users.role` column, update values:
|
||||
- `admin` → `system_admin` (app-wide admin)
|
||||
- `editor` → `user` (standard user)
|
||||
- `viewer` → `user` (standard user)
|
||||
4. Migrate grocery_list → household_lists (all to default household + default store)
|
||||
5. Migrate item_classification → household_item_classifications
|
||||
|
||||
---
|
||||
, systemRole } // System-wide role
|
||||
req.household = { id, name, role } // Household-scoped role
|
||||
req.store = { id, name } // Active store context
|
||||
### Authentication Context
|
||||
|
||||
**Before:**
|
||||
```javascript
|
||||
req.user = { id, username, role }
|
||||
```
|
||||
|
||||
**After:**
|
||||
```javascript
|
||||
req.user = { id, username }
|
||||
req.household = { id, name, role } // Set by household middleware
|
||||
req.store = { id, name } // Set by store middleware
|
||||
```
|
||||
|
||||
### Middleware Chain with systemRole)
|
||||
router.use(auth);
|
||||
|
||||
// 2. Household middleware (validates household access, sets req.household with householdRole)
|
||||
router.use('/households/:householdId', householdAccess);
|
||||
|
||||
// 3. Household role middleware (checks household-scoped permissions)
|
||||
router.post('/add', requireHouseholdRole(['user', 'admin']), controller.addItem);
|
||||
|
||||
// 4. Admin-only household operations
|
||||
router.delete('/:id', requireHouseholdRole(['admin']), controller.deleteHousehold);
|
||||
|
||||
// 5. System admin middleware (for app-wide operations)
|
||||
router.post('/stores', requireSystemRole('system_admin'), controller.createStore
|
||||
|
||||
// 3. Role middleware (checks household-specific role)
|
||||
rouSystem Administration (system_admin only)
|
||||
GET /api/admin/stores // Manage all stores
|
||||
POST /api/admin/stores // Create new store type
|
||||
PATCH /api/admin/stores/:id // Update store
|
||||
DELETE /api/admin/stores/:id // Delete store (if unused)
|
||||
GET /api/admin/households // View all households (moderation)
|
||||
GET /api/admin/items // Manage global item catalog
|
||||
GET /api/admin/metrics // System-wide analytics
|
||||
|
||||
// Household Management (any user can create)
|
||||
GET /api/households // Get all households user belongs to
|
||||
POST /api/households // Create new household (any user)
|
||||
GET /api/households/:id // Get household details
|
||||
PATCH /api/households/:id // Update household (admin only)
|
||||
DELETE /api/households/:id // Delete household (admin only)
|
||||
|
||||
// Household Members
|
||||
GET /api/households/:id/members // List members (all roles)
|
||||
POST /api/households/:id/invite // Generate/refresh invite code (admin only)
|
||||
POST /api/households/join/:inviteCode // Join household via invite code (joins as 'user')
|
||||
PATCH /api/households/:id/members/:userId // Update member role (admin only)
|
||||
DELETE /api/households/:id/members/:userId // Remove member (admin only, or self)
|
||||
|
||||
// Store Management
|
||||
GET /api/stores // Get all available store types
|
||||
GET /api/households/:id/stores // Get stores for household
|
||||
POST /api/households/:id/stores // Add store to household (admin only)
|
||||
DELETE /api/households/:id/stores/:storeId // Remove store from household (admin only)
|
||||
// Store Management
|
||||
GET /api/stores // Get all available stores
|
||||
POST /api/stores // Create custom store (system admin)
|
||||
GET /api/households/:id/stores // Get stores for household
|
||||
POST /api/households/:id/stores // Add store to household (admin+)
|
||||
DELETE /api/households/:id/stores/:storeId // Remove store (admin+)
|
||||
|
||||
// List Operations (now scoped to household + store)
|
||||
GET /api/households/:hId/stores/:sId/list // Get list
|
||||
POST /api/households/:hId/stores/:sId/list/add // Add item
|
||||
PATCH /api/households/:hId/stores/:sId/list/:itemId // Update item
|
||||
DELETE /api/households/:hId/stores/:sId/list/:itemId // Delete item
|
||||
POST /api/households/:hId/stores/:sId/list/:itemId/buy // Mark bought
|
||||
|
||||
// Item Suggestions (across user's households)
|
||||
GET /api/items/suggestions?q=milk // Search master catalog
|
||||
|
||||
// Classifications (per household + store)
|
||||
GET /api/households/:hId/stores/:sId/classifications/:itemId
|
||||
POST /api/households/:hId/stores/:sId/classifications/:itemId
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## React Context Refactoring Pattern
|
||||
|
||||
### Current Pattern (To Be Replaced)
|
||||
|
||||
```jsx
|
||||
// Bad: Context is exported, consumers use it directly
|
||||
export const AuthContext = createContext(null);
|
||||
|
||||
export function AuthProvider({ children }) {
|
||||
const [user, setUser] = useState(null);
|
||||
|
||||
return (
|
||||
<AuthContext.Provider value={{ user, setUser }}>
|
||||
{children}
|
||||
</AuthContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
// Consumer must import context and useContext
|
||||
import { useContext } from 'react';
|
||||
import { AuthContext } from '../context/AuthContext';
|
||||
|
||||
function MyComponent() {
|
||||
const { user, setUser } = useContext(AuthContext);
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### New Pattern (Best Practice)
|
||||
|
||||
```jsx
|
||||
// Good: Context is internal, custom hook is exported
|
||||
const AuthContext = createContext(null); // Not exported!
|
||||
|
||||
export function AuthProvider({ children }) {
|
||||
const [user, setUser] = useState(null);
|
||||
const [token, setToken] = useState(null);
|
||||
|
||||
const login = (userData, authToken) => {
|
||||
setUser(userData);
|
||||
setToken(authToken);
|
||||
};
|
||||
|
||||
const logout = () => {
|
||||
setUser(null);
|
||||
setToken(null);
|
||||
};
|
||||
|
||||
return (
|
||||
<AuthContext.Provider value={{ user, token, login, logout }}>
|
||||
{children}
|
||||
</AuthContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
// Export custom hook instead
|
||||
export function useAuth() {
|
||||
const context = useContext(AuthContext);
|
||||
if (!context) {
|
||||
throw new Error('useAuth must be used within AuthProvider');
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
// Consumer usage - clean and simple
|
||||
import { useAuth } from '../context/AuthContext';
|
||||
|
||||
function MyComponent() {
|
||||
const { user, login, logout } = useAuth();
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### Benefits
|
||||
|
||||
1. **Encapsulation** - Context implementation is hidden, only the hook is public API
|
||||
2. **Type Safety** - Can add TypeScript types to the hook return value
|
||||
3. **Validation** - Hook can check if used within provider (prevents null errors)
|
||||
4. **Cleaner Imports** - One import instead of two (`useContext` + `Context`)
|
||||
5. **Easier Refactoring** - Can change context internals without affecting consumers
|
||||
6. **Standard Pattern** - Aligns with React best practices and popular libraries
|
||||
|
||||
### Implementation Plan
|
||||
|
||||
**Existing Contexts to Refactor:**
|
||||
- `AuthContext` → `useAuth()`
|
||||
- `SettingsContext` → `useSettings()`
|
||||
- `ConfigContext` → `useConfig()` (if still used)
|
||||
|
||||
**New Contexts to Create:**
|
||||
- `HouseholdContext` → `useHousehold()`
|
||||
- `StoreContext` → `useStore()`
|
||||
|
||||
**Migration Steps:**
|
||||
1. Keep old context export temporarily
|
||||
2. Add custom hook export
|
||||
3. Update all components to use hook
|
||||
4. Remove old context export
|
||||
5. Make context `const` internal to file
|
||||
|
||||
---
|
||||
|
||||
## Frontend Architecture Changes
|
||||
|
||||
### Context Structure
|
||||
|
||||
```typescript
|
||||
// AuthContext - User identity
|
||||
{
|
||||
user: { id, username, display_name, systemRole },
|
||||
token: string,
|
||||
login, logout,
|
||||
isSystemAdmin: boolean // Computed from systemRole
|
||||
}
|
||||
|
||||
// HouseholdContext - Active household + household role
|
||||
{
|
||||
activeHousehold: { id, name, role }, // role is 'admin' or 'user'
|
||||
households: Household[],
|
||||
switchHousehold: (id) => void,
|
||||
createHousehold: (name) => void,
|
||||
joinHousehold: (code) => void,
|
||||
isAdmin: boolean // Computed helper: role === 'admin'
|
||||
}
|
||||
|
||||
// StoreContext - Active store
|
||||
{
|
||||
activeStore: { id, name },
|
||||
householdStores: Store[],
|
||||
allStores: Store[], // Available store types (for adding)
|
||||
switchStore: (id) => void,
|
||||
addStore: (storeId) => void // Admin+ onlyme, role },
|
||||
households: Household[],
|
||||
switchHousehold: (id) => void,
|
||||
createHousehold: (name) => void,
|
||||
joinHousehold: (code) => void
|
||||
}
|
||||
|
||||
// StoreContext - Active store
|
||||
{
|
||||
/admin → System admin panel (system_admin only)
|
||||
/admin/stores → Manage store types
|
||||
/admin/households → View all households
|
||||
/admin/items → Global item catalog
|
||||
activeStore: { id, name },
|
||||
householdStores: Store[],
|
||||
switchStore: (id) => void
|
||||
} (Owner)</option>
|
||||
<option value={2}>Work Team (Editor)</option>
|
||||
<option value={3}>Apartment 5B (Viewer)</option>
|
||||
<option>+ Create New Household</option>
|
||||
{user.systemRole === 'system_admin' && (
|
||||
<option>⚙️ System Admin</option>
|
||||
)}
|
||||
</HouseholdDropdown>
|
||||
```
|
||||
|
||||
**Store Tabs** (Within Household)
|
||||
```tsx
|
||||
<StoreTabs householdId={activeHousehold.id}>
|
||||
<Tab active>Costco</Tab>
|
||||
<Tab>Target</Tab>
|
||||
<Tab>Walmart</Tab>
|
||||
{(isAdmin || isOwner) && <Tab>+ Add Store</Tab>} → User settings (personal)
|
||||
```
|
||||
|
||||
### UI Components
|
||||
|
||||
**Household Switcher** (Navbar)
|
||||
```tsx
|
||||
<HouseholdDropdown>
|
||||
<option value={1}>Smith Family</option>
|
||||
<option value={2}>Work Team</option>
|
||||
<option value={3}>Apartment 5B</option>
|
||||
<option>+ Create New Household</option>
|
||||
</HouseholdDropdown>
|
||||
```
|
||||
|
||||
**Store Tabs** (Within Household)
|
||||
```tsx
|
||||
<StoreTabs householdId={activeHousehold.id}>
|
||||
<Tab active>Costco</Tab>
|
||||
<Tab>Target</Tab>
|
||||
<Tab>Walmart</Tab>
|
||||
<Tab>+ Add Store</Tab>
|
||||
</StoreTabs>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Migration Strategy
|
||||
|
||||
### Phase 1: Database Schema (Breaking Change)
|
||||
|
||||
**Step 1: Backup**
|
||||
```bash
|
||||
pg_dump grocery_list > backup_$(date +%Y%m%d).sql
|
||||
```
|
||||
|
||||
**Step 2: Run Migrations**
|
||||
```sql
|
||||
-- 1. Create new tables
|
||||
CREATE TABLE households (...);
|
||||
CREATE TABLE household_members (...);
|
||||
-- ... (all new tables)
|
||||
|
||||
-- 2. Create default household
|
||||
INSERT INTO households (name, created_by, invite_code)
|
||||
VALUES ('Main Household', 1, 'DEFAULT123');
|
||||
|
||||
-- 3. Migrate users → household_members
|
||||
INSERT INTO household_members (household_id, user_id, role)
|
||||
SELECT 1, id,
|
||||
CASE
|
||||
WHEN role = 'admin' THEN 'admin' -- Old admins become household admins
|
||||
ELSE 'user' -- Everyone else becomes standard user
|
||||
END
|
||||
FROM users;
|
||||
|
||||
-- 4. Create default store
|
||||
INSERT INTO stores (name, default_zones)
|
||||
VALUES ('Costco', '{"zones": [...]}');
|
||||
|
||||
-- 5. Link household to store
|
||||
INSERT INTO household_stores (household_id, store_id, is_default)
|
||||
VALUES (1, 1, TRUE);
|
||||
|
||||
-- 6. Migrate items
|
||||
INSERT INTO items (name, default_image, default_image_mime_type)
|
||||
SELECT DISTINCT item_name, item_image, image_mime_type
|
||||
FROM grocery_list;
|
||||
|
||||
-- 7. Migrate grocery_list → household_lists
|
||||
INSERT INTO household_lists (household_id, store_id, item_id, quantity, bought, added_by, modified_on)
|
||||
SELECT
|
||||
1, -- default household
|
||||
1, -- default store
|
||||
i.id,
|
||||
gl.quantity,
|
||||
gl.bought,
|
||||
gl.added_by,
|
||||
gl.modified_on
|
||||
FROM grocery_list gl
|
||||
JOIN items i ON LOWER(i.name) = LOWER(gl.item_name);
|
||||
|
||||
-- 8. Migrate classifications
|
||||
INSERT INTO household_item_classifications
|
||||
(household_id, store_id, item_id, item_type, item_group, zone, confidence, source)
|
||||
SELECT
|
||||
1, 1, i.id,
|
||||
ic.item_type, ic.item_group, ic.zone, ic.confidence, ic.source
|
||||
FROM item_classification ic
|
||||
JOIN grUpdate system roles (keep role column)
|
||||
UPDATE users SET role = 'system_admin' WHERE role = 'admin';
|
||||
UPDATE users SET role = 'user' WHERE role IN ('editor', 'viewer');
|
||||
|
||||
-- 11. Drop old tables (after verification!)
|
||||
-- DROP TABLE grocery_history;
|
||||
-- DROP TABLE item_classification;
|
||||
-- DROP TABLE grocery_listousehold_list_id, quantity, added_by, added_on)
|
||||
SELECT hl.id, gh.quantity, gh.added_by, gh.added_on
|
||||
FROM grocery_history gh
|
||||
JOIN grocery_list gl ON gh.list_item_id = gl.id
|
||||
JOIN items i ON LOWER(i.name) = LOWER(gl.item_name)
|
||||
JOIN household_lists hl ON hl.item_id = i.id AND hl.household_id = 1 AND hl.store_id = 1;
|
||||
|
||||
-- 10. Drop old tables (after verification!)
|
||||
-- DROP TABLE grocery_history;
|
||||
-- DROP TABLE item_classification;
|
||||
-- DROP TABLE grocery_list;
|
||||
-- ALTER TABLE users DROP COLUMN role;
|
||||
```
|
||||
|
||||
### Phase 2: Backend API (Incremental)
|
||||
|
||||
1. ✅ Create new models (households, stores, household_lists)
|
||||
2. ✅ Create new middleware (householdAccess, storeAccess)
|
||||
3. ✅ Create new controllers (households, stores)
|
||||
4. ✅ Add new routes alongside old ones
|
||||
5. ✅ Update list controllers to be household+store aware
|
||||
6. ✅ Deprecate old routes (return 410 Gone)
|
||||
|
||||
### Phase 3: Frontend UI (Incremental)
|
||||
|
||||
1. ✅ **Refactor Context Pattern** (applies to all contexts)
|
||||
- Move `createContext` inside component files (not exported)
|
||||
- Export custom hooks instead: `useAuth()`, `useHousehold()`, `useStore()`, `useSettings()`
|
||||
- Consumers use hooks directly instead of `useContext(ExportedContext)`
|
||||
2. ✅ Create HouseholdContext with `useHousehold()` hook
|
||||
3. ✅ Create StoreContext with `useStore()` hook
|
||||
4. ✅ Refactor existing AuthContext to use custom `useAuth()` hook
|
||||
5. ✅ Refactor existing SettingsContext to use custom `useSettings()` hook
|
||||
6. ✅ Add household switcher to navbar
|
||||
7. ✅ Create household management pages
|
||||
8. ✅ Add store tabs to list view
|
||||
9. ✅ Update all API calls to use household + store IDs
|
||||
7. ✅ Add invite system UI
|
||||
8. ✅ Update settings page to show household-specific settings
|
||||
|
||||
---
|
||||
|
||||
## Advanced Features (Future)
|
||||
|
||||
### 1. Item Sharing & Privacy
|
||||
|
||||
**Levels:**
|
||||
- **Private**: Only visible to your household
|
||||
- **Public**: Available in global item catalog
|
||||
- **Suggested**: Anonymously contribute to shared catalog
|
||||
|
||||
### 2. Smart Features
|
||||
|
||||
**Cross-Household Intelligence:**
|
||||
- "10,000 households buy milk at Costco" → suggest classification
|
||||
- "Items commonly bought together"
|
||||
- Price tracking across stores
|
||||
- Store-specific suggestions
|
||||
|
||||
**Household Patterns:**
|
||||
- "You usually buy milk every 5 days"
|
||||
- "Bananas are typically added by [User]"
|
||||
- Auto-add recurring items
|
||||
|
||||
### 3. Multi-Store Optimization
|
||||
|
||||
**Store Comparison:**
|
||||
- Track which items each household buys at which store
|
||||
- "This item is 20% cheaper at Target"
|
||||
- Generate shopping lists across stores
|
||||
|
||||
**Route Optimization:**
|
||||
- Sort list by store zone
|
||||
- "You can save 15 minutes by shopping in this order"
|
||||
|
||||
### 4. Enhanced Collaboration
|
||||
|
||||
**Shopping Mode:**
|
||||
- Real-time collaboration (one person shops, another adds from home)
|
||||
- Live updates via WebSockets
|
||||
- "John is currently at Costco (aisle 12)"
|
||||
|
||||
**Shopping Lists:**
|
||||
- Pre-planned lists (weekly meal prep)
|
||||
- Recurring lists (monthly bulk buy)
|
||||
- Shared templates between households
|
||||
|
||||
---
|
||||
|
||||
## Implementation Timeline
|
||||
|
||||
### Sprint 1: Foundation (2-3 weeks)
|
||||
- [ ] Design finalization & review
|
||||
- [ ] Create migration scripts
|
||||
- [ ] Implement new database tables
|
||||
- [ ] Test migration on staging data
|
||||
- [ ] Create new models (household, store, household_list)
|
||||
|
||||
### Sprint 2: Backend API (2-3 weeks)
|
||||
- [ ] Implement household management endpoints
|
||||
- [ ] Implement store management endpoints
|
||||
- [ ] Update list endpoints for household+store scope
|
||||
- [ ] Create new middleware (householdAccess, storeAccess)
|
||||
- [ ] Update authentication to remove global role
|
||||
|
||||
### Sprint 3: Frontend Core (2-3 weeks)
|
||||
- [ ] **Refactor Context Pattern** (foundational change):
|
||||
- [ ] Refactor AuthContext to internal context + `useAuth()` hook
|
||||
- [ ] Refactor SettingsContext to internal context + `useSettings()` hook
|
||||
- [ ] Update all components using old context pattern
|
||||
- [ ] Create HouseholdContext with `useHousehold()` hook
|
||||
- [ ] Create StoreContext with `useStore()` hook
|
||||
- [ ] Build household switcher UI
|
||||
- [ ] Build store tabs UI
|
||||
- [ ] Update GroceryList page for new API
|
||||
- [ ] Create household management pages
|
||||
|
||||
### Sprint 4: Member Management (1-2 weeks)
|
||||
- [ ] Implement invite code system
|
||||
- [ ] Build member management UI
|
||||
- [ ] Implement role updates
|
||||
- [ ] Add join household flow
|
||||
|
||||
### Sprint 5: Polish & Testing (1-2 weeks)
|
||||
- [ ] End-to-end testing
|
||||
- [ ] Performance optimization
|
||||
- [ ] Mobile responsiveness
|
||||
- [ ] Documentation updates
|
||||
- [ ] Migration dry-run on production backup
|
||||
|
||||
### Sprint 6: Production Migration (1 week)
|
||||
- [ ] Announce maintenance window
|
||||
- [ ] Run migration on production
|
||||
- [ ] Verify data integrity
|
||||
- [ ] Deploy new frontend
|
||||
- [ ] Monitor for issues
|
||||
|
||||
**Total: 9-14 weeks**
|
||||
|
||||
---
|
||||
|
||||
## Risk Assessment & Mitigation
|
||||
|
||||
### High Risk Areas
|
||||
|
||||
1. **Data Loss During Migration**
|
||||
- **Mitigation**: Full backup, dry-run on production copy, rollback plan
|
||||
|
||||
2. **Breaking Existing Users**
|
||||
- **Mitigation**: Default household preserves current behavior, phased rollout
|
||||
|
||||
3. **Performance Degradation**
|
||||
- **Mitigation**: Proper indexing, query optimization, caching strategy
|
||||
|
||||
4. **Complexity Creep**
|
||||
- **Mitigation**: MVP first (basic households), iterate based on feedback
|
||||
|
||||
### Testing Strategy
|
||||
|
||||
1. **Unit Tests**: All new models and controllers
|
||||
2. **Integration Tests**: API endpoint flows
|
||||
3. **Migration Tests**: Verify data integrity post-migration
|
||||
4. **Load Tests**: Multi-household concurrent access
|
||||
5. **User Acceptance**: Beta test with small group before full rollout
|
||||
|
||||
---
|
||||
|
||||
## Open Questions & Decisions Needed
|
||||
|
||||
### 1. Item Naming Strategy
|
||||
- **Question**: Should "milk" from Household A and "Milk" from Household B be the same item?
|
||||
- **Options**:
|
||||
- Case-insensitive merge (current behavior, recommended)
|
||||
- Exact match only
|
||||
- User prompt for merge confirmation
|
||||
- **Recommendation**: Case-insensitive with optional household override
|
||||
|
||||
### 2. Store Management
|
||||
- **Question**: Should all stores be predefined, or can users create custom stores?
|
||||
- **Options**:
|
||||
- Admin-only store creation (controlled list)
|
||||
- Users can create custom stores (flexible but messy)
|
||||
- Hybrid: predefined + custom
|
||||
- **Recommendation**: Start with predefined stores, add custom later
|
||||
|
||||
### 3. Historical Data
|
||||
- **Question**: When a user leaves a household, what happens to their history?
|
||||
- **Options**:
|
||||
- Keep history, anonymize user
|
||||
- Keep history with user name (allows recovery if re-added)
|
||||
- Delete history
|
||||
- **Recommendation**: Keep history with actual user name preserved
|
||||
- **Rationale**: If user is accidentally removed, their contributions remain attributed correctly when re-added
|
||||
- History queries should JOIN with users table but handle missing users gracefully
|
||||
- Display format: Show user name if still exists, otherwise show "User [id]" or handle as deleted account
|
||||
|
||||
### 4. Invite System
|
||||
- **Question**: Should invite codes expire?
|
||||
- **Options**:
|
||||
- Never expire (simpler)
|
||||
- 7-day expiration (more secure)
|
||||
- Configurable per household
|
||||
- **Recommendation**: Optional expiration, default to never
|
||||
|
||||
### 5. Default Household
|
||||
- **Question**: When user logs in, which household/store do they see?
|
||||
- **Options**:
|
||||
- Last used (remember preference)
|
||||
- Most recently modified list
|
||||
- User-configured default
|
||||
- **Recommendation**: Remember last used in localStorage
|
||||
|
||||
---
|
||||
|
||||
## Summary & Next Steps
|
||||
|
||||
### Recommended Approach: **Hybrid Multi-Tenant Architecture**
|
||||
|
||||
**Core Principles:**
|
||||
1. ✅ Shared item catalog with household-specific lists
|
||||
2. ✅ Per-household roles (not global)
|
||||
3. ✅ Store-specific classifications
|
||||
4. ✅ Invite-based household joining
|
||||
5. ✅ Backward-compatible migration
|
||||
|
||||
### Immediate Actions
|
||||
|
||||
1. **Review & Approve**: Get stakeholder buy-in on this architecture
|
||||
2. **Validate Assumptions**: Confirm design decisions (item sharing, store management)
|
||||
3. **Create Detailed Tickets**: Break down sprints into individual tasks
|
||||
4. **Set Up Staging**: Create test environment with production data copy
|
||||
5. **Begin Sprint 1**: Start with database design and migration scripts
|
||||
|
||||
### Success Metrics
|
||||
|
||||
- ✅ Zero data loss during migration
|
||||
- ✅ 100% existing users migrated to default household
|
||||
- ✅ Performance within 20% of current (queries < 200ms)
|
||||
- ✅ Users can create households and invite others
|
||||
- ✅ Lists properly isolated between households
|
||||
- ✅ Mobile UI remains responsive
|
||||
|
||||
---
|
||||
|
||||
## Appendix A: Example User Flows
|
||||
|
||||
### Creating a Household
|
||||
1. User clicks "Create Household"
|
||||
2. Enters name "Smith Family"
|
||||
3. System generates invite code "SMITH2026"
|
||||
4. User is set as "admin" role (creator is always admin)
|
||||
5. User can share code with family members
|
||||
|
||||
### Joining a Household
|
||||
1. User receives invite code "SMITH2026"
|
||||
2. Navigates to /join/SMITH2026
|
||||
3. Sees "Join Smith Family?"
|
||||
4. Confirms, added as "user" role by default
|
||||
5. Admin can promote to "admin" role if needed
|
||||
|
||||
### Managing Multiple Households
|
||||
1. User belongs to "Smith Family" and "Work Team"
|
||||
2. Navbar shows dropdown: [Smith Family ▼]
|
||||
3. Clicks dropdown, sees both households
|
||||
4. Switches to "Work Team"
|
||||
5. List updates to show Work Team's items
|
||||
6. Store tabs show Work Team's configured stores
|
||||
|
||||
### Adding Item to Store
|
||||
1. User in "Smith Family" household
|
||||
2. Sees store tabs: [Costco] [Target]
|
||||
3. Clicks "Costco" tab
|
||||
4. Adds "Milk" - goes to Costco list
|
||||
5. Switches to "Target" tab
|
||||
6. Adds "Bread" - goes to Target list
|
||||
7. Milk and Bread are separate list entries (same item, different stores)
|
||||
|
||||
---
|
||||
|
||||
## Appendix B: Database Size Estimates
|
||||
|
||||
**Current Single List:**
|
||||
- Users: 10
|
||||
- Items: 200
|
||||
- History records: 5,000
|
||||
|
||||
**After Multi-Household (10 households, 5 stores each):**
|
||||
- Users: 10
|
||||
- Households: 10
|
||||
- Household_members: 30 (avg 3 users per household)
|
||||
- Stores: 5
|
||||
- Household_stores: 50
|
||||
- Items: 500 (some shared, some unique)
|
||||
- Household_lists: 2,500 (500 items × 5 stores)
|
||||
- History: 25,000
|
||||
|
||||
**Storage Impact:** ~5x increase in list records, but items are deduplicated.
|
||||
|
||||
**Query Performance:**
|
||||
- Without indexes: O(n) → O(10n) = 10x slower
|
||||
- With indexes: O(log n) → O(log 10n) = minimal impact
|
||||
|
||||
**Conclusion:** With proper indexing, performance should remain acceptable even at 100+ households.
|
||||
Loading…
Reference in New Issue
Block a user