# medusa-analytics
> Medusa v2 admin analytics extension that adds orders and customers dashboards with custom admin APIs.

## Plugin Overview

`medusa-analytics` adds an **Analytics** extension page to the Medusa Admin with:

- Orders KPIs (total orders, revenue, AOV, pending/cancelled/delivered, returns, exchanges, fulfillment time)
- Orders time-series charts (7 days, 30 days, 12 months)
- Customer KPIs (guest, registered, total, deleted accounts)
- Customer time-series charts (registered vs guest over time)

It solves the need for a lightweight, in-admin analytics dashboard without external BI tools.

### Medusa Compatibility

- Built for **Medusa v2**
- Package versions indicate Medusa `2.12.4` compatibility (`@medusajs/framework`, `@medusajs/medusa`)

## Installation & Setup

### 1) Install

```bash
npm install medusa-analytics
```

```bash
yarn add medusa-analytics
```

### 2) Register plugin in `medusa-config.ts` / `medusa-config.js`

```ts
import { defineConfig } from "@medusajs/framework"

export default defineConfig({
  plugins: [
    {
      resolve: "medusa-analytics",
      options: {},
    },
  ],
})
```

### 3) Build and run

```bash
npx medusa plugin:build
npx medusa develop
```

### 4) Migrations

This plugin does **not** define custom models or migrations. No plugin-specific migration step is required.

## Configuration (`config.ts` / plugin options)

The plugin currently does **not** read or enforce runtime plugin options in source code.

| Option | Type | Required | Default | Description |
|---|---|---|---|---|
| _None implemented_ | - | - | - | No custom option parsing is present in the plugin code. |

Example registration:

```ts
{
  resolve: "medusa-analytics",
  options: {}
}
```

> ⚠️ Note: If you plan to add configurable chart limits (for example `take` size, currency, or timezone), define and document them in typed plugin options.

## Environment Variables

No runtime environment variables are consumed by the implemented analytics code.

| Variable | Required | Purpose | Example |
|---|---|---|---|
| _None in implementation_ | - | - | - |

> ⚠️ Note: `src/modules/README.md` contains generic starter examples using `process.env`, but those are documentation snippets, not active plugin logic.

## REST APIs / Routes

All implemented custom APIs are admin routes under `/admin/analytics/*`.

### 1) `GET /admin/analytics/orders-summary`

Returns orders KPI cards data and status counts (`pending`, `cancelled`, `delivered`) for a selected day range.

**Auth requirement:** Admin authenticated request (admin route context)

#### Query Params

| Name | Type | Required | Description |
|---|---|---|---|
| `days` | `string \| number` | No | Supports `all` (default), `0` (today), or integer day ranges (clamped between 1 and 365). |

#### Response Schema

| Field | Type | Description |
|---|---|---|
| `totalOrders` | `number` | Total orders in range. |
| `totalRevenue` | `number` | Revenue sum excluding cancelled orders. |
| `aov` | `number` | Average order value excluding cancelled orders. |
| `ordersToday` | `number` | Orders created today. |
| `pendingOrders` | `number` | Pending orders count. |
| `cancelledOrders` | `number` | Cancelled orders count. |
| `deliveredOrders` | `number` | Delivered orders count (based on `fulfillments.delivered_at`). |
| `exchangeOrders` | `number` | Exchange order changes in range. |
| `returnOrders` | `number` | Returns in range. |
| `avgFulfillmentTimeHours` | `number \| null` | Average `created_at -> first shipped_at` duration. |
| `statusCounts` | `{ pending?: number; cancelled?: number; delivered?: number }` | Status breakdown used by charts/cards. |

#### Example

```bash
curl -X GET "http://localhost:9000/admin/analytics/orders-summary?days=30" \
  -H "Authorization: Bearer <ADMIN_TOKEN>"
```

### 2) `GET /admin/analytics/orders-over-time`

Returns orders time-series for charting.

**Auth requirement:** Admin authenticated request (admin route context)

#### Query Params

| Name | Type | Required | Description |
|---|---|---|---|
| `period` | `string` | No | `one_week` (default), `one_month`, `12_months`, or `one_year`. |

#### Response Schema

| Field | Type | Description |
|---|---|---|
| `dailyOrders` | `Array<{ date: string; orders_count: number; label?: string }>` | Time-series points; monthly modes include optional month `label`. |

#### Example

```bash
curl -X GET "http://localhost:9000/admin/analytics/orders-over-time?period=one_year" \
  -H "Authorization: Bearer <ADMIN_TOKEN>"
```

### 3) `GET /admin/analytics/customers-summary`

Returns customer summary cards (guest, registered, total, deleted).

**Auth requirement:** Admin authenticated request (admin route context)

#### Query Params

| Name | Type | Required | Description |
|---|---|---|---|
| `days` | `string \| number` | No | Supports `all` (default), `0`, or integer day ranges (clamped between 1 and 365). |

#### Response Schema

