# 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
