feat(vps): add interactive estimator + refresh Included card
Implements the design captured in
docs/superpowers/specs/2026-04-26-vps-hosting-estimator-design.md.
Estimator (after the hero on /vps-hosting):
- Workload picker (9 chips) recommends a plan; "Not sure" opens a
mini-quiz with a 12-app catalog and a traffic+priority follow-up.
- Recommended-plan card with "or pick another" alternates dropdown.
- Add-ons panel: IPv4 stepper (1-8, $8/extra), Windows BYOL toggle,
4-tier Managed Support radio (Self/Basic/Pro/Pilot @ $0/29/79/99),
5-tier Off-site Backup radio (None/Lite/Standard/Extended/Vault @
$0/5/12/25/59).
- Pilot tier gated to VPS-8+ via plan.features.tier; auto-fallback to
Pro on plan downgrade with snackbar warning.
- Billing cycle toggle (Monthly / Quarterly / Annual) reuses
per-cycle prices already on plan_prices and plan_config_values.
- Sticky footer with live total, "Order this configuration"
(deep-links to /checkout/{plan} with all params), and "Copy share
link" (history.replaceState debounced 300ms).
- Plans-table rows get an "Estimate →" link that pre-fills the
estimator with that plan and scrolls up.
Backend:
- PlanSeeder: each VPS plan gets features.tier (1-32) for gating.
- ConfigOptionSeeder: scope existing Server Management group to
dedicated only; add VPS Managed Support and Off-site Backup
groups with full per-cycle prices.
- routes/marketing.php /vps-hosting: pass addOns + workloadMap +
appExamples Inertia props.
- CheckoutController::show: build prefilledSelections from
?ipv4&windows&managed&backup query params; Vue page hydrates
configSelections from this prop.
Included With All Plans: rewritten to 13 accurate items with
per-line wording (10 Gbps fair-use uplink, ZFS snapshots free,
KVM virtualization, rDNS/PTR control, OOB console/VNC, 99.9%
SLA, etc.) plus a "Coming soon" badge for DDoS protection.
Tests: 10 Pest feature tests in tests/Feature/Marketing/
VpsHostingEstimatorTest.php cover the page props, both new
seeded groups, plan-tier metadata, Server Management dedicated
scope, configGroups attachment, and checkout query-param
pre-fill round-trip. All 10 pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -70,13 +70,13 @@ class ConfigOptionSeeder extends Seeder
|
||||
|
||||
private function seedPresetGroups(): void
|
||||
{
|
||||
// ─── Server Management (all dedicated + VPS plans) ──────────────
|
||||
// ─── Server Management (dedicated only) ─────────────────────────
|
||||
$serverMgmt = PlanConfigGroup::updateOrCreate(
|
||||
['name' => 'Server Management'],
|
||||
[
|
||||
'description' => 'Add managed support to your server.',
|
||||
'mode' => 'preset',
|
||||
'service_type' => null,
|
||||
'service_type' => 'dedicated',
|
||||
'is_active' => true,
|
||||
'sort_order' => 10,
|
||||
],
|
||||
@@ -89,12 +89,66 @@ class ConfigOptionSeeder extends Seeder
|
||||
['label' => 'Fully Managed', 'value' => 'fully_managed', 'monthly' => 60.00],
|
||||
]);
|
||||
|
||||
// Attach to all active dedicated AND vps plans
|
||||
$dedicatedAndVpsPlans = Plan::query()
|
||||
->whereIn('service_type', ['dedicated', 'vps'])
|
||||
// Attach to dedicated plans only (VPS plans use the dedicated VPS Managed Support group below)
|
||||
$dedicatedPlanIds = Plan::query()
|
||||
->where('service_type', 'dedicated')
|
||||
->whereIn('status', ['active', 'internal'])
|
||||
->pluck('id');
|
||||
$serverMgmt->plans()->syncWithoutDetaching($dedicatedAndVpsPlans);
|
||||
$serverMgmt->plans()->sync($dedicatedPlanIds);
|
||||
|
||||
// ─── VPS Managed Support ────────────────────────────────────────
|
||||
// 4-tier radio: Self ($0) / Basic ($29) / Pro ($79) / Pilot ($99, VPS-8+).
|
||||
// Pilot tier gating (min plan tier 8) is enforced in the frontend.
|
||||
$vpsManaged = PlanConfigGroup::updateOrCreate(
|
||||
['name' => 'VPS Managed Support'],
|
||||
[
|
||||
'description' => 'Choose how hands-on you want our team to be with your VPS.',
|
||||
'mode' => 'preset',
|
||||
'service_type' => 'vps',
|
||||
'is_active' => true,
|
||||
'sort_order' => 10,
|
||||
],
|
||||
);
|
||||
|
||||
$vpsManagedOption = $this->seedRadioOption($vpsManaged, 'Managed Support', false, 1);
|
||||
$this->seedValues($vpsManagedOption, [
|
||||
['label' => 'Self-Managed', 'value' => 'self', 'monthly' => 0, 'is_default' => true],
|
||||
['label' => 'Managed Basic', 'value' => 'basic', 'monthly' => 29.00],
|
||||
['label' => 'Managed Pro', 'value' => 'pro', 'monthly' => 79.00],
|
||||
['label' => 'Pilot', 'value' => 'pilot', 'monthly' => 99.00],
|
||||
]);
|
||||
|
||||
$vpsPlanIds = Plan::query()
|
||||
->where('service_type', 'vps')
|
||||
->whereIn('status', ['active', 'internal'])
|
||||
->pluck('id');
|
||||
$vpsManaged->plans()->syncWithoutDetaching($vpsPlanIds);
|
||||
|
||||
// ─── Off-site Backup (VPS) ──────────────────────────────────────
|
||||
// 5-tier radio: None / Lite (7d, 200GB, $5) / Standard (30d, 500GB, $12)
|
||||
// / Extended (90d, 1TB, $25) / Vault (1yr, 2TB, $59).
|
||||
// StorJ-backed, encrypted at rest. Restores included (no egress charge).
|
||||
$vpsBackup = PlanConfigGroup::updateOrCreate(
|
||||
['name' => 'Off-site Backup'],
|
||||
[
|
||||
'description' => 'Encrypted off-site backups stored on StorJ. Daily snapshots, free restores.',
|
||||
'mode' => 'preset',
|
||||
'service_type' => 'vps',
|
||||
'is_active' => true,
|
||||
'sort_order' => 12,
|
||||
],
|
||||
);
|
||||
|
||||
$vpsBackupOption = $this->seedRadioOption($vpsBackup, 'Backup Tier', false, 1);
|
||||
$this->seedValues($vpsBackupOption, [
|
||||
['label' => 'None', 'value' => 'none', 'monthly' => 0, 'is_default' => true],
|
||||
['label' => 'Lite (7-day, 200 GB)', 'value' => 'lite', 'monthly' => 5.00],
|
||||
['label' => 'Standard (30-day, 500 GB)', 'value' => 'standard', 'monthly' => 12.00],
|
||||
['label' => 'Extended (90-day, 1 TB)', 'value' => 'extended', 'monthly' => 25.00],
|
||||
['label' => 'Vault (1-year, 2 TB)', 'value' => 'vault', 'monthly' => 59.00],
|
||||
]);
|
||||
|
||||
$vpsBackup->plans()->syncWithoutDetaching($vpsPlanIds);
|
||||
|
||||
// ─── VPS Add-ons ────────────────────────────────────────────────
|
||||
$vpsAddons = PlanConfigGroup::updateOrCreate(
|
||||
|
||||
Reference in New Issue
Block a user