Commit Graph
19 Commits
Author SHA1 Message Date
AFFAANhandClaude Opus 5 ba5818b2fd fix(campaigns): show the billing panel without VITE_META_AD_ACCOUNT_ID
It falls back to the backend's own configured account when the env var is
unset, which is the default case — no .env ships with the repo. Previously
the Ad account card, and with it balance, spend and minimum daily budget,
just did not appear, with nothing explaining why.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 16:26:49 +05:30
AFFAANhandClaude Opus 5 937c3222d8 feat(campaigns): enable Delete on imported campaigns, with a real warning
The button was disabled, which meant a campaign left over from a failed
connection test could not be cleared from MaskanX at all.

It is enabled now and sends delete_in_meta=true, behind a confirmation
that says plainly the campaign was not created here and that deleting it
removes it from Meta permanently — and tells you to close the dialog if it
is real Ads Manager work.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 15:46:29 +05:30
AFFAANhandClaude Opus 5 f643539c29 fix(campaigns): enable the connection test when it can actually run
Follows the backend split between "can launch" and "can run a connection
test". An account with no payment method still gets the test, because the
test only creates paused objects — and that is the case where confirming
Meta accepts our requests matters most.

The disabled note now names the check that is actually in the way instead
of implying every failed check is.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 13:12:32 +05:30
AFFAANhandClaude Opus 5 096c47dd0c feat(campaigns): show what is blocking a launch, and test the connection
The Campaigns page now opens with a Setup panel listing every check that
can stop a launch — token, ad account, account status, payment method,
Page, operator identity — each failure carrying the sentence that says
what to do about it. A red cross on its own tells the operator nothing
they can act on.

When a Page id is missing, the backend names the Pages the token can
already reach, which turns "set an id" into "use this one".

Once the checks pass, Run connection test builds a real campaign chain in
Meta, confirms it is paused, and deletes it. It sits behind a confirmation
that says plainly what will appear in the ad account and that nothing can
be spent.

VITE_META_AD_ACCOUNT_ID is normalised to undefined when empty so the
backend falls back to META_ADS_ACCOUNT_ID rather than being asked about an
empty account id.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 11:21:31 +05:30
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