Carmen Labs

Sales Dashboard

A complete, working MView built from a three-column sales CSV.

Example: Sales Dashboard

A minimal but complete MView, end to end. Source file:

Month,Client,Total
Jan,Acme,100
Feb,Globex,250
Mar,Initech,175

mview.json

{
  "$schema": "https://schemas.carmenlabs.com/mview/v1/schema.json",
  "id": "sales-dashboard",
  "name": "Sales Dashboard",
  "description": "Executive dashboard generated from sales.csv.",
  "version": "1.0.0",
  "prompt": "Create an executive dashboard from sales.csv showing monthly sales, top clients, and total sales.",
  "runtime": {
    "environment": "web",
    "renderer": "react",
    "version": "^19.0.0"
  },
  "author": "Victor Avila",
  "license": "MIT",
  "icon": "./resources/icon.svg",
  "accepts": ["table"],
  "source": {
    "path": "../sales.csv",
    "contentHash": "sha256:8d969eef6ecad3c29a3a629280e686cff8ca58e3f3f2d4d0c7d7f9f2e2f0f4ab"
  },
  "entry": {
    "parser": "./parser.ts",
    "component": "./Dashboard.tsx"
  },
  "resources": {
    "path": "./resources"
  },
  "settings": {
    "currency": "USD",
    "topClientsLimit": 10
  },
  "metadata": {
    "createdBy": "llm",
    "model": "gpt-5.5",
    "createdAt": "2026-07-11T05:00:00Z"
  }
}

parser.ts

export default async function parse(input: any, context: any) {
  const rows = input.rows ?? [];
 
  const totalSales = rows.reduce((sum: number, row: any) => {
    return sum + Number(row.Total ?? row.total ?? 0);
  }, 0);
 
  const salesByMonth = rows.map((row: any) => ({
    month: row.Month ?? row.month,
    total: Number(row.Total ?? row.total ?? 0)
  }));
 
  const topClients = [...rows]
    .sort((a: any, b: any) => Number(b.Total ?? b.total ?? 0) - Number(a.Total ?? a.total ?? 0))
    .slice(0, context.settings?.topClientsLimit ?? 10);
 
  return { totalSales, salesByMonth, topClients };
}

Dashboard.tsx

import React from "react";
 
export default function Dashboard({ data, settings }: any) {
  return (
    <div style={{ padding: 24, fontFamily: "system-ui, sans-serif" }}>
      <h1>Sales Dashboard</h1>
 
      <section>
        <h2>Total Sales</h2>
        <strong>
          {settings?.currency ?? "USD"} {data.totalSales.toLocaleString()}
        </strong>
      </section>
 
      <section>
        <h2>Sales by Month</h2>
        <ul>
          {data.salesByMonth.map((item: any) => (
            <li key={item.month}>
              {item.month}: {item.total}
            </li>
          ))}
        </ul>
      </section>
 
      <section>
        <h2>Top Clients</h2>
        <table>
          <thead>
            <tr><th>Client</th><th>Total</th></tr>
          </thead>
          <tbody>
            {data.topClients.map((row: any, index: number) => (
              <tr key={index}>
                <td>{row.Client ?? row.client ?? "Unknown"}</td>
                <td>{row.Total ?? row.total ?? 0}</td>
              </tr>
            ))}
          </tbody>
        </table>
      </section>
    </div>
  );
}

Resulting file tree

.mviews/sales-dashboard/
├── mview.json
├── parser.ts
├── Dashboard.tsx
└── resources/
    └── icon.svg

What happens when sales.csv changes

Add a row, and the runtime re-reads the file, re-normalizes it to { columns, rows }, re-runs parse, and re-renders Dashboard with the new data. parser.ts and Dashboard.tsx don't change unless the shape of the CSV itself changes (e.g. a renamed column) — in which case only the parser needs regenerating. See Security Model → Regeneration.

Sales Dashboard

A complete, working MView built from a three-column sales CSV.

Example: Sales Dashboard

A minimal but complete MView, end to end. Source file:

Month,Client,Total
Jan,Acme,100
Feb,Globex,250
Mar,Initech,175

mview.json

{
  "$schema": "https://schemas.carmenlabs.com/mview/v1/schema.json",
  "id": "sales-dashboard",
  "name": "Sales Dashboard",
  "description": "Executive dashboard generated from sales.csv.",
  "version": "1.0.0",
  "prompt": "Create an executive dashboard from sales.csv showing monthly sales, top clients, and total sales.",
  "runtime": {
    "environment": "web",
    "renderer": "react",
    "version": "^19.0.0"
  },
  "author": "Victor Avila",
  "license": "MIT",
  "icon": "./resources/icon.svg",
  "accepts": ["table"],
  "source": {
    "path": "../sales.csv",
    "contentHash": "sha256:8d969eef6ecad3c29a3a629280e686cff8ca58e3f3f2d4d0c7d7f9f2e2f0f4ab"
  },
  "entry": {
    "parser": "./parser.ts",
    "component": "./Dashboard.tsx"
  },
  "resources": {
    "path": "./resources"
  },
  "settings": {
    "currency": "USD",
    "topClientsLimit": 10
  },
  "metadata": {
    "createdBy": "llm",
    "model": "gpt-5.5",
    "createdAt": "2026-07-11T05:00:00Z"
  }
}

parser.ts

export default async function parse(input: any, context: any) {
  const rows = input.rows ?? [];
 
  const totalSales = rows.reduce((sum: number, row: any) => {
    return sum + Number(row.Total ?? row.total ?? 0);
  }, 0);
 
  const salesByMonth = rows.map((row: any) => ({
    month: row.Month ?? row.month,
    total: Number(row.Total ?? row.total ?? 0)
  }));
 
  const topClients = [...rows]
    .sort((a: any, b: any) => Number(b.Total ?? b.total ?? 0) - Number(a.Total ?? a.total ?? 0))
    .slice(0, context.settings?.topClientsLimit ?? 10);
 
  return { totalSales, salesByMonth, topClients };
}

Dashboard.tsx

import React from "react";
 
export default function Dashboard({ data, settings }: any) {
  return (
    <div style={{ padding: 24, fontFamily: "system-ui, sans-serif" }}>
      <h1>Sales Dashboard</h1>
 
      <section>
        <h2>Total Sales</h2>
        <strong>
          {settings?.currency ?? "USD"} {data.totalSales.toLocaleString()}
        </strong>
      </section>
 
      <section>
        <h2>Sales by Month</h2>
        <ul>
          {data.salesByMonth.map((item: any) => (
            <li key={item.month}>
              {item.month}: {item.total}
            </li>
          ))}
        </ul>
      </section>
 
      <section>
        <h2>Top Clients</h2>
        <table>
          <thead>
            <tr><th>Client</th><th>Total</th></tr>
          </thead>
          <tbody>
            {data.topClients.map((row: any, index: number) => (
              <tr key={index}>
                <td>{row.Client ?? row.client ?? "Unknown"}</td>
                <td>{row.Total ?? row.total ?? 0}</td>
              </tr>
            ))}
          </tbody>
        </table>
      </section>
    </div>
  );
}

Resulting file tree

.mviews/sales-dashboard/
├── mview.json
├── parser.ts
├── Dashboard.tsx
└── resources/
    └── icon.svg

What happens when sales.csv changes

Add a row, and the runtime re-reads the file, re-normalizes it to { columns, rows }, re-runs parse, and re-renders Dashboard with the new data. parser.ts and Dashboard.tsx don't change unless the shape of the CSV itself changes (e.g. a renamed column) — in which case only the parser needs regenerating. See Security Model → Regeneration.