Skill: Cost Reporting

## Overview

7 stars

Best use case

Skill: Cost Reporting is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

## Overview

Teams using Skill: Cost Reporting should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/cost-reporting/SKILL.md --create-dirs "https://raw.githubusercontent.com/heldernoid/agentic-build-templates/main/projects/web-applications/meeting-cost-calculator/skills/cost-reporting/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/cost-reporting/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How Skill: Cost Reporting Compares

Feature / AgentSkill: Cost ReportingStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

## Overview

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

# Skill: Cost Reporting

## Overview

Pattern for generating meeting cost reports including Chart.js line charts, per-attendee breakdowns, share percentages, and exportable markdown summaries. Used by the Meeting Cost Calculator SPA's history detail and copy-summary flows.

## Chart.js Cost-Over-Time Line Chart

```ts
// src/components/CostChart.tsx
import { Line } from 'react-chartjs-2';
import {
  Chart as ChartJS,
  LineElement,
  PointElement,
  LinearScale,
  CategoryScale,
  Filler,
  Tooltip,
} from 'chart.js';
import type { CostPoint } from '../types';

ChartJS.register(LineElement, PointElement, LinearScale, CategoryScale, Filler, Tooltip);

interface Props {
  costHistory: CostPoint[];
  currency?: string;
}

export function CostChart({ costHistory, currency = 'USD' }: Props) {
  const labels = costHistory.map(p => formatElapsed(p.elapsedSeconds));
  const data   = costHistory.map(p => p.cost);

  return (
    <Line
      data={{
        labels,
        datasets: [{
          data,
          borderColor: '#7c3aed',
          backgroundColor: 'rgba(124, 58, 237, 0.08)',
          fill: true,
          tension: 0.3,
          pointRadius: 3,
          pointBackgroundColor: '#7c3aed',
        }],
      }}
      options={{
        responsive: true,
        maintainAspectRatio: false,
        scales: {
          x: { grid: { color: '#e7e5e4' }, ticks: { color: '#a8a29e', font: { size: 11 } } },
          y: {
            grid: { color: '#e7e5e4' },
            ticks: {
              color: '#a8a29e',
              font: { size: 11 },
              callback: (v) =>
                new Intl.NumberFormat('en-US', { style: 'currency', currency, maximumFractionDigits: 0 }).format(Number(v)),
            },
          },
        },
        plugins: {
          tooltip: {
            callbacks: {
              label: (ctx) =>
                new Intl.NumberFormat('en-US', { style: 'currency', currency }).format(ctx.parsed.y),
            },
          },
          legend: { display: false },
        },
      }}
    />
  );
}
```

## Per-Attendee Cost Breakdown

```ts
export interface AttendeeBreakdown {
  attendee: Attendee;
  cost: number;
  sharePercent: number;
}

export function computeBreakdown(
  attendees: Attendee[],
  durationSeconds: number,
): AttendeeBreakdown[] {
  const breakdown = attendees.map(a => ({
    attendee: a,
    cost: a.hourlyRate / 3600 * durationSeconds,
    sharePercent: 0,
  }));

  const total = breakdown.reduce((sum, b) => sum + b.cost, 0);

  return breakdown.map(b => ({
    ...b,
    sharePercent: total > 0 ? Math.round((b.cost / total) * 100) : 0,
  }));
}
```

Shares always sum to approximately 100% because they are rounded independently. For display, this is acceptable. No rounding correction is needed.

## Monthly History Grouping

```ts
export function groupByMonth(meetings: Meeting[]): Record<string, Meeting[]> {
  const groups: Record<string, Meeting[]> = {};
  for (const m of meetings) {
    const label = new Intl.DateTimeFormat('en-US', { year: 'numeric', month: 'long' })
      .format(new Date(m.endedAt));
    (groups[label] ??= []).push(m);
  }
  return groups;
}
```

Render each group as a section header followed by `MeetingCard` items.

## Monthly Stats Summary

```ts
export interface MonthStats {
  count: number;
  totalMinutes: number;
  totalCost: number;
}

export function monthStats(meetings: Meeting[]): MonthStats {
  return {
    count:        meetings.length,
    totalMinutes: Math.round(meetings.reduce((s, m) => s + m.durationSeconds, 0) / 60),
    totalCost:    meetings.reduce((s, m) => s + m.totalCost, 0),
  };
}
```

## Markdown Summary Format

```
## Meeting Cost Summary

Title:      {title}
Date:       {endedAt formatted as "MMMM D, YYYY"}
Duration:   {HH:MM:SS}
Total cost: ${totalCost}
Rate:       ${costPerMin}/min (${totalHourlyRate}/hr combined)

### Attendees

| Name | Rate | Cost | Share |
|------|------|------|-------|
| {name} | ${hourlyRate}/hr | ${cost} | {share}% |

---
Generated by MeetingCost
```

## Clipboard Write

```ts
export async function copyToClipboard(text: string): Promise<boolean> {
  try {
    await navigator.clipboard.writeText(text);
    return true;
  } catch {
    // Fallback for older browsers / non-secure contexts
    const ta = document.createElement('textarea');
    ta.value = text;
    ta.style.position = 'fixed';
    ta.style.opacity = '0';
    document.body.appendChild(ta);
    ta.select();
    const ok = document.execCommand('copy');
    document.body.removeChild(ta);
    return ok;
  }
}
```

## History Export (JSON)

```ts
export function exportHistoryJson(meetings: Meeting[]): void {
  const blob = new Blob([JSON.stringify(meetings, null, 2)], { type: 'application/json' });
  const url  = URL.createObjectURL(blob);
  const a    = Object.assign(document.createElement('a'), {
    href:     url,
    download: `meetingcost-history-${new Date().toISOString().slice(0, 10)}.json`,
  });
  a.click();
  URL.revokeObjectURL(url);
}
```

## History Export (CSV)

```ts
export function exportHistoryCsv(meetings: Meeting[]): void {
  const rows = [
    ['id', 'title', 'startedAt', 'endedAt', 'durationSeconds', 'totalCost', 'attendeeCount'],
    ...meetings.map(m => [
      m.id,
      `"${m.title.replace(/"/g, '""')}"`,
      m.startedAt,
      m.endedAt,
      String(m.durationSeconds),
      m.totalCost.toFixed(2),
      String(m.attendees.length),
    ]),
  ];
  const csv  = rows.map(r => r.join(',')).join('\n');
  const blob = new Blob([csv], { type: 'text/csv' });
  const url  = URL.createObjectURL(blob);
  const a    = Object.assign(document.createElement('a'), {
    href:     url,
    download: `meetingcost-history-${new Date().toISOString().slice(0, 10)}.csv`,
  });
  a.click();
  URL.revokeObjectURL(url);
}
```

Related Skills

cloud-cost-estimator

7
from heldernoid/agentic-build-templates

No description provided.

Skill: Uptime Monitoring

7
from heldernoid/agentic-build-templates

## Overview

Skill: Status Page

7
from heldernoid/agentic-build-templates

## Overview

Skill: unit-conversion

7
from heldernoid/agentic-build-templates

## Overview

Skill: recipe-scaler

7
from heldernoid/agentic-build-templates

## Overview

reading-list

7
from heldernoid/agentic-build-templates

Operate the reading-list API to save, manage, tag, search, and export articles.

email-digest

7
from heldernoid/agentic-build-templates

Configure, test, and troubleshoot the reading-list daily email digest delivered via nodemailer.

websocket-realtime

7
from heldernoid/agentic-build-templates

Use the WebSocket connection in poll-builder to receive live vote updates. Use when you need to stream real-time poll results, monitor a poll for new votes, or build a live dashboard. Triggers include "live results", "real-time updates", "stream votes", "watch poll", or "WebSocket".

poll-builder

7
from heldernoid/agentic-build-templates

Self-hosted poll creation tool with real-time results. Use when you need to create a poll, check vote counts, close a poll, export results, or get the shareable link for a poll. Triggers include "create poll", "vote", "poll results", "survey", "collect votes", "share poll", or any task involving polling or voting.

Skill: personal-finance

7
from heldernoid/agentic-build-templates

## Overview

Skill: csv-import

7
from heldernoid/agentic-build-templates

## Overview

Skill: Syntax Highlighting

7
from heldernoid/agentic-build-templates

## Purpose