Skip to content

Free to Paid new triggerrs - #836

Merged
charles-ramos merged 17 commits into
production2from
baas-backup-before-delete
Oct 6, 2026
Merged

charles-ramos merged 17 commits into
production2from
baas-backup-before-delete

Conversation

@alyssoncm

@alyssoncm alyssoncm commented Oct 5, 2026 •

Copy link
Copy Markdown

Why

Follow-up to #835 (direct checkout from paywalls). Two moments where a Free user is about to lose something and today sees no offer:

  1. Deleting data: Free has no backups, so "Delete this class?" / "Delete rows" is permanent, and the dialog says nothing about it.
  2. Hitting a plan limit: apps over a limit without payment are blocked (402). The Overview only colored a bar; when blocked, the dashboard redirected to the Overview with an error icon and no explanation.

What changed (one commit each)

Backup before delete (802594c3)

  • On Free only, the delete-class and delete-rows confirmations show: "Free apps don't keep backups, so this can't be recovered. MVP keeps a daily backup for 7 days." + Get daily backups (MVP checkout, "Keep daily backups of your data").
  • Delete works exactly as before. Paid plans see nothing new. Relation "Detach" is skipped (no data removed).
  • New gates: backup_delete_class, backup_delete_rows.

Usage banner on the Overview (5479c5fe)

  • Above the app summary, same thresholds as the existing usage bars:
    • above 70% (yellow) / above 90% (red): "You've used 84% of this month's API requests (21 K of 25 K). At 100%, your app stops responding and your users start getting errors." For storage: "Your database storage is 91% full (235 MB of 0.25 GB)." Mentions how many other limits are close.
    • blocked (100% or the server already answering 402): "Your app stopped responding: it reached its API requests limit. Your app's users are getting errors right now."
  • Never mentions waiting for the next month.
  • Free: button opens the MVP checkout ("Keep your app running"). Paid plans: "See plans" link to Plan Usage.
  • New gate: usage_limit. getUsagePercent extracted from getUsageClassName (same behavior).

Jobs limit → MVP checkout on Free (8a19acbe)

  • "Job limit reached" → "Upgrade plan" now opens the MVP checkout on Free ("Schedule more background jobs", up to 3 jobs). Paid plans still go to Plan Usage (same double-billing caution as below).

Database Profiler → standard checkout (198dbdde)

  • "Buy now" opens the shared direct checkout (Dedicated, yearly preselected, light/dark theme) instead of its own monthly-only Paddle modal. Card shows $400/month billed yearly. -154 lines of duplicated Paddle code.

Logs footer (4420401b)

  • Every log screen ends with "These are the most recent logs. Need older ones? Open a ticket." (older logs only come through support).
  • Access logs on Free also show "Free keeps access logs for 1 day; MVP keeps 7 days." (pricing-page retention) + MVP checkout. New gate: logs_retention.
  • Note: .gitignore's logs/ rule also matches src/dashboard/Data/Logs/ on macOS, so new files there are silently ignored; LogsFooter.react.js was added with -f.
  • Copy conflict to decide separately: the Access empty state says "No Access logs in the last 30 days" for every plan, while pricing says Free keeps 1 day.

Backup notice as a dialog row + column delete (1dc6abbd)

  • The notice is now a regular dialog row ("No backups on Free / Deleted data can't be recovered. MVP keeps a daily backup for 7 days.") with the standard green "Get daily backups" button.
  • Also on "Remove a column?" and "Delete this column?" (new gate backup_delete_column).

Usage priority, sidebar badge, Plan Usage (ab4a72c0, d371571a, latest)

  • Banner picks the most urgent limit first (blocked > red > yellow), then API requests > database > file storage.
  • Sidebar: "84%" pill next to Plan Usage on every app page (yellow >70%, red >90%), same limit as the banner; plan fetched through a 5-minute per-app cache.
  • Plan Usage: same banner on top; on Free, MVP is highlighted with "Recommended for you" (paid plans keep "Most Popular" on Pay As You Go).
  • Refactor: banner → components/UsageLimitBanner; plan list → dashboard/AppPlan/prices.js (breaks an import cycle; AppPlan re-exports it).

