Dynamic product pages + Battlefield ACP marketing page

- Web Hosting & Game Servers now pull plans from database (same pattern as VPS/Dedicated)
- Add 6 game server plans to PlanSeeder (Minecraft, Rust, ARK, Valheim, CS2, Palworld)
- Create Battlefield ACP marketing page with ProCon replacement hero, 6 feature cards,
  ML analytics, ban appeals, PunkBuster screenshots, Discord integration sections
- Add Battlefield ACP to Products dropdown navigation
- Marketing pages use SectionHeader component, fade-in animations, Vuexy design patterns

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Claude Dev
2026-02-10 11:32:35 -05:00
parent 45d25d61ba
commit 169b06e349
20 changed files with 1498 additions and 481 deletions

View File

@@ -2,20 +2,22 @@
import { usePage } from '@inertiajs/vue3'
import { computed } from 'vue'
import MarketingLayout from '@/Layouts/MarketingLayout.vue'
import SectionHeader from '@/Components/Marketing/SectionHeader.vue'
defineOptions({ layout: MarketingLayout })
interface PageProps {
domains: { marketing: string; account: string; admin: string }
interface Plan {
id: number
name: string
slug: string
price: string
features: Record<string, string | number> | null
stock_quantity: number | null
}
interface VpsPlan {
name: string
cpu: string
ram: string
storage: string
bandwidth: string
price: string
interface PageProps {
plans: Plan[]
domains: { marketing: string; account: string; admin: string }
}
interface Feature {
@@ -27,6 +29,13 @@ interface Feature {
const page = usePage()
const props = computed(() => page.props as unknown as PageProps)
const accountUrl = computed<string>(() => `https://${props.value.domains?.account}`)
const plans = computed(() => props.value.plans || [])
const startingPrice = computed<string>(() => {
if (plans.value.length === 0) return '3.50'
const lowest = Math.min(...plans.value.map(p => parseFloat(p.price)))
return lowest % 1 === 0 ? lowest.toString() : lowest.toFixed(2)
})
const features: Feature[] = [
{ icon: 'tabler-database', title: 'RAID 10 SSD Storage', description: 'Redundant SSD arrays for fast read/write speeds and data protection.' },
@@ -37,43 +46,51 @@ const features: Feature[] = [
{ icon: 'tabler-server', title: 'VirtFusion Panel', description: 'Powerful control panel for managing your VPS with ease.' },
]
const plans: VpsPlan[] = [
{ name: 'Micro VPS', cpu: '1 vCPU', ram: '1 GB', storage: '25 GB SSD', bandwidth: '2 TB', price: '$4.20' },
{ name: 'Mini VPS', cpu: '1 vCPU', ram: '2 GB', storage: '50 GB SSD', bandwidth: '4 TB', price: '$6.00' },
{ name: 'Dev Starter', cpu: '2 vCPU', ram: '2 GB', storage: '60 GB SSD', bandwidth: '4 TB', price: '$8.00' },
{ name: 'Basic VPS', cpu: '2 vCPU', ram: '4 GB', storage: '80 GB SSD', bandwidth: '6 TB', price: '$12.00' },
{ name: 'Storage Box', cpu: '2 vCPU', ram: '2 GB', storage: '500 GB SSD', bandwidth: '8 TB', price: '$15.00' },
{ name: 'Standard VPS', cpu: '4 vCPU', ram: '8 GB', storage: '160 GB SSD', bandwidth: '8 TB', price: '$15.60' },
{ name: 'RAM Optimized', cpu: '4 vCPU', ram: '16 GB', storage: '240 GB SSD', bandwidth: '10 TB', price: '$19.00' },
{ name: 'Advanced VPS', cpu: '6 vCPU', ram: '16 GB', storage: '320 GB SSD', bandwidth: '10 TB', price: '$21.60' },
{ name: 'Pro VPS', cpu: '8 vCPU', ram: '32 GB', storage: '640 GB SSD', bandwidth: '16 TB', price: '$30.00' },
]
const includedFeatures: string[] = [
'1 IPv4 & 1 /64 IPv6',
'Near instant provisioning',
'VM backups',
'Windows (BYOL) & Linux support',
'Intel E5-2680 v4 processors',
'Full root access',
'VirtFusion control panel',
'RAID 10 backed storage',
'14-day money back guarantee',
]
// Keys from features JSON that should not be shown as table columns
const internalKeys = new Set([
'virtfusion_package_id',
'virtfusion_server_id',
'control_panel',
'os',
'ipv4',
'ipv6',
])
function getFeature(plan: Plan, key: string): string {
return String(plan.features?.[key] ?? '-')
}
function formatPrice(plan: Plan): string {
const price = parseFloat(plan.price) || 0
return price % 1 === 0 ? `$${price}` : `$${price.toFixed(2)}`
}
</script>
<template>
<div>
<!-- Hero -->
<div class="py-16" style="background: linear-gradient(135deg, rgb(var(--v-theme-primary), 0.1), rgb(var(--v-theme-surface)));">
<div class="hero-section" style="padding-block: 5.25rem; background: linear-gradient(135deg, rgb(var(--v-theme-primary), 0.1), rgb(var(--v-theme-surface)));">
<VContainer class="text-center">
<VChip color="primary" variant="tonal" class="mb-4">VPS Hosting</VChip>
<h1 class="text-h2 font-weight-bold mb-3">Virtual Private Servers</h1>
<h1 class="text-h2 font-weight-bold mb-3">
Virtual Private <span class="hero-gradient-text">Servers</span>
</h1>
<p class="text-h6 text-medium-emphasis font-weight-regular mb-4 mx-auto" style="max-width: 600px;">
High-performance VPS hosting with RAID 10 SSD storage, dedicated resources, and full root access from our Atlanta, GA datacenter.
</p>
<p class="text-body-1 text-medium-emphasis mb-8">
Starting at just <span class="text-primary font-weight-bold">$4.20/mo</span>
Starting at just <span class="text-primary font-weight-bold">${{ startingPrice }}/mo</span>
</p>
<a :href="accountUrl + '/register'" class="text-decoration-none">
<VBtn color="primary" size="x-large" rounded="lg">
@@ -85,13 +102,16 @@ const includedFeatures: string[] = [
</div>
<!-- Features -->
<VContainer class="py-16">
<div class="text-center mb-12">
<h2 class="text-h3 font-weight-bold mb-3">Why Choose EZSCALE VPS?</h2>
</div>
<VContainer class="marketing-section">
<SectionHeader
label="Features"
title="Why Choose EZSCALE VPS?"
highlight-word="EZSCALE"
subtitle="Enterprise-grade infrastructure with the simplicity you deserve."
/>
<VRow>
<VCol v-for="feature in features" :key="feature.title" cols="12" sm="6" md="4">
<div class="d-flex ga-3 mb-4">
<VCol v-for="(feature, index) in features" :key="feature.title" cols="12" sm="6" md="4">
<div :class="['d-flex ga-3 mb-4 feature-card-hover pa-3 rounded-lg', `fade-in-up stagger-delay-${index + 1}`]">
<VAvatar color="primary" variant="tonal" size="44">
<VIcon :icon="feature.icon" size="22" />
</VAvatar>
@@ -105,16 +125,16 @@ const includedFeatures: string[] = [
</VContainer>
<!-- Plans Table -->
<div class="bg-surface-variant py-16">
<div class="section-alt-bg marketing-section">
<VContainer>
<div class="text-center mb-8">
<h2 class="text-h3 font-weight-bold mb-3">VPS Plans</h2>
<p class="text-body-1 text-medium-emphasis">
All plans hosted in our Atlanta, GA datacenter. DDoS protection, full root access, and VirtFusion panel included.
</p>
</div>
<SectionHeader
label="Pricing"
title="VPS Plans"
highlight-word="Plans"
subtitle="All plans hosted in our Atlanta, GA datacenter. DDoS protection, full root access, and VirtFusion panel included."
/>
<VCard>
<VCard class="feature-card-hover">
<VTable>
<thead>
<tr>
@@ -128,13 +148,13 @@ const includedFeatures: string[] = [
</tr>
</thead>
<tbody>
<tr v-for="plan in plans" :key="plan.name">
<tr v-for="plan in plans" :key="plan.id">
<td class="font-weight-bold">{{ plan.name }}</td>
<td>{{ plan.cpu }}</td>
<td>{{ plan.ram }}</td>
<td>{{ plan.storage }}</td>
<td>{{ plan.bandwidth }}</td>
<td class="text-primary font-weight-bold">{{ plan.price }}/mo</td>
<td>{{ getFeature(plan, 'cpu') }}</td>
<td>{{ getFeature(plan, 'ram') }}</td>
<td>{{ getFeature(plan, 'storage') }}</td>
<td>{{ getFeature(plan, 'bandwidth') }}</td>
<td class="text-primary font-weight-bold">{{ formatPrice(plan) }}/mo</td>
<td>
<a :href="accountUrl + '/register'" class="text-decoration-none">
<VBtn color="primary" size="small" variant="tonal">Order Now</VBtn>
@@ -146,7 +166,7 @@ const includedFeatures: string[] = [
</VCard>
<!-- Included with all plans -->
<VCard class="mt-8 pa-6">
<VCard class="mt-8 pa-6 feature-card-hover">
<h3 class="text-h5 font-weight-bold mb-4 text-center">Included With All Plans</h3>
<VRow>
<VCol
@@ -165,5 +185,28 @@ const includedFeatures: string[] = [
</VCard>
</VContainer>
</div>
<!-- CTA -->
<div class="marketing-section" style="background: linear-gradient(135deg, rgb(var(--v-theme-primary), 0.08), rgb(var(--v-theme-surface)));">
<VContainer class="text-center">
<h2 class="text-h4 font-weight-bold mb-3">Ready to Get Started?</h2>
<p class="text-body-1 text-medium-emphasis mb-6">
Deploy your VPS in seconds with full root access, DDoS protection, and 24/7 support.
</p>
<div class="d-flex ga-3 justify-center flex-wrap">
<a :href="accountUrl + '/register'" class="text-decoration-none">
<VBtn color="primary" size="large" rounded="lg">
Get Started
<VIcon icon="tabler-arrow-right" end />
</VBtn>
</a>
<a href="/contact" class="text-decoration-none">
<VBtn color="primary" variant="outlined" size="large" rounded="lg">
Contact Sales
</VBtn>
</a>
</div>
</VContainer>
</div>
</div>
</template>