- 6 notification classes: PaymentSucceeded, PaymentFailed, SubscriptionCreated, SubscriptionCancelled, ServiceProvisioned, InvoiceGenerated (mail + database) - Wire notifications to existing event listeners + new subscription listeners - NotificationBell component in Account and Admin layouts - NotificationController with index, markAsRead, markAllAsRead endpoints - 62 new Pest tests: AdminPanelTest (admin CRUD) + CustomerAccountTest (account features) - Add Legal links column to marketing footer - 114 tests passing (623 assertions) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
354 lines
10 KiB
Vue
354 lines
10 KiB
Vue
<script lang="ts" setup>
|
|
import { usePage } from '@inertiajs/vue3'
|
|
import { computed } from 'vue'
|
|
import { useTheme } from 'vuetify'
|
|
import { marketingNavItems } from '@/navigation/marketing'
|
|
import ThemeSwitcher from '@/Components/ThemeSwitcher.vue'
|
|
import logoWhite from '@images/ezscale_logo_white.png'
|
|
|
|
const theme = useTheme()
|
|
const isDark = computed(() => theme.global.current.value.dark)
|
|
|
|
interface PageProps {
|
|
domains: { marketing: string; account: string; admin: string }
|
|
}
|
|
|
|
const page = usePage()
|
|
const props = computed(() => page.props as unknown as PageProps)
|
|
const accountUrl = computed(() => `https://${props.value.domains?.account}`)
|
|
|
|
const footerLinks = {
|
|
products: [
|
|
{ title: 'VPS Hosting', href: '/vps-hosting' },
|
|
{ title: 'Dedicated Servers', href: '/dedicated-servers' },
|
|
{ title: 'Web Hosting', href: '/web-hosting' },
|
|
{ title: 'Game Servers', href: '/game-servers' },
|
|
],
|
|
company: [
|
|
{ title: 'About', href: '/about' },
|
|
{ title: 'Pricing', href: '/pricing' },
|
|
{ title: 'Contact', href: '/contact' },
|
|
{ title: 'Blog', href: '/blog' },
|
|
],
|
|
support: [
|
|
{ title: 'Help Center', href: 'https://ezscale.support' },
|
|
{ title: 'Knowledge Base', href: 'https://ezscale.support/en/knowledgebase' },
|
|
{ title: 'Status Page', href: 'https://status.ezscale.cloud' },
|
|
],
|
|
legal: [
|
|
{ title: 'Terms of Service', href: '/terms-of-service' },
|
|
{ title: 'Privacy Policy', href: '/privacy-policy' },
|
|
{ title: 'Acceptable Use', href: '/acceptable-use' },
|
|
{ title: 'SLA', href: '/sla' },
|
|
],
|
|
}
|
|
|
|
const socialLinks = [
|
|
{ title: 'twitter', icon: 'tabler-brand-twitter-filled', href: '#' },
|
|
{ title: 'facebook', icon: 'tabler-brand-facebook-filled', href: '#' },
|
|
{ title: 'github', icon: 'tabler-brand-github-filled', href: '#' },
|
|
{ title: 'discord', icon: 'tabler-brand-discord-filled', href: '#' },
|
|
]
|
|
</script>
|
|
|
|
<template>
|
|
<VApp>
|
|
<VAppBar flat>
|
|
<VContainer class="d-flex align-center">
|
|
<a href="/" class="d-inline-flex align-center">
|
|
<img
|
|
:src="logoWhite"
|
|
alt="EZSCALE"
|
|
:class="{ 'logo-light': !isDark }"
|
|
style="height: 32px; width: auto;"
|
|
>
|
|
</a>
|
|
|
|
<VSpacer />
|
|
|
|
<div class="d-flex align-center ga-1">
|
|
<template v-for="item in marketingNavItems" :key="item.title">
|
|
<VMenu v-if="item.children" open-on-hover>
|
|
<template #activator="{ props: menuProps }">
|
|
<VBtn variant="text" size="small" v-bind="menuProps">
|
|
{{ item.title }}
|
|
<VIcon icon="tabler-chevron-down" end size="small" />
|
|
</VBtn>
|
|
</template>
|
|
|
|
<VList>
|
|
<VListItem
|
|
v-for="child in item.children"
|
|
:key="child.href"
|
|
:href="child.href"
|
|
>
|
|
<template #prepend>
|
|
<VIcon v-if="child.icon" :icon="child.icon" />
|
|
</template>
|
|
<VListItemTitle>{{ child.title }}</VListItemTitle>
|
|
<VListItemSubtitle v-if="child.description">
|
|
{{ child.description }}
|
|
</VListItemSubtitle>
|
|
</VListItem>
|
|
</VList>
|
|
</VMenu>
|
|
|
|
<a v-else :href="item.href" class="text-decoration-none">
|
|
<VBtn variant="text" size="small">
|
|
{{ item.title }}
|
|
</VBtn>
|
|
</a>
|
|
</template>
|
|
</div>
|
|
|
|
<VSpacer />
|
|
|
|
<div class="d-flex align-center ga-2">
|
|
<ThemeSwitcher />
|
|
|
|
<a :href="accountUrl + '/login'" class="text-decoration-none">
|
|
<VBtn variant="text" size="small">
|
|
Login
|
|
</VBtn>
|
|
</a>
|
|
|
|
<a :href="accountUrl + '/register'" class="text-decoration-none">
|
|
<VBtn variant="flat" size="small" color="primary">
|
|
Sign Up
|
|
</VBtn>
|
|
</a>
|
|
</div>
|
|
</VContainer>
|
|
</VAppBar>
|
|
|
|
<VMain>
|
|
<slot />
|
|
</VMain>
|
|
|
|
<!-- Footer -->
|
|
<div class="footer">
|
|
<div class="footer-top pt-11">
|
|
<VContainer>
|
|
<VRow>
|
|
<!-- Logo + Description + Newsletter -->
|
|
<VCol cols="12" md="4">
|
|
<div
|
|
class="mb-4"
|
|
:class="$vuetify.display.smAndDown ? 'w-100' : 'w-75'"
|
|
>
|
|
<div class="d-flex align-center mb-6">
|
|
<img
|
|
:src="logoWhite"
|
|
alt="EZSCALE"
|
|
style="height: 32px; width: auto;"
|
|
>
|
|
</div>
|
|
|
|
<div class="text-white-variant mb-6">
|
|
High-performance VPS, dedicated servers, and hosting solutions with 24/7 support and enterprise-grade infrastructure.
|
|
</div>
|
|
|
|
<VForm class="subscribe-form d-flex align-center">
|
|
<VTextField
|
|
label="Subscribe to newsletter"
|
|
placeholder="john@email.com"
|
|
variant="outlined"
|
|
density="comfortable"
|
|
hide-details
|
|
/>
|
|
<VBtn class="align-self-end rounded-s-0">
|
|
Subscribe
|
|
</VBtn>
|
|
</VForm>
|
|
</div>
|
|
</VCol>
|
|
|
|
<!-- Products -->
|
|
<VCol md="2" sm="4" xs="6">
|
|
<div class="footer-links">
|
|
<h6 class="footer-title text-h6 mb-6">
|
|
Products
|
|
</h6>
|
|
<ul style="list-style: none; padding: 0;">
|
|
<li
|
|
v-for="link in footerLinks.products"
|
|
:key="link.href"
|
|
class="mb-4"
|
|
>
|
|
<a
|
|
:href="link.href"
|
|
class="text-white-variant"
|
|
>
|
|
{{ link.title }}
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</VCol>
|
|
|
|
<!-- Company -->
|
|
<VCol md="2" sm="4" xs="6">
|
|
<div class="footer-links">
|
|
<h6 class="footer-title text-h6 mb-6">
|
|
Company
|
|
</h6>
|
|
<ul style="list-style: none; padding: 0;">
|
|
<li
|
|
v-for="link in footerLinks.company"
|
|
:key="link.href"
|
|
class="mb-4"
|
|
>
|
|
<a
|
|
:href="link.href"
|
|
class="text-white-variant"
|
|
>
|
|
{{ link.title }}
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</VCol>
|
|
|
|
<!-- Support -->
|
|
<VCol md="2" sm="4" xs="6">
|
|
<div class="footer-links">
|
|
<h6 class="footer-title text-h6 mb-6">
|
|
Support
|
|
</h6>
|
|
<ul style="list-style: none; padding: 0;">
|
|
<li
|
|
v-for="link in footerLinks.support"
|
|
:key="link.href"
|
|
class="mb-4"
|
|
>
|
|
<a
|
|
:href="link.href"
|
|
class="text-white-variant"
|
|
>
|
|
{{ link.title }}
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</VCol>
|
|
|
|
<!-- Legal -->
|
|
<VCol md="2" sm="4" xs="6">
|
|
<div class="footer-links">
|
|
<h6 class="footer-title text-h6 mb-6">
|
|
Legal
|
|
</h6>
|
|
<ul style="list-style: none; padding: 0;">
|
|
<li
|
|
v-for="link in footerLinks.legal"
|
|
:key="link.href"
|
|
class="mb-4"
|
|
>
|
|
<a
|
|
:href="link.href"
|
|
class="text-white-variant"
|
|
>
|
|
{{ link.title }}
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</VCol>
|
|
</VRow>
|
|
</VContainer>
|
|
</div>
|
|
|
|
<!-- Footer Line -->
|
|
<div class="footer-line w-100">
|
|
<VContainer>
|
|
<div class="d-flex justify-space-between flex-wrap gap-y-5 align-center">
|
|
<div class="text-body-1 text-white-variant text-wrap me-4">
|
|
© {{ new Date().getFullYear() }}
|
|
<span class="font-weight-bold ms-1 text-white">EZSCALE</span>,
|
|
All rights reserved.
|
|
</div>
|
|
|
|
<div class="d-flex gap-x-6">
|
|
<a
|
|
v-for="item in socialLinks"
|
|
:key="item.title"
|
|
:href="item.href"
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
>
|
|
<VIcon
|
|
:icon="item.icon"
|
|
size="16"
|
|
color="white"
|
|
/>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</VContainer>
|
|
</div>
|
|
</div>
|
|
</VApp>
|
|
</template>
|
|
|
|
<style lang="scss" scoped>
|
|
.footer-title {
|
|
color: rgba(255, 255, 255, 92%);
|
|
}
|
|
|
|
.footer-top {
|
|
border-radius: 60px 60px 0 0;
|
|
background-color: #2f3349;
|
|
background-size: cover;
|
|
color: #fff;
|
|
}
|
|
|
|
.footer-links {
|
|
a {
|
|
text-decoration: none;
|
|
|
|
&:hover {
|
|
color: #fff !important;
|
|
}
|
|
}
|
|
}
|
|
|
|
.footer-line {
|
|
background: #282c3e;
|
|
}
|
|
|
|
.text-white-variant {
|
|
color: rgba(255, 255, 255, 70%);
|
|
}
|
|
</style>
|
|
|
|
<style lang="scss">
|
|
.subscribe-form {
|
|
.v-label {
|
|
color: rgba(225, 222, 245, 90%) !important;
|
|
}
|
|
|
|
.v-field {
|
|
border-end-end-radius: 0;
|
|
border-end-start-radius: 10px;
|
|
border-start-end-radius: 0;
|
|
border-start-start-radius: 10px;
|
|
|
|
input.v-field__input::placeholder {
|
|
color: rgba(225, 222, 245, 40%) !important;
|
|
}
|
|
|
|
input.v-field__input {
|
|
color: rgba(255, 255, 255, 78%);
|
|
}
|
|
}
|
|
}
|
|
|
|
.footer {
|
|
@media (min-width: 600px) and (max-width: 960px) {
|
|
.v-container {
|
|
padding-inline: 2rem !important;
|
|
}
|
|
}
|
|
}
|
|
</style>
|