Hosting region on Free (ec552b8b)

  • "Change" on Free opens a region picker: Europe → migration Typeform (free); South Korea, India, Australia, Singapore → MVP checkout ("Run your app closer to your users"), then "Request the migration" link to the same form. Paid apps still go straight to the form. New gate: region_change.
  • Assumption to confirm: after paying, the migration is still requested through the Typeform.

Usage warnings by plan (5c81c266)

  • Free: unchanged (yellow >70%, red >90%, blocked at 100%).
  • MVP: always yellow, with the overage price ("Above 100%, extra requests are billed at $5 per 100K"; past 100%: "It keeps running, and …"). Button: Upgrade plan.
  • Pay As You Go: nothing until over its included usage, then a neutral note that the overage is billed + "Upgrade to Dedicated".
  • Dedicated / custom: no warnings. A real 402 is red on any plan. Sidebar pill follows the same rules.
  • Overage prices come from the pricing page FAQ (requests $5/$2 per 100K, database $15/GB, files $1 per 10 GB); please confirm they match billing.

Custom domains start on Pay As You Go (2c23bea8) — fixes a problem shipped in #835

  • Backend (checkBackendPricingPermission.js) only grants canChangeCustomDomain from Pay As You Go up; BaaS free→paid: funnel tracking, direct checkout from paywalls, yearly default #835 sold custom domains on MVP.
  • Free → MVP now sells web hosting on a b4a.app subdomain only; MVP → Pay As You Go sells custom domain + HTTPS.
  • Domains custom-domain paywall and Overview "Enable HTTPS" link to Plan Usage (no second subscription for MVP payers). Plan cards/copy updated.

Direct checkout only for Free (b194f445) — fixes a risk shipped in #835

  • Paywalls show for any plan lacking the permission (e.g. Starter/Solo collaborators, Resource-Based/Partners/Public App features), but the button always opened the MVP checkout → a second subscription, sometimes below the current plan.
  • UpgradeGateButton now checks the plan on click (cached): Free → direct checkout; any other plan or a failed lookup → Plan Usage with ?gate=.

Free over its limit vs paused (7f03e5a7)

  • At 100% a Free app still serves requests until paused: "Your app reached its … limit and can stop responding at any moment. Upgrade now to keep it running."
  • "Stopped responding" only when the plan is "Free Plan - Paused" or the server answers 402. To confirm: is there another BLOCKED signal (e.g. in serverInfo) we should also use?

Not in this PR

  • Usage warnings and next-plan suggestion for legacy plan families (Starter, Basic, Gold, Silver, Advanced, …): waiting for the family table to be confirmed.
  • Direct checkout to the next plan for paid apps (MVP → Pay As You Go): waiting to confirm /save-subscription cancels the old subscription, to avoid double billing.

Please verify

  • Free app: delete a row / a class → the backup box shows; "Get daily backups" opens the checkout over the dialog; closing it returns to the dialog; Delete still works.
  • Free app: Remove a column → same backup row.
  • Paid app: delete dialogs unchanged.
  • Free app above 70% usage → yellow banner on Overview naming the fullest resource; above 90% → red.
  • Free app at the job limit → "Upgrade plan" opens the MVP checkout; on a paid app it goes to Plan Usage.
  • App without Dedicated → Advisors → Database Profiler → "Buy now" opens the Dedicated checkout on yearly; paying still links the plan to the app.
  • Logs (any type) → footer with "Open a ticket"; Access logs on Free → 1 day vs 7 days line + "Upgrade to MVP".
  • Free app above 70% → sidebar shows the % next to Plan Usage on every page; Plan Usage shows the banner and MVP as "Recommended for you".
  • Free app → Overview → Hosting Region → Change: picker; Europe opens the form, Singapore opens the MVP checkout. Paid app: Change opens the form.
  • MVP app at 98% → yellow banner with "$5 per 100K"; at 104% still yellow, "It keeps running". Pay As You Go: no banner until over 100%, then the neutral note with "Upgrade to Dedicated".
  • MVP app → Domains → Custom Domain shows "Upgrade to Pay As You Go" linking to Plan Usage; MVP card no longer promises a custom domain.
  • App on a paid legacy plan without a feature (e.g. Starter → Collaborators): "Upgrade Plan" goes to Plan Usage, no checkout.
  • App blocked (over 100%) → Overview shows the blocked banner with "Upgrade to bring it back".

