feat(hytale): implement Hytale plugin development page and related components

- Added a new `/hytale` page with sections for hero, services, and pricing.
- Updated existing components to support Hytale-specific content and i18n.
- Modified site configuration and state to reflect the new focus on Hytale plugin development.
- Enhanced testimonials section to feature relevant client feedback.
- Adjusted navigation to include a link to the new Hytale page.
This commit is contained in:
2026-04-11 04:19:27 +02:00
parent 215fba6342
commit 39f2a81e8f
17 changed files with 1080 additions and 201 deletions
+1
View File
@@ -7,6 +7,7 @@ const mobileOpen = ref(false)
const navLinks = computed(() => [
{ key: 'home', path: '/' },
{ key: 'hytale', path: '/hytale' },
{ key: 'projects', path: '/projects' },
{ key: 'about', path: '/about' },
{ key: 'contact', path: '/contact' },
+23 -25
View File
@@ -1,6 +1,10 @@
<script setup lang="ts">
import { siteConfig } from '~/data/site'
const { t } = useI18n()
const localePath = useLocalePath()
const discordUrl = siteConfig.social.find(s => s.name === 'Discord')?.url ?? '#'
</script>
<template>
@@ -27,16 +31,14 @@ const localePath = useLocalePath()
<span class="animate-ping absolute inline-flex h-full w-full rounded-full bg-brand-400 opacity-75" />
<span class="relative inline-flex rounded-full h-2 w-2 bg-brand-500" />
</span>
<span class="text-sm font-medium text-brand-700 dark:text-brand-400">Available for projects</span>
<span class="text-sm font-medium text-brand-700 dark:text-brand-400">{{ t('home.badge.available') }}</span>
</div>
<div class="space-y-4">
<h1 class="text-4xl sm:text-5xl md:text-6xl lg:text-7xl font-extrabold tracking-tight leading-[1.1]">
<span class="text-gray-900 dark:text-white">{{ t('home.title').split(' ').slice(0, -2).join(' ') }}
</span>
<span
class="bg-gradient-to-r from-brand-500 via-brand-400 to-emerald-400 bg-clip-text text-transparent">{{
t('home.title').split(' ').slice(-2).join(' ') }}</span>
t('home.title') }}</span>
</h1>
<p class="text-lg sm:text-xl text-gray-600 dark:text-gray-400 max-w-xl leading-relaxed">
{{ t('home.subtitle') }}
@@ -45,27 +47,23 @@ const localePath = useLocalePath()
<!-- CTA Buttons -->
<div class="flex flex-col sm:flex-row gap-3">
<NuxtLink
:to="localePath('/projects')"
class="inline-flex items-center justify-center gap-2 px-6 py-3 rounded-xl bg-brand-500 hover:bg-brand-600 text-white font-semibold text-sm transition-all duration-200 shadow-lg shadow-brand-500/25 hover:shadow-brand-500/40"
<UButton
:to="discordUrl"
target="_blank"
rel="noopener"
color="primary"
icon="i-simple-icons-discord"
size="lg"
>
{{ t('home.cta.viewProjects') }}
<UIcon name="i-lucide-arrow-right" class="w-4 h-4" />
</NuxtLink>
<NuxtLink
:to="localePath('/fiverr')"
class="inline-flex items-center justify-center gap-2 px-6 py-3 rounded-xl border border-gray-300 dark:border-gray-700 text-gray-700 dark:text-gray-300 hover:border-brand-500/50 hover:text-brand-500 font-semibold text-sm transition-all duration-200"
>
{{ t('nav.fiverr') }}
<UIcon name="i-lucide-external-link" class="w-4 h-4" />
</NuxtLink>
<NuxtLink
{{ t('home.cta.discord') }}
</UButton>
<UButton
:to="localePath('/contact')"
class="inline-flex items-center justify-center gap-2 px-6 py-3 rounded-xl text-gray-600 dark:text-gray-400 hover:text-brand-500 font-semibold text-sm transition-all duration-200"
variant="outline"
size="lg"
>
{{ t('home.cta.contactMe') }}
<UIcon name="i-lucide-message-circle" class="w-4 h-4" />
</NuxtLink>
{{ t('home.cta.contact') }}
</UButton>
</div>
</div>
@@ -101,7 +99,7 @@ const localePath = useLocalePath()
</div>
<div class="pl-6">
<span class="text-purple-500 dark:text-purple-400">role</span><span class="text-gray-500">: </span>
<span class="text-amber-600 dark:text-amber-400">'Full Stack Dev'</span><span
<span class="text-amber-600 dark:text-amber-400">'{{ t('home.terminal.role') }}'</span><span
class="text-gray-500">,</span>
</div>
<div class="pl-6">
@@ -144,12 +142,12 @@ const localePath = useLocalePath()
<div
class="absolute -top-4 -right-4 px-3 py-2 rounded-lg bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 shadow-lg text-xs font-medium flex items-center gap-2">
<span class="w-2 h-2 rounded-full bg-brand-500" />
<span class="text-gray-700 dark:text-gray-300">50+ projects</span>
<span class="text-gray-700 dark:text-gray-300">{{ t('home.stats.projects') }}</span>
</div>
<div
class="absolute -bottom-3 -left-3 px-3 py-2 rounded-lg bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 shadow-lg text-xs font-medium flex items-center gap-2">
<UIcon name="i-lucide-star" class="text-yellow-400 w-3.5 h-3.5" />
<span class="text-gray-700 dark:text-gray-300">5.0 rating</span>
<span class="text-gray-700 dark:text-gray-300">{{ t('home.stats.rating') }}</span>
</div>
</div>
</div>
@@ -1,7 +1,13 @@
<script setup lang="ts">
import { testimonials, testimonialsStats } from '~/data/testimonials'
const props = defineProps<{
featured?: boolean
}>()
const { t } = useI18n()
const displayed = computed(() => props.featured ? testimonials.filter(t => t.featured) : testimonials)
</script>
<template>
@@ -34,7 +40,7 @@ const { t } = useI18n()
<!-- Horizontal scrolling testimonials -->
<div class="flex gap-5 overflow-x-auto overflow-y-visible pb-8 -mx-4 px-4 pt-2 snap-x snap-mandatory scrollbar-hide">
<div
v-for="(testimonial, index) in testimonials"
v-for="(testimonial, index) in displayed"
:key="index"
class="flex-none w-[340px] sm:w-[400px] snap-start"
>
@@ -0,0 +1,17 @@
<script setup lang="ts">
const { t } = useI18n()
</script>
<template>
<section class="py-16 md:py-24 px-4 sm:px-6 lg:px-8">
<div class="max-w-4xl mx-auto text-center">
<span class="font-mono text-sm text-brand-500 dark:text-brand-400 tracking-wider">{{ t('hytale.hero.label') }}</span>
<h1 class="text-4xl sm:text-5xl md:text-6xl font-extrabold tracking-tight leading-[1.1] mt-4">
<span class="bg-gradient-to-r from-brand-500 via-brand-400 to-emerald-400 bg-clip-text text-transparent">{{ t('hytale.hero.title') }}</span>
</h1>
<p class="text-lg sm:text-xl text-gray-500 dark:text-gray-400 mt-6 max-w-2xl mx-auto leading-relaxed">
{{ t('hytale.hero.subtitle') }}
</p>
</div>
</section>
</template>
@@ -0,0 +1,63 @@
<script setup lang="ts">
import { hytalePricing } from '~/data/pricing'
const { t } = useI18n()
const localePath = useLocalePath()
</script>
<template>
<section class="py-16 md:py-24 px-4 sm:px-6 lg:px-8">
<div class="max-w-7xl mx-auto">
<div class="text-center mb-16">
<span class="font-mono text-sm text-brand-500 dark:text-brand-400 tracking-wider">{{ t('hytale.pricing.label') }}</span>
<h2 class="text-3xl sm:text-4xl lg:text-5xl font-bold mt-3 bg-gradient-to-r from-gray-900 via-gray-800 to-gray-600 dark:from-white dark:via-gray-200 dark:to-gray-500 bg-clip-text text-transparent">{{ t('hytale.pricing.title') }}</h2>
<p class="text-lg text-gray-500 dark:text-gray-400 mt-4 max-w-2xl mx-auto leading-relaxed">{{ t('hytale.pricing.subtitle') }}</p>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<UCard
v-for="tier in hytalePricing"
:key="tier.id"
class="hover:border-brand-500/40 hover:shadow-xl hover:shadow-brand-500/10 hover:-translate-y-1 transition-all duration-200"
:class="{ 'ring-2 ring-brand-500': tier.featured }"
>
<div class="flex flex-col gap-4 p-2">
<div class="flex items-center justify-between">
<h3 class="text-xl font-bold text-gray-900 dark:text-white">{{ t(`hytale.pricing.${tier.id}.name`) }}</h3>
<UBadge v-if="tier.featured" color="primary" variant="subtle">{{ t('hytale.pricing.popular') }}</UBadge>
</div>
<div class="text-3xl font-extrabold text-gray-900 dark:text-white">
<template v-if="tier.priceFixed">
<span class="text-sm font-normal text-gray-500 dark:text-gray-400">{{ t('hytale.pricing.from') }} </span>
{{ tier.priceFixed }}
</template>
<template v-else>
<span class="text-lg">{{ t('hytale.pricing.onQuote') }}</span>
</template>
</div>
<p class="text-sm text-gray-500 dark:text-gray-400">{{ t(`hytale.pricing.${tier.id}.description`) }}</p>
<ul class="space-y-2 flex-1">
<li v-for="i in 4" :key="i" class="flex items-center gap-2 text-sm text-gray-600 dark:text-gray-300">
<UIcon name="i-lucide-check" class="w-4 h-4 text-brand-500 shrink-0" />
{{ t(`hytale.pricing.${tier.id}.features.${i - 1}`) }}
</li>
</ul>
<UButton
:to="localePath('/contact')"
:color="tier.featured ? 'primary' : 'neutral'"
:variant="tier.featured ? 'solid' : 'outline'"
block
class="mt-4"
>
{{ t('hytale.pricing.cta') }}
</UButton>
</div>
</UCard>
</div>
</div>
</section>
</template>
@@ -0,0 +1,33 @@
<script setup lang="ts">
const { t } = useI18n()
const services = [
{ id: 'plugin', icon: 'i-lucide-puzzle' },
{ id: 'config', icon: 'i-lucide-settings' },
{ id: 'support', icon: 'i-lucide-shield-check' },
]
</script>
<template>
<section class="py-16 md:py-24 px-4 sm:px-6 lg:px-8 bg-gray-50/50 dark:bg-gray-900/20">
<div class="max-w-7xl mx-auto">
<div class="text-center mb-16">
<span class="font-mono text-sm text-brand-500 dark:text-brand-400 tracking-wider">{{ t('hytale.services.label') }}</span>
<h2 class="text-3xl sm:text-4xl lg:text-5xl font-bold mt-3 bg-gradient-to-r from-gray-900 via-gray-800 to-gray-600 dark:from-white dark:via-gray-200 dark:to-gray-500 bg-clip-text text-transparent">{{ t('hytale.services.title') }}</h2>
<p class="text-lg text-gray-500 dark:text-gray-400 mt-4 max-w-2xl mx-auto leading-relaxed">{{ t('hytale.services.subtitle') }}</p>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<UCard v-for="service in services" :key="service.id" class="hover:border-brand-500/40 hover:shadow-xl hover:shadow-brand-500/10 hover:-translate-y-1 transition-all duration-200">
<div class="flex flex-col items-center text-center gap-4 p-2">
<div class="w-12 h-12 rounded-xl bg-brand-500/10 dark:bg-brand-500/15 flex items-center justify-center">
<UIcon :name="service.icon" class="w-6 h-6 text-brand-500" />
</div>
<h3 class="text-lg font-semibold text-gray-900 dark:text-white">{{ t(`hytale.services.items.${service.id}.title`) }}</h3>
<p class="text-sm text-gray-500 dark:text-gray-400 leading-relaxed">{{ t(`hytale.services.items.${service.id}.description`) }}</p>
</div>
</UCard>
</div>
</div>
</section>
</template>
+9
View File
@@ -0,0 +1,9 @@
import type { PricingTier } from '~~/shared/types'
export const hytalePricing: PricingTier[] = [
{ id: 'simple', priceFixed: '50€', featured: false },
{ id: 'complex', priceFixed: null, priceLabel: 'Sur devis', featured: true },
{ id: 'custom', priceFixed: null, priceLabel: 'Sur devis', featured: false },
{ id: 'maintenance', priceFixed: '30€/mois', featured: false },
{ id: 'web', priceFixed: null, priceLabel: 'Sur devis', featured: false },
]
+4 -3
View File
@@ -4,9 +4,10 @@ export type { SiteConfig, ContactInfo, SocialLink, FiverrService, FiverrConfig }
export const siteConfig: SiteConfig = {
name: 'Killian',
title: "Killian' DAL-CIN - Full Stack Developer | Vue.js, React, Node.js Expert",
title: "Killian' DAL-CIN - Hytale Plugin Developer | Freelance",
description:
'Professional Full Stack Developer specializing in modern web development with Vue.js, React, Node.js. Expert in Discord bots, web applications, and custom software solutions.',
jobTitle: 'Hytale Plugin Developer',
author: 'Killian',
url: 'https://killiandalcin.fr',
@@ -91,12 +92,12 @@ export const siteConfig: SiteConfig = {
},
organization: {
'@type': 'ProfessionalService',
name: "Killian' DAL-CIN - Developpeur Full Stack",
name: "Killian' DAL-CIN - Hytale Plugin Developer",
logo: 'https://killiandalcin.fr/logo.webp',
priceRange: '$$$',
aggregateRating: {
ratingValue: '5',
reviewCount: '10',
reviewCount: '5',
},
},
},
+3 -1
View File
@@ -25,6 +25,7 @@ export const testimonials: Testimonial[] = [
"Travail excellent, Communication au top, Disponible en tout temps, réactif et à l'écoute je le recommande vivement et reviendrai vers lui si je dois refaire un projet similaire !",
date: '22/04/2023',
platform: 'Fiverr',
featured: true,
project_type: 'Bot Discord',
results: ["Prix: Jusqu'à 50€", 'Durée: 4 jours', 'Communication parfaite'],
},
@@ -51,6 +52,7 @@ export const testimonials: Testimonial[] = [
'Excellent développeur, la commande fut plus rapide que prévu la communication est instantané et le résultat est parfait. Je recommande fortement et reviendrai sûrement pour des mise à jour !',
date: '12/11/2022',
platform: 'Fiverr',
featured: true,
project_type: 'Bot Discord',
results: [
'Livraison plus rapide que prévu',
@@ -73,7 +75,7 @@ export const testimonials: Testimonial[] = [
]
export const testimonialsStats: TestimonialsStats = {
totalReviews: 10,
totalReviews: 5,
averageRating: 5.0,
projectsCompleted: 25,
}
+39
View File
@@ -0,0 +1,39 @@
<script setup lang="ts">
const { t } = useI18n()
useSeoMeta({
title: () => t('seo.hytale.title'),
description: () => t('seo.hytale.description'),
ogTitle: () => t('seo.hytale.title'),
ogDescription: () => t('seo.hytale.description'),
ogImage: 'https://killiandalcin.fr/og-image.png',
ogType: 'website',
})
useHead({
script: [{
type: 'application/ld+json',
innerHTML: JSON.stringify({
'@context': 'https://schema.org',
'@type': 'Service',
name: 'Hytale Plugin Development',
provider: {
'@type': 'Person',
name: "Killian' DAL-CIN",
jobTitle: 'Hytale Plugin Developer',
},
}),
}],
})
</script>
<template>
<div>
<HytaleHeroSection />
<HytaleServicesSection />
<HytalePricingSection />
<div class="relative bg-gray-50/50 dark:bg-gray-900/20">
<TestimonialsSection />
</div>
</div>
</template>
+1 -1
View File
@@ -63,7 +63,7 @@ useHead({
<!-- Testimonials Section -->
<div class="relative bg-gray-50/50 dark:bg-gray-900/20">
<TestimonialsSection />
<TestimonialsSection featured />
</div>
<!-- FAQ Section -->