Commit Graph
15 Commits
Author SHA1 Message Date
AFFAANh e600642bce Merge phases 3-6: guardrail badges and the analytics panel
Shows which rule halted a campaign, and spend, leads and cost per lead
for yesterday and the last seven days.
2026-08-04 11:11:33 +05:30
AFFAANhandClaude Opus 5 e00884d369 feat(campaigns): show spend, leads and cost per lead
Yesterday and last-7-days panels, the most and least efficient campaign by
cost per lead, and a per-campaign table.

Both windows end yesterday, matching the API: today's figures are partial
and would make every morning read as a collapse in spend.

Costs render as a dash rather than zero when there is nothing to divide
by. A campaign with no leads yet has no cost per lead, and zero would read
as "free".

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 18:17:03 +05:30
AFFAANhandClaude Opus 5 5b0088e95a feat(campaigns): show which guardrail halted a campaign
A campaign that stopped overnight has to be explainable at a glance, so
the rule and the observed value ride on the status tag itself.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 18:04:33 +05:30
AFFAANh 3c5f9e3673 Merge Phase 2: campaign sync, launch and delete controls
Status-aware row actions, an operator token field, sync failure detail on
hover, and a delete action whose confirmation says what is actually about
to be destroyed.
2026-08-03 17:51:10 +05:30
AFFAANhandClaude Opus 5 a054c1dfab fix(campaigns): disable Delete on imported campaigns
The backend refuses it, and the tooltip says where it can be done instead.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 17:45:07 +05:30
AFFAANhandClaude Opus 5 f8a9d5b250 feat(campaigns): add a delete action with a scope-aware confirmation
The dialog says what will actually be destroyed, which differs by case:
a synced MaskanX campaign is deleted on Meta too, an imported one is only
forgotten here, and a draft never reached Meta at all.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 17:33:32 +05:30
AFFAANhandClaude Opus 5 23ce0eae5f feat(campaigns): add sync, launch, pause and stop controls
Row actions are status-aware: Sync only on approved, Launch only on synced
or paused, Pause only on live. Imported campaigns offer no Sync, since they
were authored in Ads Manager.

Launch is the only action behind a confirmation, and the dialog names the
daily budget and says spending begins immediately. It is the last human
checkpoint before money moves.

The operator token is stored in localStorage and sent as X-MaskanX-Operator
on approve, sync, launch, pause and stop. The backend resolves the operator
name from it and ignores any actor in the body, so approval stays
attributable; without a token, launch is refused and the panel says so.

A failed sync shows its stored Meta error on hover rather than a bare status.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 17:17:37 +05:30
AFFAANhandClaude Opus 5 6962334cbd Merge Phase 1 campaign management
Campaigns page with ad-account billing panel, six-step creation wizard,
and Meta ad preview across six placements.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-02 23:36:05 +05:30
AFFAANhandClaude Opus 5 66cfa38076 fix(campaigns): preview and review steps showed empty values
Form.useWatch([], form) tracks getFieldsValue(), which returns {} on a step
with no mounted Form.Item. The Preview and Review steps have none, so on
entering Preview the creative memo recomputed to empty and previewed a blank
ad pointing at example.com, and the Review step rendered "-" for every field.
The submitted payload was always correct — submit() reads the preserved store
via getFieldsValue(true) — so this was a display and preview defect, not data
loss. Pass preserve: true so the watch tracks the whole store.

Also document VITE_META_AD_ACCOUNT_ID. Unset, it silently disables the
billing panel, the missing-payment-method warning, ad preview, and the
account-minimum budget check, with no diagnostic anywhere.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-02 19:45:59 +05:30
AFFAANhandClaude Opus 5 39119163c1 fix(campaigns): wizard sent an empty payload; harden preview URL extraction
Critical: only the active wizard step is mounted, so on the Review step no
Form.Items are registered and form.validateFields() resolved to {}. Every
field was dropped and every submit failed validation server-side. Read the
preserved store with getFieldsValue(true) instead, and validate each step's
fields when leaving it so the required rules actually run.

Security: extractSrc matched src=" anywhere in the body with no scheme or
host check, so a data-src attribute or an earlier <img> could hijack the
match, and javascript:/data:/protocol-relative URLs passed through. Combined
with sandbox="allow-same-origin" a javascript: URL would have inherited the
app origin. It now anchors on the iframe tag and accepts only https URLs on
facebook.com/fbcdn.net, failing closed otherwise. Verified against 14 inputs
including both hijack shapes.

Also: memoise the creative object so PreviewStep does not refetch on every
parent render, type the form values, guard the preview effect against stale
responses, reset the wizard on close, and show an explicit message when no
placement renders.

Moving extractSrc to its own module removes the react-refresh lint
suppression rather than hiding it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-02 18:48:20 +05:30
AFFAANh 07e4c8997e feat(campaigns): add campaign wizard with Meta ad preview 2026-08-02 02:57:41 +05:30
AFFAANh 3d238bcfec feat(campaigns): add campaigns list page with billing panel 2026-08-02 02:41:16 +05:30
AFFAANhandClaude Opus 5 72dcba1e6e fix(campaigns): make budget and guardrails optional in CampaignPayload
PUT /campaigns/{id} is a partial update on the backend, applying only the
fields present in the body. Requiring budget and guardrails in the client
type forced callers to resend both just to change a name. `name` stays
required because the backend request model requires it on POST and PUT.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-02 02:22:08 +05:30
AFFAANh 8145fcb52d feat(campaigns): add frontend campaign API module and types
Adds the typed API client for campaign management (Task 7): campaign.ts
types, campaignApi module wired to the backend's /campaigns endpoints,
and registration in the aggregated api index. This is the API layer
only; the list page and wizard land in Tasks 8/9.
2026-08-01 14:09:08 +05:30
AFFAANhandClaude Opus 5 b42b7406dc Initial commit: MaskanX frontend
Independent React, TypeScript and Vite web application for MaskanX.

Includes the chat console, agent and persona configuration, MCP client
management, model and provider settings, cron jobs, sessions, and
diagnostics.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-01 10:28:39 +05:30