Skip to content

Add expansion tracking and report-data foundation - #7

Open
Occumed79 wants to merge 3 commits into
mainfrom
expansion-reports-foundation
Open

Occumed79 wants to merge 3 commits into
mainfrom
expansion-reports-foundation

Conversation

@Occumed79

Copy link
Copy Markdown
Owner

Implements the non-visual foundation for the agreed Network Development Hub plan without adding or redesigning any UI.

Expansion tracking API

  • /api/analytics/network-development/expansion
  • range support: week, month, quarter, year, all; optional from/to
  • targets added, stage movement, pricing secured, agreement movement, activations, losses, new providers, new markets, services added, active development, outreach touches
  • current pipeline by stage
  • market-level live/development status
  • activation history
  • service expansion history
  • time-series buckets driven by real timestamps/audit events

Report data API

  • /api/analytics/network-development/report-data
  • current network snapshot
  • outreach status counts
  • previous-period comparison
  • report-ready top markets, services, activations, timeline and pipeline
  • factual highlight strings generated from real data

Export

  • /api/analytics/network-development/activations.csv

No frontend changes are included in this PR. UI work will only proceed from an established external template/reference after explicit approval.

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your trial has ended. Reactivate Greptile to resume code reviews.

@coderabbitai

coderabbitai Bot commented Sep 14, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: 09bb4aa1-dfc4-4ee8-b72b-f8805c183e72


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Sep 14, 2026

Copy link
Copy Markdown

Deploying provider-intelligence-hub with  Cloudflare Pages  Cloudflare Pages

Latest commit: ea82b49
Status: ✅  Deploy successful!
Preview URL: https://a22e9868.provider-intelligence-hub.pages.dev
Branch Preview URL: https://expansion-reports-foundation.provider-intelligence-hub.pages.dev

View logs

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your trial has ended. Reactivate Greptile to resume code reviews.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 04266bde03

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +142 to +145
const activations = procurement
.filter((record) => inPeriod(record.activatedAt, period.start, period.end))
.sort((a, b) => new Date(b.activatedAt || 0).getTime() - new Date(a.activatedAt || 0).getTime());
const losses = procurement.filter((record) => inPeriod(record.lostAt, period.start, period.end));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Derive activation history from immutable events

When an activated record is later moved back to another stage, the procurement PATCH route clears activatedAt (and similarly clears lostAt when reopening a loss), so this current-row lookup retroactively removes the event from prior-period reports and CSV exports. Reactivation also overwrites the timestamp, moving the original activation into the new period; use the persisted stage-change audit events to preserve historical activation and loss dates.

Useful? React with 👍 / 👎.

Comment on lines +149 to +150
const transitions = audits
.filter((event) => event.entityType === "procurement_record" && event.action === "stage_changed" && inPeriod(event.createdAt, period.start, period.end))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Include outreach-driven stage changes

Stage changes performed by syncProcurementFromOutreach are recorded with action stage_changed_from_outreach, not stage_changed, so routine received/signed outreach transitions are omitted from transitions, the timeline, and the contact/agreement KPIs. Include both stage-change action values when building the transition history.

Useful? React with 👍 / 👎.

.sort((a, b) => new Date(b.activatedAt || 0).getTime() - new Date(a.activatedAt || 0).getTime());
const losses = procurement.filter((record) => inPeriod(record.lostAt, period.start, period.end));
const newProviders = providers.filter((provider) => inPeriod(provider.createdAt, period.start, period.end));
const outreachTouches = outreach.filter((record) => inPeriod(record.createdAt || record.sentAt, period.start, period.end));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Exclude unsent drafts from outreach touches

Whenever email or fax delivery fails, the outreach route still creates a draft record, and imported planned drafts are also valid records; filtering every row by createdAt therefore reports these non-deliveries as outreach touches. This overstates both the current and comparison KPIs, so only actual sent/received/follow-up activity should contribute.

Useful? React with 👍 / 👎.

Comment on lines +97 to +100
function marketKey(record: Pick<ProcurementRecord, "city" | "state" | "country">) {
const parts = locationParts(record);
return parts.length ? parts.join(" | ").toLowerCase() : "unlocated";
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Exclude locationless records from market counts

Procurement locations are optional, but this sentinel key is inserted into activationMarkets and later contributes one to kpis.newMarkets and appears as an activated market. Thus any activation without city/state/country falsely reports a newly covered market; skip such records for market metrics instead of grouping them as unlocated.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant