# Phase 2 — Medicine Cabinet

**Goal**: Track medicine inventory — what you have, how much of each, and when it expires.

**Depends on**: Phase 0, Phase 1 (medicines)

---

## Deliverables

1. `CabinetItem` MongoDB schema and full CRUD API
2. Aggregate quantity view per medicine
3. Expiry date tracking with visual indicators
4. Medicine cabinet web UI with status indicators

---

## Data Model

### CabinetItem Schema

```typescript
// packages/shared/src/types/cabinet.ts
export interface CabinetItem {
  id: string;
  householdId: string;
  medicineId: string; // Reference to Medicine (generic level)
  medicineName: string; // Denormalized
  medicineStrength: number; // Denormalized for display
  medicineStrengthUnit: StrengthUnit; // Denormalized
  medicineForm: MedicineForm; // Denormalized
  medicineProductId?: string; // Optional reference to MedicineProduct (what was purchased)
  medicineProductBrand?: string; // Denormalized
  concentration?: number; // Denormalized from product (injection vials)
  concentrationUnit?: ConcentrationUnit; // Denormalized from product
  quantity: number; // Current quantity (independent of package size)
  unit: DosageUnit;
  expirationDate?: Date;
  status: CabinetItemStatus;
  notes?: string;
  createdBy: string;
  createdAt: Date;
  updatedAt: Date;
}

export enum CabinetItemStatus {
  ACTIVE = 'active',
  DEPLETED = 'depleted',
  EXPIRED = 'expired',
}
```

### CabinetSummary (Computed, not stored)

```typescript
// Aggregate view — total per medicine across all cabinet items
export interface CabinetSummary {
  medicineId: string;
  medicineName: string;
  medicineStrength: number;
  medicineStrengthUnit: StrengthUnit;
  medicineForm: MedicineForm;
  totalQuantity: number;
  unit: DosageUnit;
  earliestExpiry: Date | null;
  itemCount: number; // How many cabinet items (bottles/boxes)
}
```

### MongoDB Indexes

```javascript
{ householdId: 1, medicineId: 1, status: 1 }
{ householdId: 1, status: 1 }
{ householdId: 1, expirationDate: 1 }
```

---

## API Endpoints

### CabinetModule

| Method | Path                        | Description                                     | Auth   |
| ------ | --------------------------- | ----------------------------------------------- | ------ |
| GET    | `/cabinet`                  | List cabinet items (filtered, paginated)         | member |
| GET    | `/cabinet/summary`          | Aggregate quantities per medicine                | member |
| GET    | `/cabinet/:id`              | Get single cabinet item                          | member |
| POST   | `/cabinet`                  | Add item to cabinet                              | member |
| PATCH  | `/cabinet/:id`              | Update item (quantity, notes, etc.)              | member |
| POST   | `/cabinet/:id/adjust`       | Adjust quantity (add/subtract without full edit) | member |
| DELETE | `/cabinet/:id`              | Delete cabinet item                              | member |
| GET    | `/cabinet/expiring-soon`    | Items expiring within N days                     | member |

### Query Parameters for GET `/cabinet`

```
?medicineId=abc123            # Filter by medicine
&status=active                # Filter by status
&expiringWithin=30            # Days until expiry
&cursor=abc123
&limit=20
```

### Adjust Quantity Request

```typescript
// POST /cabinet/:id/adjust
interface AdjustQuantityRequest {
  delta: number; // Positive to add, negative to subtract
  reason?: string; // e.g., "Correcting count"
}
```

---

## Tasks

### 2.1 — Shared Types & Validation

- Add cabinet types to `packages/shared/src/types/cabinet.ts`
- Zod schemas:
  - `CreateCabinetItemSchema`
  - `UpdateCabinetItemSchema`
  - `AdjustQuantitySchema`
  - `CabinetQuerySchema`

### 2.2 — Mongoose Schema & Repository

- `packages/api/src/modules/cabinet/cabinet.repository.ts`
- `CabinetRepository` with:
  - `findByHousehold(householdId, query)` — filtered, paginated
  - `findById(id, householdId)`
  - `getAggregateSummary(householdId)` — MongoDB aggregation pipeline
  - `create(data)`
  - `update(id, householdId, data)`
  - `adjustQuantity(id, householdId, delta)` — atomic adjust with floor at 0
  - `findExpiringSoon(householdId, withinDays)`
  - `softDelete(id, householdId)`

### 2.3 — Cabinet Service

```typescript
class CabinetService {
  /** Add item, denormalizing medicine and product fields */
  addItem(data: CreateCabinetItem): Promise<CabinetItem>;

  /** Adjust quantity with floor at 0, auto-set depleted status */
  adjustQuantity(id: string, householdId: string, delta: number): Promise<CabinetItem>;

  /** Get aggregate summary */
  getSummary(householdId: string): Promise<CabinetSummary[]>;

  /** Find items expiring within N days */
  getExpiringSoon(householdId: string, withinDays: number): Promise<CabinetItem[]>;
}
```

### 2.4 — Web UI: Medicine Cabinet

- `/medicines/cabinet` page:
  - **Summary view** (default): aggregated per medicine
    - Medicine name, total quantity, earliest expiry
    - Expand to see individual items (bottles/boxes)
  - **Detail view**: all individual cabinet items with status filter
  - Each item shows: quantity, unit, expiry date, status badge
  - Color-coded expiry: green (>30 days), yellow (7-30 days), red (<7 days), bold red (expired)
  - Quick actions: adjust quantity (+/-), delete
  - "Add to Cabinet" form with medicine search/select
- `/medicines/[id]` detail page:
  - Inventory section showing cabinet items for that medicine
  - Adjust and delete actions inline

---

## Acceptance Criteria

- [x] Can add items to cabinet linked to medicines
- [x] Aggregate summary shows total quantity per medicine
- [x] Quantity adjustments are atomic and floor at 0
- [x] Items auto-transition to `depleted` when quantity reaches 0
- [x] Expiring-soon endpoint returns items within N days
- [x] Web UI shows color-coded expiry indicators
- [x] All cabinet queries are scoped to `householdId`
- [x] Medicine detail page shows inventory for that medicine
- [x] Concentration is denormalized from product for injection vials
