Captures the brainstorm: 8 new Dell 14th-gen chassis SKUs alongside
existing rack inventory, per-chassis configurator at
/dedicated-servers/{slug}, 5-stage post-order build tracker,
tiered setup fees waived at Semi-Annual+ cycles.
User approved design + skipped spec-review gate. Implementation
phasing in 4 commits per the spec.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
276 lines
13 KiB
Markdown
276 lines
13 KiB
Markdown
# Dedicated Server Lineup — Design Spec
|
||
|
||
**Date:** 2026-04-26
|
||
**Status:** Approved (skip user-review gate per owner override — implementation starting immediately)
|
||
**Owner:** Andrew
|
||
**Repo:** `EZSCALE/website`
|
||
|
||
## Goals
|
||
|
||
1. Ship the **new Dell 14th-gen dedicated server lineup** (8 chassis SKUs sourced from SaveMyServer per order) on `/dedicated-servers`.
|
||
2. Re-surface the **existing 12th/13th-gen rack inventory** (currently `status: hidden` in the seeder) so it's visible alongside the new line as instant-deploy "in stock" SKUs.
|
||
3. Build an interactive **per-chassis configurator** at `/dedicated-servers/{slug}` covering CPU, RAM, per-bay drives, OS, bandwidth, networking, IPv4.
|
||
4. Add a **5-stage post-order build status tracker** (Ordered → Hardware acquired → Assembly → Racked → Deployed) visible to the customer and editable by admins.
|
||
5. Wire **tiered setup fees** that waive automatically on Semi-Annual / Annual cycles.
|
||
|
||
## Non-goals (deferred)
|
||
|
||
- **C from Q6: Two-preset cards** (`Starter` + `Performance` per chassis) — v1.1, after first-month signal on what people actually buy
|
||
- **Workload bundles page** — v2 if pricing-positioning-vs-budget-bundlers keeps coming up in support
|
||
- **Automated SaveMyServer order placement** — admin-driven for v1
|
||
- **Live capacity check at order time** — buffer-stock model not in v1
|
||
- **Multi-region (currently Atlanta-only)** — `Location` selector renders but only has one option
|
||
- **Custom-quote builder for non-catalog requests** — uses existing `/contact` form
|
||
- **Hetzner comparison rows** — placeholder; fills in when separate research lands
|
||
|
||
## Sources of truth
|
||
|
||
| Asset | Path | Role |
|
||
|---|---|---|
|
||
| Catalog JSON | `docs/integrations/savemyserver/dedicated-server-catalog.json` | 27 raw SaveMyServer products, drive options, BOM costs |
|
||
| Build sheets | `../infrastructure/docs/dedicated-server-configurations.md` | Curated 8-product BOMs, EZSCALE Standard Build (locked baseline) |
|
||
| Pricing | `../infrastructure/docs/dedicated-server-pricing-2026q2.md` | Customer-facing monthly prices, margin math, drive add-on tables |
|
||
| Configurator design | `../infrastructure/docs/superpowers/specs/2026-04-24-dedicated-server-gen14-configurator-design.md` | 15 configurable option groups, cross-server compliance matrix |
|
||
| Competitor research | `../infrastructure/docs/competitors-atlanta-2026.md` | ColoCrossing/GTHost positioning baseline |
|
||
| Rack reality | `../infrastructure/docs/reference/rack-atl.md` | Existing customer-rentable rack slots |
|
||
|
||
## EZSCALE Standard Build (locked baseline — applies to all 8 14th-gen chassis)
|
||
|
||
Non-negotiable per `dedicated-server-configurations.md`:
|
||
|
||
| Component | Selection |
|
||
|---|---|
|
||
| CPU | 2× Intel Xeon Gold 6230 (40C / 2.10 GHz) |
|
||
| RAM | 32 GB DDR4-2666 ECC RDIMM (2× 16 GB) |
|
||
| Boot | Dell BOSS Card with 2× 240 GB M.2 SSDs (mirrored) — **all main bays free for data** |
|
||
| Storage controller | PERC HBA330 (Non-RAID, IT mode) — N/A on R740xd NVMe (pure-PCIe) |
|
||
| iDRAC | iDRAC9 Enterprise License |
|
||
| Power | Dual redundant PSU |
|
||
| Network | 1× 1 GbE onboard + 1× 10 Gbps SFP+ |
|
||
| Drive trays | Pre-installed in every bay |
|
||
| OS | No OS (customer choice via post-provisioning panel) |
|
||
| Bandwidth | 1 Gbps unmetered uplink |
|
||
| IPv4 | 1× included |
|
||
| IPv6 | /64 included |
|
||
|
||
## The 8 14th-gen chassis
|
||
|
||
| Slug | Title | Form | Bays | Std build cost | Starting $/mo | Setup tier |
|
||
|---|---|---|---|---|---|---|
|
||
| `r440-4lff` | R440 4-Bay 3.5" LFF (1U) | 1U | 4× LFF | $1,690.61 | $119 | 2 ($349) |
|
||
| `r540-8lff` | R540 8-Bay 3.5" LFF (2U) | 2U | 8× LFF | $1,877.03 | $159 | 3 ($549) |
|
||
| `r640-8sff` | R640 8-Bay 2.5" SFF (1U) | 1U | 8× SFF | $1,411.83 | $179 | 2 ($349) |
|
||
| `r740-16sff` | R740 16-Bay 2.5" SFF (2U) | 2U | 16× SFF | $1,704.26 | $229 | 3 ($549) |
|
||
| `r740xd-24sff` | R740xd 24-Bay 2.5" SFF (2U) | 2U | 24× SFF | $1,919.73 | $279 | 3 ($549) |
|
||
| `r740xd-12lff` | R740xd 12-Bay 3.5" LFF (2U) | 2U | 12× LFF | $2,148.96 | $249 | 3 ($549) |
|
||
| `r640-10nvme` | R640 10-Bay U.2 NVMe (1U) | 1U | 10× NVMe | $1,606.79 | $239 | 4 ($799) |
|
||
| `r740xd-24nvme` | R740xd 24-Bay U.2 NVMe (2U) | 2U | 24× NVMe | $2,275.44 | $279 | 4 ($799) |
|
||
|
||
Setup tiers (per Q5 brainstorm):
|
||
|
||
| Tier | Chassis class | Setup fee |
|
||
|---|---|---|
|
||
| 1 | Single-socket Budget (R240/R340) — *not in v1 lineup* | $149 |
|
||
| 2 | Dual-socket 1U | $349 |
|
||
| 3 | Dual-socket 2U | $549 |
|
||
| 4 | NVMe / Quad-socket Enterprise | $799 |
|
||
|
||
**Setup fee waived on Semi-Annual (6 mo) and Annual (12 mo) cycles. Non-refundable once hardware is purchased** (typically within 24h of order). Rental fees stay under the 14-day money-back guarantee.
|
||
|
||
## Architecture
|
||
|
||
### Routes
|
||
|
||
```
|
||
/dedicated-servers ← catalog landing (chassis grid)
|
||
/dedicated-servers/{slug} ← per-chassis detail + configurator
|
||
|
||
account.<domain>/services/{service} ← existing route, gets BuildStatusPanel
|
||
admin.<domain>/orders/{order}/build ← admin milestone updater
|
||
```
|
||
|
||
### Database
|
||
|
||
**1. Migration: `add_setup_fee_to_plans_table`**
|
||
|
||
```php
|
||
$table->decimal('setup_fee', 10, 2)->default(0)->after('price');
|
||
```
|
||
|
||
**2. Migration: `create_service_build_milestones_table`**
|
||
|
||
```php
|
||
Schema::create('service_build_milestones', function (Blueprint $table): void {
|
||
$table->id();
|
||
$table->foreignId('subscription_id')->constrained()->cascadeOnDelete();
|
||
$table->string('stage'); // 'ordered' | 'hardware_acquired' | 'assembly' | 'racked' | 'deployed'
|
||
$table->timestamp('reached_at')->nullable();
|
||
$table->text('note')->nullable();
|
||
$table->foreignId('updated_by')->nullable()->constrained('users');
|
||
$table->timestamps();
|
||
$table->unique(['subscription_id', 'stage']);
|
||
});
|
||
```
|
||
|
||
**3. PlanSeeder updates**
|
||
|
||
- Re-enable existing 12th/13th-gen rows: `status: hidden` → `status: active`. Stock counts preserved.
|
||
- Insert 8 new 14th-gen rows with: `features` JSON containing `cpu`, `ram`, `storage`, `bays`, `tier` (chassis tier number), `lead_time_days: '7-10'`, `generation: '14th-gen'`. Setup fee per the table above.
|
||
|
||
**4. ConfigOptionSeeder updates**
|
||
|
||
Add a `Dedicated Gen14 Configurator` group cluster per `2026-04-24-dedicated-server-gen14-configurator-design.md`:
|
||
|
||
- `dedicated_server_gen14_cpu` (standard CPU group — R440/R540/R640/R740)
|
||
- `dedicated_server_gen14_cpu_r740xd` (R740xd-only CPU group)
|
||
- `dedicated_server_gen14_ram` (RAM tiers)
|
||
- `dedicated_server_gen14_os` (OS selector)
|
||
- `dedicated_server_gen14_bandwidth` (bandwidth tiers)
|
||
- `dedicated_server_gen14_networking` (private networking add-on)
|
||
- `dedicated_server_gen14_ipv4` (IPv4 block size)
|
||
- `dedicated_server_gen14_location` (Atlanta only for v1)
|
||
- `dedicated_server_gen14_lff_bays_1_4` (drive selection bays 1-4 LFF)
|
||
- `dedicated_server_gen14_lff_bays_5_8` (drive selection bays 5-8 LFF)
|
||
- `dedicated_server_gen14_r740xd_lff_bays` (R740xd 12-bay LFF)
|
||
- `dedicated_server_gen14_sff_slots_1_8` (SFF slots 1-8)
|
||
- `dedicated_server_gen14_sff_slots_9_16` (SFF slots 9-16, R740 only)
|
||
- `dedicated_server_gen14_sff_slots_17_24` (SFF slots 17-24, R740xd SFF only)
|
||
- `dedicated_server_gen14_nvme_slots` (U.2 NVMe slots)
|
||
|
||
Attach each sub-group only to applicable chassis per the cross-server compliance matrix.
|
||
|
||
### Frontend file map
|
||
|
||
```
|
||
resources/ts/Pages/Marketing/
|
||
├─ DedicatedServers.vue (REWRITE)
|
||
└─ DedicatedServerDetail.vue (NEW)
|
||
|
||
resources/ts/Pages/Account/Services/
|
||
└─ Show.vue (extend with BuildStatusPanel)
|
||
|
||
resources/ts/Pages/Admin/Orders/
|
||
└─ BuildTracker.vue (NEW)
|
||
|
||
resources/ts/Components/Marketing/Dedicated/
|
||
├─ ChassisCard.vue (NEW)
|
||
├─ GenerationFilter.vue (NEW)
|
||
├─ DedicatedConfigurator/
|
||
│ ├─ index.vue
|
||
│ ├─ CpuSelector.vue
|
||
│ ├─ RamSelector.vue
|
||
│ ├─ DriveBayPicker.vue
|
||
│ ├─ OsSelector.vue
|
||
│ ├─ BandwidthSelector.vue
|
||
│ ├─ NetworkingSelector.vue
|
||
│ ├─ Ipv4Selector.vue
|
||
│ └─ ConfiguratorFooter.vue
|
||
└─ BuildStatusPanel.vue (used by both customer + admin)
|
||
|
||
resources/ts/stores/
|
||
└─ dedicatedConfigurator.ts (NEW Pinia store)
|
||
```
|
||
|
||
### Pinia state
|
||
|
||
```ts
|
||
state = {
|
||
chassis: Plan | null,
|
||
cpu: string,
|
||
ramGb: number, // 32, 64, 128, 256, 512, 1024, 1536
|
||
bays: Array<string|'empty'>,
|
||
os: string,
|
||
bandwidth: string,
|
||
privateNetworking: boolean,
|
||
ipv4: 1 | 5 | 13 | 29,
|
||
cycle: 'monthly' | 'quarterly' | 'semi_annual' | 'annual',
|
||
}
|
||
|
||
getters = {
|
||
baselinePrice, cpuUpgradeCost, ramUpgradeCost, driveCost,
|
||
bandwidthCost, ipv4Cost, cycleSubtotal, setupFee,
|
||
cycleTotal, shareUrl, checkoutUrl,
|
||
}
|
||
```
|
||
|
||
### URL state contract
|
||
|
||
`/dedicated-servers/{slug}?<params>` and `/checkout/{plan_id}?<params>` share:
|
||
|
||
| Param | Values | Default |
|
||
|---|---|---|
|
||
| `cpu` | option value slug | baseline |
|
||
| `ram` | `32` `64` `128` `256` `512` `1024` `1536` | `32` |
|
||
| `bays` | comma-list, length = bay count | chassis-specific lean preset |
|
||
| `os` | `none` `alma9` `ubuntu24` `debian12` `windows-byol` | `none` |
|
||
| `bw` | `1g-unmetered` `10g-1tb` `10g-10tb` `10g-100tb` `10g-unmetered` | `1g-unmetered` |
|
||
| `pn` | `0` `1` | `0` |
|
||
| `ipv4` | `1` `5` `13` `29` | `1` |
|
||
| `cycle` | `monthly` `quarterly` `semi_annual` `annual` | `monthly` |
|
||
|
||
### Setup fee flow
|
||
|
||
Setup fee is calculated at checkout based on `plan.setup_fee` and selected cycle:
|
||
|
||
```php
|
||
$includesSetupFee = in_array($cycle, ['monthly', 'quarterly']);
|
||
$setupFee = $includesSetupFee ? $plan->setup_fee : 0;
|
||
```
|
||
|
||
Surfaced as a separate line item on the checkout page with explanatory text.
|
||
|
||
### Build tracker stages
|
||
|
||
| # | Stage | Triggered by | Customer-visible message |
|
||
|---|---|---|---|
|
||
| 1 | `ordered` | Subscription `created` event | "Order received. We're placing the hardware order with our supplier." |
|
||
| 2 | `hardware_acquired` | Admin click | "Hardware acquired and en route to our datacenter (typically 2-3 days)." |
|
||
| 3 | `assembly` | Admin click | "Server received in Atlanta. Our DC tech is configuring iDRAC and assembly." |
|
||
| 4 | `racked` | Admin click | "Server racked, powered up, and online for final QA." |
|
||
| 5 | `deployed` | Admin click | "Server is live. Root credentials and iDRAC access have been emailed." |
|
||
|
||
The `deployed` milestone flips `subscription.status` from `pending` → `active` and triggers the deployment email.
|
||
|
||
## Edge cases
|
||
|
||
| Case | Behavior |
|
||
|---|---|
|
||
| URL has more bay entries than chassis has bays | Truncate; toast warning |
|
||
| URL has invalid CPU for the chassis | Fall back to baseline; snackbar |
|
||
| Subscription cancelled before `hardware_acquired` | Full refund (rental + setup fee) |
|
||
| Subscription cancelled after `hardware_acquired`, inside 14 days | Pro-rated rental refund; setup fee non-refundable; chassis added to "ready-to-deploy" inventory |
|
||
| Customer wants off-catalog chassis | "Need something custom?" CTA → `/contact` form |
|
||
| Mobile (< 768 px) | DriveBayPicker collapses to single-column list; sticky footer docks to viewport bottom |
|
||
| Browser back/forward | URL hydration restores everything |
|
||
|
||
## Testing
|
||
|
||
- **Pest** — `tests/Feature/Marketing/DedicatedServersTest.php` and `tests/Feature/Account/BuildTrackerTest.php`
|
||
- **Vitest** — *deferred (Vitest not currently installed in this project; covered by Playwright + Pest)*
|
||
- **Playwright** — `tests/e2e/dedicated-configurator.spec.ts` (one happy-path E2E)
|
||
|
||
## Acceptance criteria
|
||
|
||
- [ ] Migration adds `plans.setup_fee` and `service_build_milestones` table
|
||
- [ ] PlanSeeder seeds 8 new 14th-gen rows; un-hides existing 12th/13th-gen rows
|
||
- [ ] ConfigOptionSeeder adds 15 sub-groups under "Dedicated Gen14 Configurator" with chassis-specific attachments
|
||
- [ ] `/dedicated-servers` lists all dedicated plans (both lines), generation filter works
|
||
- [ ] `/dedicated-servers/{slug}` works for all 8 new chassis slugs
|
||
- [ ] Configurator computes correct totals across all option groups
|
||
- [ ] Setup fee shows on Monthly/Quarterly, hides on Semi-Annual/Annual
|
||
- [ ] Setup fee non-refundable note on checkout + FAQ
|
||
- [ ] `Order this configuration` deep-links with all params; checkout pre-selects them
|
||
- [ ] `Copy share link` works
|
||
- [ ] BuildStatusPanel renders during build; admin can mark milestones; customer sees timestamps
|
||
- [ ] `deployed` milestone flips subscription `pending` → `active`
|
||
- [ ] Pest tests pass
|
||
- [ ] `npm run build` and `php artisan test --compact` pass
|
||
- [ ] Mobile layout works without horizontal scroll
|
||
|
||
## Open implementation questions (deferred to phase work)
|
||
|
||
- Exact RAM upgrade pricing tiers — pull from `dedicated-server-pricing-2026q2.md` at seeder-write time
|
||
- Exact drive add-on prices per chassis — pull from catalog JSON `drives.drives` array per chassis
|
||
- Bandwidth tier pricing — pull from `dedicated-server-pricing-2026q2.md`
|
||
- Hetzner comparison row data — pending separate research
|