| Field | Type | Description |
|---|---|---|
| `guestCount` | `number` | Customers without account in range. |
| `registeredCount` | `number` | Customers with account in range. |
| `totalCount` | `number` | Total customers in range. |
| `deletedAccountsCount` | `number` | Count of soft-deleted customer accounts (`withDeleted: true`). |

#### Example

```bash
curl -X GET "http://localhost:9000/admin/analytics/customers-summary?days=90" \
  -H "Authorization: Bearer <ADMIN_TOKEN>"
```

### 4) `GET /admin/analytics/customers-over-time`

Returns customer time-series split by registered vs guest.

**Auth requirement:** Admin authenticated request (admin route context)

#### Query Params

| Name | Type | Required | Description |
|---|---|---|---|
| `period` | `string` | No | `one_week` (default), `one_month`, `12_months`, or `one_year`. |

#### Response Schema

| Field | Type | Description |
|---|---|---|
| `series` | `Array<{ date: string; label?: string; registered_count: number; guest_count: number }>` | Time-series rows for dual-line chart rendering. |

#### Example

```bash
curl -X GET "http://localhost:9000/admin/analytics/customers-over-time?period=one_month" \
  -H "Authorization: Bearer <ADMIN_TOKEN>"
```

## Services

No custom service classes are implemented in this plugin.

The routes query Medusa data directly via:

- `ContainerRegistrationKeys.REMOTE_QUERY` (`RemoteQueryFunction`)
- Core modules from `Modules.ORDER` and `Modules.CUSTOMER`

## Workflows & Steps (Medusa v2)

No custom workflows or steps are implemented in source code.

## Subscribers / Event Hooks

No event subscribers are implemented in source code.

## Admin UI / Widgets

This plugin ships a custom **Admin route extension** (not widget-zone injection).

### Route: Analytics page

- **Location:** Extensions sidebar as `Analytics` (ChartBar icon)
- **File:** `src/admin/routes/analytics/page.tsx`
- **What it renders:** Module switcher with two dashboards: **Orders** and **Customers**
- **Interactions:** Tab/button switch between modules
- **Data consumed:** Internal fetches from all 4 custom admin analytics APIs

### Orders dashboard

- **File:** `src/admin/routes/analytics/components/OrdersDashboard.tsx`
- **Renders:**
  - KPI cards (orders, revenue, AOV, orders today, pending, cancelled, delivered, exchange, return)
  - Pie chart for order status
  - Pie chart for returns vs exchanges
  - Line chart for orders over time
- **Interactions:**
  - Summary period selector (`all`, `0`, `7`, `30`, `90`)
  - Over-time period selector (`one_week`, `one_month`, `one_year`)
  - Clear filter action
- **Data consumed:**
  - `/admin/analytics/orders-summary`
  - `/admin/analytics/orders-over-time`

### Customers dashboard

- **File:** `src/admin/routes/analytics/components/CustomersDashboard.tsx`
- **Renders:**
  - KPI cards (guest, registered, overall, deleted accounts)
  - Dual-line chart (registered vs guest over time)
- **Interactions:**
  - Count period selector (`all`, `0`, `7`, `30`, `90`)
  - Graph period selector (`one_week`, `one_month`, `one_year`)
  - Clear filter action
- **Data consumed:**
  - `/admin/analytics/customers-summary`
  - `/admin/analytics/customers-over-time`

## Models & Entities

No custom entities/models are defined by this plugin.

It reads existing Medusa entities through query/module APIs:

- `order`
- `fulfillment` (via order fulfillments fields)
- `return`
- `order_change`
- `customer`

## Use Cases & Examples

1. **Track order health in admin**
   - Use `/admin/analytics/orders-summary?days=30` to monitor pending, delivered, and cancelled distribution.

2. **Visualize order demand trends**
   - Use `/admin/analytics/orders-over-time?period=one_year` for monthly trendline analysis.

3. **Monitor customer acquisition mix**
   - Use `/admin/analytics/customers-over-time?period=one_month` to compare guest vs registered growth.

4. **Operational return/exchange visibility**
   - Use `exchangeOrders` and `returnOrders` from `/admin/analytics/orders-summary` to track post-purchase operations.

5. **Review account lifecycle signals**
   - Use `/admin/analytics/customers-summary` to compare total/registered/guest counts and deleted accounts.

## Troubleshooting

### Analytics page does not appear in Admin

- Ensure plugin is registered in `medusa-config.ts`.
- Rebuild plugin: `npx medusa plugin:build`.
- Restart backend and hard refresh admin.

### 401/403 on analytics APIs

- These are `/admin/*` routes; call with valid admin auth token/session.

### Empty charts or zero counts

- Confirm store has orders/customers in selected range.
- Try broader ranges (`days=all`, `period=one_year`).

### Slow response with large datasets

- Current implementation fetches up to `take: 50000` records per query.
- If needed, customize aggregation strategy/pagination for very large stores.

### "Failed to fetch ..." API errors

- Check backend logs for the route-specific error messages.
- Verify module availability and that core services (`ORDER`, `CUSTOMER`) resolve correctly.



