feat(dedicated): drive bay configurator + sticky-summary fix + reword setup fee

Three changes bundled:

1. Drive bay configurator (3 new ConfigOptionSeeder groups):
   - LFF Drive Bays (3.5") — 7 starter combos from "None" to
     12× 8 TB SATA HDD; attached to R440 / R540 / R740xd LFF
   - SFF Drive Bays (2.5") — 8 starter combos from "None" to
     24× 1.92 TB SATA SSD; attached to R640 / R740 / R740xd SFF
   - NVMe Drive Bays (U.2) — 7 starter combos from "None" to
     16× 2 TB U.2 NVMe; attached to R640 NVMe / R740xd NVMe
   Combos enforce chassis bay-count constraints via labels
   ("R740xd LFF only"); customers wanting heterogeneous setups
   use the post-order ticket flow.

2. Sticky build-summary fix: previously the BuildSummary card
   slid under the Vuetify navbar at scroll. Moved sticky from
   the inner card to the .detail-grid__summary wrapper, removed
   align-items: start so the right grid cell stretches to the
   configurator column's height (giving sticky a tall enough
   container), and offset top by 80px (64px navbar + 16px
   breathing room). Mobile path drops sticky entirely.

3. Setup fee reword — "Hardware acquisition" was leaking our
   cost structure and making the fee feel like procurement
   passthrough. Now reads "Server provisioning & deployment"
   in BuildSummary, and the FAQ describes what the fee covers
   (build, racking, iDRAC config, deployment) without exposing
   margins. Same shift across the non-refundable note: "once
   your build starts" instead of "once hardware is purchased."

Detail page bay-strategy callout updated: drive selection IS now
self-serve, so the callout pivots to "need a custom drive layout?"
pointing customers with mixed-size / hot-spare / RAID-preference
needs to the contact form.

Tests: updated count assertion to 9 groups, added a new test
verifying drive bay groups attach to chassis by bay type.
22/22 of my session's Pest tests pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-26 18:25:33 -04:00
parent be3eaba2a1
commit 61afa4ed14
5 changed files with 129 additions and 24 deletions

View File

@@ -498,6 +498,88 @@ class ConfigOptionSeeder extends Seeder
]);
$gen14Ipv4->plans()->syncWithoutDetaching($gen14AllPlans);
// ─── Dedicated 14th Gen — LFF Drive Bays ────────────────────────
$gen14Lff = PlanConfigGroup::updateOrCreate(
['name' => 'Dedicated 14th Gen — LFF Drive Bays'],
[
'description' => 'Pick a starter drive configuration for the 3.5" LFF bays. Customers needing a heterogeneous setup (mixed sizes per bay, hot spares, etc.) can request a custom layout via the post-order ticket flow.',
'mode' => 'preset',
'service_type' => 'dedicated',
'is_active' => true,
'sort_order' => 50,
],
);
$gen14LffOption = $this->seedRadioOption($gen14Lff, 'LFF Drive Bays', false, 1);
$this->seedValues($gen14LffOption, [
['label' => 'No drives — configure via ticket', 'value' => 'none', 'monthly' => 0, 'is_default' => true],
['label' => '2× 4 TB SATA HDD', 'value' => '2x4tb-hdd', 'monthly' => 24.00],
['label' => '4× 4 TB SATA HDD', 'value' => '4x4tb-hdd', 'monthly' => 48.00],
['label' => '2× 8 TB SATA HDD', 'value' => '2x8tb-hdd', 'monthly' => 40.00],
['label' => '4× 8 TB SATA HDD', 'value' => '4x8tb-hdd', 'monthly' => 80.00],
['label' => '8× 8 TB SATA HDD (R540 / R740xd LFF only)', 'value' => '8x8tb-hdd', 'monthly' => 160.00],
['label' => '12× 8 TB SATA HDD (R740xd LFF only)', 'value' => '12x8tb-hdd', 'monthly' => 240.00],
]);
$gen14LffPlanSlugs = ['r440-4lff', 'r540-8lff', 'r740xd-12lff'];
$gen14LffPlans = Plan::query()->whereIn('slug', $gen14LffPlanSlugs)->pluck('id');
$gen14Lff->plans()->syncWithoutDetaching($gen14LffPlans);
// ─── Dedicated 14th Gen — SFF Drive Bays ────────────────────────
$gen14Sff = PlanConfigGroup::updateOrCreate(
['name' => 'Dedicated 14th Gen — SFF Drive Bays'],
[
'description' => 'Pick a starter drive configuration for the 2.5" SFF bays. Mixed-size setups via post-order ticket.',
'mode' => 'preset',
'service_type' => 'dedicated',
'is_active' => true,
'sort_order' => 51,
],
);
$gen14SffOption = $this->seedRadioOption($gen14Sff, 'SFF Drive Bays', false, 1);
$this->seedValues($gen14SffOption, [
['label' => 'No drives — configure via ticket', 'value' => 'none', 'monthly' => 0, 'is_default' => true],
['label' => '2× 480 GB SATA SSD', 'value' => '2x480gb-ssd', 'monthly' => 20.00],
['label' => '4× 480 GB SATA SSD', 'value' => '4x480gb-ssd', 'monthly' => 40.00],
['label' => '2× 1.92 TB SATA SSD', 'value' => '2x1920gb-ssd', 'monthly' => 36.00],
['label' => '4× 1.92 TB SATA SSD', 'value' => '4x1920gb-ssd', 'monthly' => 72.00],
['label' => '8× 1.92 TB SATA SSD', 'value' => '8x1920gb-ssd', 'monthly' => 144.00],
['label' => '16× 1.92 TB SATA SSD (R740 16-bay / R740xd 24-SFF only)', 'value' => '16x1920gb-ssd', 'monthly' => 288.00],
['label' => '24× 1.92 TB SATA SSD (R740xd 24-SFF only)', 'value' => '24x1920gb-ssd', 'monthly' => 432.00],
]);
$gen14SffPlanSlugs = ['r640-8sff', 'r740-16sff', 'r740xd-24sff'];
$gen14SffPlans = Plan::query()->whereIn('slug', $gen14SffPlanSlugs)->pluck('id');
$gen14Sff->plans()->syncWithoutDetaching($gen14SffPlans);
// ─── Dedicated 14th Gen — NVMe Drive Bays ───────────────────────
$gen14Nvme = PlanConfigGroup::updateOrCreate(
['name' => 'Dedicated 14th Gen — NVMe Drive Bays'],
[
'description' => 'Pick a starter NVMe drive configuration. U.2 bays direct-attach to CPU PCIe lanes — no RAID controller in the data path; software RAID (ZFS / mdraid / btrfs) only.',
'mode' => 'preset',
'service_type' => 'dedicated',
'is_active' => true,
'sort_order' => 52,
],
);
$gen14NvmeOption = $this->seedRadioOption($gen14Nvme, 'NVMe Drive Bays', false, 1);
$this->seedValues($gen14NvmeOption, [
['label' => 'No drives — configure via ticket', 'value' => 'none', 'monthly' => 0, 'is_default' => true],
['label' => '2× 1 TB U.2 NVMe', 'value' => '2x1tb-nvme', 'monthly' => 44.00],
['label' => '4× 1 TB U.2 NVMe', 'value' => '4x1tb-nvme', 'monthly' => 88.00],
['label' => '2× 2 TB U.2 NVMe', 'value' => '2x2tb-nvme', 'monthly' => 96.00],
['label' => '4× 2 TB U.2 NVMe', 'value' => '4x2tb-nvme', 'monthly' => 192.00],
['label' => '8× 2 TB U.2 NVMe (R640 NVMe 10-bay / R740xd NVMe 24-bay)', 'value' => '8x2tb-nvme', 'monthly' => 384.00],
['label' => '16× 2 TB U.2 NVMe (R740xd NVMe 24-bay only)', 'value' => '16x2tb-nvme', 'monthly' => 768.00],
]);
$gen14NvmePlanSlugs = ['r640-10nvme', 'r740xd-24nvme'];
$gen14NvmePlans = Plan::query()->whereIn('slug', $gen14NvmePlanSlugs)->pluck('id');
$gen14Nvme->plans()->syncWithoutDetaching($gen14NvmePlans);
}
/**