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:
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user