Improve variant capabilites
All checks were successful
Build and deploy updated apps / Build & deploy (push) Successful in 2m11s

This commit is contained in:
Dominik Milacher 2025-06-15 01:20:03 +02:00
parent cf76334c38
commit 2b8a67af9b
17 changed files with 122 additions and 24 deletions

View File

@ -54,7 +54,7 @@
<script setup lang="ts">
import type {Apartment} from '@/composables/useApartments'
const {t, tm, rt} = useI18n()
const {t, tm, rt} = useVariantData()
defineProps<{ apartment: Apartment }>()
const { variantPath } = useVariantPath()

View File

@ -38,5 +38,5 @@ const props = defineProps<{
features: Feature[]
}>()
const {t, tm, rt} = useI18n()
const {t, tm, rt} = useVariantData()
</script>

View File

@ -73,7 +73,7 @@
</template>
<script setup lang="ts">
const {t, tm, rt} = useI18n()
const {t, tm, rt} = useVariantData()
const year = new Date().getFullYear()
const { variantPath } = useVariantPath()
</script>

View File

@ -10,15 +10,16 @@
<!-- nav links -->
<ul class="flex space-x-6">
<li>
<NuxtLink :to="variantPath('apartments')">{{ t('header.apartments') }}</NuxtLink>
<NuxtLink :to="variantPath('apartments')">{{ rt(tm('header').apartments) }}</NuxtLink>
</li>
<li>
<NuxtLink :to="variantPath('book')">{{ t('header.book') }}</NuxtLink>
</li>
<li>
<NuxtLink :to="variantPath('contact')">{{ t('header.contact') }}</NuxtLink>
<NuxtLink :to="variantPath('contact')">{{ rt(tm('header').contact) }}</NuxtLink>
</li>
<AppLocaleSwitcher/>
<WVariantSwitcher/>
</ul>
</nav>
</AppStripe>
@ -26,6 +27,6 @@
</template>
<script setup lang="ts">
const {t, tm, rt} = useI18n()
const {t, tm, rt} = useVariantData()
const { variantPath } = useVariantPath()
</script>

View File

@ -23,5 +23,5 @@ const props = defineProps<{
i18nKey: string
}>()
const {t, tm, rt} = useI18n()
const {t, tm, rt} = useVariantData()
</script>

View File