Tests run locally

  • jest: new usageAlert.test.js (7) + backup cases in UpgradeCheckout.test.js; 29 passing across the upgrade suites.
  • eslint clean on changed files; npm run build passes; madge shows only the pre-existing dashboard/Settings/Util cycle.
  • Not run: the UI in a browser.

🤖 Generated with Claude Code

alyssoncm and others added 5 commits October 5, 2026 17:04
Free apps have no backups, so deleting a class or rows is permanent. The
"Delete this class?" and "Delete rows" confirmations now say so on Free
and link to the MVP checkout ("Keep daily backups of your data"). Delete
still works as before; paid plans see nothing new; relation detach is
skipped since no data is removed.

New gates: backup_delete_class, backup_delete_rows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Apps over a limit without payment are blocked (402), but the Overview only
showed a yellow or red bar. A banner above the app summary now says what
is close to the limit and what happens at 100%, without suggesting the
user wait for the next month:
- above 70% (yellow) and above 90% (red): "You've used 84% of this
  month's API requests (21 K of 25 K). At 100%, your app stops responding
  and your users start getting errors.", plus how many other limits are
  close
- blocked (100% or a 402 from the server): "Your app stopped responding:
  it reached its ... limit. Your app's users are getting errors right now."

Free opens the MVP checkout (new gate usage_limit, "Keep your app
running"); paid plans link to Plan Usage. Thresholds match the existing
usage bars. getUsagePercent is extracted from getUsageClassName.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
"Job limit reached" sent everyone to the plan comparison page, and the
clicks so far did not turn into purchases. On Free the next step is
always MVP, so its "Upgrade plan" now opens the MVP checkout ("Schedule
more background jobs", up to 3 jobs). Paid plans still go to Plan Usage
until upgrades between paid plans are confirmed not to double bill.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The profiler paywall had its own Paddle checkout that always charged
Dedicated monthly ($500) in a plain white modal. "Buy now" now opens the
shared direct checkout (yearly preselected, light/dark theme, "See which
queries slow your app down"), and the card shows $400/month billed
yearly. Removes the duplicated Paddle setup, owner-email lookup and
manual event logging (-154 lines); events now come from the shared
component, same gate db_profiler.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The log screens show only the most recent lines and older ones come
through support, but nothing said so. Every log screen now ends with
"These are the most recent logs. Need older ones? Open a ticket."
On Access logs, Free also sees "Free keeps access logs for 1 day; MVP
keeps 7 days." (the retention on the pricing page) with an MVP checkout
("Debug with a week of access logs"). New gate: logs_retention.

LogsFooter.react.js is added with -f: the repo's "logs/" ignore rule also
matches src/dashboard/Data/Logs on case-insensitive file systems.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@alyssoncm alyssoncm changed the title BaaS: backup offer before deleting data, usage banner near/over limits Free to Paid new triggerrs Oct 5, 2026
alyssoncm and others added 12 commits October 6, 2026 08:42
…mn delete

The backup notice looked like a separate card inside the delete dialogs
(bordered box, different width, plain text link). It is now a regular
dialog row, like "Confirm this action": "No backups on Free / Deleted
data can't be recovered. MVP keeps a daily backup for 7 days." with the
standard green "Get daily backups" button, same as the other paywalls.

Removing a column deletes that field in every row, so the "Remove a
column?" and "Delete this column?" dialogs get the notice too (new gate
backup_delete_column).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The banner talked about whichever limit had the highest percentage, so a
78% file storage beat API requests at 74%. Now it picks the most urgent
level first (blocked, then red above 90%, then yellow above 70%) and,
within the same level, prefers API requests (they run out fastest), then
database, then file storage.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Overview banner only shows on the Overview, but the sidebar is on
every app page. Plan Usage now shows the same limit as the banner (most
urgent first, then API, database, files) as a "84%" pill: yellow above
70%, red above 90%, capped at 100%. Hidden otherwise.

Each page renders its own sidebar, so the plan is fetched through a
5-minute per-app cache (lib/planDataCache) instead of on every
navigation. getMostUrgentUsage is shared with the banner. B4aSidebar
accepts a ready badge element per section besides badgeParams.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Plan Usage only colored the numbers yellow. It now shows the same
  usage banner as the Overview above the plan card (same limit, same
  wording, MVP checkout on Free; no "See plans" link since the user is
  already there).
- On Free, MVP gets the highlight and the green button with a
  "Recommended for you" ribbon: for a Free app near its limits, MVP
  ($15/yr-month) already covers it. Paid plans keep Pay As You Go as
  "Most Popular".
- The banner moves to components/UsageLimitBanner (shared by Overview
  and Plan Usage), and the plan list moves to dashboard/AppPlan/prices.js
  to avoid an import cycle (AppPlan -> banner -> checkout -> AppPlan).
  AppPlan still re-exports prices.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…line

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
"Change" sent every app to the migration Typeform, where Free users pick
South Korea, India, Australia or Singapore only to learn they need a paid
plan. On Free it now opens a region picker in the dashboard:
- Europe (free) goes to the migration form, as before
- the other regions open the MVP checkout ("Run your app closer to your
  users"); after paying, the checkout points to the same form for this
  app ("Request the migration")
Paid apps still go straight to the form. New gate: region_change.

The checkout's post-purchase step now takes any link (nextStepLink);
HTTPS keeps "Open a ticket".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… only over quota

Paid plans keep running over their limits and pay the overage, so "your
app stops responding" was wrong for them:
- Free: unchanged (yellow >70%, red >90%, blocked at 100%).
- MVP: always yellow, with the overage price from the pricing page.
  "You've used 98% of this month's API requests on MVP (490 K of 500 K).
  Above 100%, extra requests are billed at $5 per 100K." Past 100%: "It
  keeps running, and extra requests are billed at $5 per 100K." Button:
  Upgrade plan (Plan Usage).
- Pay As You Go: nothing until over its included usage, then a neutral
  note that the overage is billed ($2 per 100K requests, $15/GB database,
  $1 per 10 GB files) with "Upgrade to Dedicated".
- Dedicated / custom: no usage warnings.
- A real 402 from the server is red on any plan.
The sidebar pill follows the same rules and shows the real percentage
past 100% on paid plans.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The backend only grants canChangeCustomDomain from Pay As You Go up
(Gold, Silver, Dedicated, Custom), but the dashboard sold custom domains
on MVP, so an MVP buyer would stay blocked. Two steps now:
- Free -> MVP: web hosting on a b4a.app subdomain (copy no longer
  mentions your own domain: web hosting headline, MVP card, "Take this
  app to production", Plan Usage upgrade box).
- MVP -> Pay As You Go: custom domain with HTTPS. The Domains custom
  domain paywall and the Overview "Enable HTTPS" link to Plan Usage
  (Pay As You Go highlighted) instead of opening a second subscription
  for someone already paying for MVP. The Pay As You Go card lists
  "Custom domain".
The custom_domain checkout offer is Pay As You Go ("Use your own domain").

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Paywalls follow the backend permissions, so they also show for paying
apps (e.g. Starter/Solo have no collaborators; Resource-Based, Partners
or Public App have none of the paid features). Their button opened the
MVP checkout anyway, which starts a second subscription, sometimes for a
plan below the current one.

UpgradeGateButton now checks the plan (5-minute per-app cache) when
clicked: Free opens the direct checkout as before; any other plan, or a
failed plan lookup, goes to Plan Usage with ?gate=. The click event is
logged in both cases.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A Free app at 100% still serves requests until it is paused, so "Your app
stopped responding" was not true yet (suggested by Charles). Two states:
- over the limit, still running: "Your app reached its API requests limit
  and can stop responding at any moment. Upgrade now to keep it running."
  (red, "Upgrade to keep it running")
- blocked, when the plan is "Free Plan - Paused" or the server answers
  402: the existing "stopped responding" message ("Upgrade to bring it
  back").

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Solo and Starter offer MVP; MVP, Basic, Intermediate and Standard offer
Pay As You Go; Pay As You Go and Advanced offer Dedicated. Dedicated,
Silver, Gold, Platinum and custom plans stay without usage warnings.
Legacy plans show no overage price, since only MVP and Pay As You Go
publish one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Advanced has the same limits and overage prices as Pay As You Go, so it
now names the price of the extra usage instead of only the amount.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@nataliaconde nataliaconde 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.

LGTM

@charles-ramos
charles-ramos merged commit 0c1b78e into production2 Oct 6, 2026
0 of 7 checks passed
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.

3 participants