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:
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user