@ -3,7 +3,10 @@
"home": "Landhaus Panoramablick",
"apartments": "Apartments & Preise",
"book": "Buchen",
"contact": "Kontakt"
"contact": {
"#su": "KontaktS",
"#wi": "KontaktW"
}
},
"footer": {
"questions": "Fragen oder Wünsche?",
@ -83,7 +86,10 @@
"list": [
{
"id": "apartment-1",
"title": "Apartment 1",
"title": {
"#su": "Appartment 1 (Sommer)",
"#wi": "Appartment 1 (Winter)"
},
"subtitle": "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.",
"thumbnail": "/ap-1/1.webp",
"highlights": [

View File

@ -3,7 +3,10 @@
"home": "Landhaus Panoramablick",
"apartments": "Apartments & Rates",
"book": "Book",
"contact": "Contact"
"contact": {
"#su": "ContactS",
"#wi": "ContactW"
}
},
"footer": {
"questions": "Fragen oder Wünsche?",
@ -83,7 +86,10 @@
"list": [
{
"id": "apartment-1",
"title": "Apartment 1",
"title": {
"#su": "Appartment 1 (Summer)",
"#wi": "Appartment 1 (Winter)"
},
"subtitle": "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.",
"thumbnail": "/ap-1/1.webp",
"highlights": [

View File

@ -27,7 +27,7 @@ export default defineNuxtConfig({
}
},
websites: {
defaultVariant: 'sumu',
defaultVariant: 'su',
variants: [
{ code: 'su', name: 'Summer' },
{ code: 'wi', name: 'Winter' }

View File

@ -21,5 +21,5 @@
</template>
<script setup lang="ts">
const {t, tm, rt} = useI18n()
const {t, tm, rt} = useVariantData()
</script>

View File

@ -96,7 +96,7 @@
<script setup lang="ts">
/*TODO form should contain link to privacy statement?*/
const {t, tm, rt} = useI18n()
const {t, tm, rt} = useVariantData()
const { variantPath } = useVariantPath()
import * as v from 'valibot'
import type {FormSubmitEvent} from '@nuxt/ui'

View File

@ -130,6 +130,6 @@ const images = [
'/panorama.webp'
]
const {t, tm, rt} = useI18n()
const {t, tm, rt} = useVariantData()
const { variantPath } = useVariantPath()
</script>

View File

@ -26,5 +26,5 @@
</template>
<script setup lang="ts">
const {t, tm, rt} = useI18n()
const {t, tm, rt} = useVariantData()
</script>

View File

@ -8,14 +8,7 @@ export default defineNuxtModule<ModuleOptions>({
name: 'websites',
configKey: 'websites',
},
defaults: {
defaultVariant: "su",
variants: [
{ code: 'su', name: 'Summer' },
{ code: 'wi', name: 'Winter' }
]
},
setup(_options, _nuxt) {
setup(options, nuxt) {
const resolver = createResolver(import.meta.url)
addComponentsDir({
path: resolver.resolve('./runtime/components'),
@ -24,5 +17,6 @@ export default defineNuxtModule<ModuleOptions>({
global: true,
})
addImportsDir(resolver.resolve('./runtime/composables'))
nuxt.options.runtimeConfig.public.websites = options
}
})

View File

@ -0,0 +1,18 @@
<script setup>
const { variants, current } = useVariants()
const switchVariantPath = useSwitchVariantPath()
const availableVariants = computed(() =>
variants.filter(v => String(v.code) !== String(current.value))
)
</script>
<template>
<NuxtLink
v-for="variant in availableVariants"
:key="variant.code"
:to="switchVariantPath(variant.code)"
>
{{ variant.code.toUpperCase() }}
</NuxtLink>
</template>

View File

@ -0,0 +1,18 @@
export function useSwitchVariantPath() {
const route = useRoute()
return (variant) => {
// Clone current params and replace variant
const params = { ...route.params, variant }
// Build a new path with the new params
// This uses named routes (recommended). If not, fallback to direct path replace:
if (route.name) {
return { name: route.name, params }
} else {
// fallback: replace variant in path
return route.fullPath.replace(
`/${route.params.variant || ''}`,
`/${variant}`
)
}
}
}

View File

@ -0,0 +1,42 @@
export function useVariantData() {
const { t, tm, rt } = useI18n()
const route = useRoute()
const config = useRuntimeConfig()
function vtm(key: string) {
// Get the latest variant *each time* this runs for reactivity!
const variant = route.params.variant || config.public.websites.defaultVariant
const value = tm(key)
if (value && typeof value === 'object' && value[`#${variant}`] !== undefined) {
return value[`#${variant}`]
}
return value
}
function vt(key: string) {
try {
return rt(vtm(key))
} catch {
return key
}
}
function vrt(value) {
const variant = route.params.variant || config.public.websites.defaultVariant
if (value && typeof value === 'object' && value[`#${variant}`]) {
value = value[`#${variant}`]
}
try {
return rt(value)
} catch (err) {
console.error('Exception in vrt:', err)
return value || err
}
}
return {
t: vt,
tm: vtm,
rt: vrt,
}
}

View File

@ -0,0 +1,13 @@
export function useVariants() {
const config = useRuntimeConfig()
const route = useRoute()
const variants = config.public.websites.variants || []
const defaultVariant = config.public.websites.defaultVariant
// Make current a computed so it tracks route changes
const current = computed(() => route.params.variant || defaultVariant)
return {
variants,
current,
}
}