Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .nuxtrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
setups.@nuxt/test-utils="3.23.0"
2 changes: 1 addition & 1 deletion app/app.vue
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const pageName = computed<string>(() => !route.name || typeof route.name !== 'st
/>

<ClientOnly>
<CookieBanner />
<ConsentBanner />
<UiToaster />
<ModalGroup />
<ConfirmDialog
Expand Down
138 changes: 138 additions & 0 deletions app/components/atoms/ConsentBanner.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
<script setup lang="ts">
const {
showPopup,
consents,
consentTypes,
toggleConsent,
acceptAll,
rejectAll,
savePreferences,
} = useConsent()

const showSettings = ref(false)
</script>

<template>
<div
v-if="showPopup"
tabindex="0"
:aria-label="$t('components.ConsentBanner.banner.title')"
aria-modal="true"
role="dialog"
:data-state="showPopup ? 'open' : 'closed'"
data-test-banner
class="fixed border-4 border-muted bg-background shadow-lg z-50 md:w-md overflow-hidden rounded-2xl right-4 left-4 bottom-4 p-4 duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom"
>
<div
v-if="!showSettings"
data-test-banner-info
class="flex flex-col gap-6"
>
<div class="flex flex-col gap-1">
<p class="font-bold">
{{ $t('components.ConsentBanner.banner.title') }}
</p>
<p class="text-muted-foreground text-sm">
{{ $t('components.ConsentBanner.banner.text') }}
</p>
</div>
<div class="flex flex-col md:flex-row justify-between gap-2">
<div class="flex flex-row gap-2">
<UiButton
data-test-reject-all
variant="secondary"
class="w-full md:w-fit"
@click="rejectAll"
>
{{ $t('components.ConsentBanner.rejectAll') }}
</UiButton>
<UiButton
data-test-accept-all
variant="secondary"
class="w-full md:w-fit"
@click="acceptAll"
>
{{ $t('components.ConsentBanner.acceptAll') }}
</UiButton>
</div>
<UiButton
data-test-customize
variant="tertiary"
@click="showSettings = true"
>
{{ $t('components.ConsentBanner.customize') }}
</UiButton>
</div>
</div>

<div
v-else
data-test-banner-settings
class="flex flex-col gap-6"
>
<div class="flex flex-col gap-1">
<p class="font-bold">
{{ $t('components.ConsentBanner.settings.title') }}
</p>
<p class="text-muted-foreground text-sm">
{{ $t('components.ConsentBanner.settings.text') }}
</p>
</div>

<div class="flex flex-col gap-2">
<div
v-for="type in consentTypes"
:key="type"
class="border rounded-lg p-4 flex flex-col gap-2"
>
<div class="flex items-center justify-between">
<UiLabel :for="type">
{{ $t(`components.ConsentBanner.${type}`) }}
</UiLabel>
<UiSwitch
:id="type"
:checked="consents?.[type] || false"
:default-value="consents?.[type] || false"
:disabled="type === 'necessary'"
@update:model-value="() => {
console.log('toggling consent for', type)
toggleConsent(type)
}"
/>
</div>
<p class="text-sm text-muted-foreground">
{{ $t(`components.ConsentBanner.${type}Text`) }}
</p>
</div>
</div>

<div class="flex flex-col md:flex-row justify-between gap-2">
<div class="flex flex-row gap-2">
<UiButton
data-test-reject-all
class="w-full md:w-fit"
variant="secondary"
@click="rejectAll"
>
{{ $t('components.ConsentBanner.rejectAll') }}
</UiButton>
<UiButton
data-test-accept-all
class="w-full md:w-fit"
variant="secondary"
@click="acceptAll"
>
{{ $t('components.ConsentBanner.acceptAll') }}
</UiButton>
</div>
<UiButton
data-test-save-preferences
variant="tertiary"
@click="savePreferences"
>
{{ $t('components.ConsentBanner.saveSettings') }}
</UiButton>
</div>
</div>
</div>
</template>
42 changes: 0 additions & 42 deletions app/components/atoms/CookieBanner.vue

This file was deleted.

154 changes: 114 additions & 40 deletions app/components/atoms/FantasyNameGenerator.vue
Original file line number Diff line number Diff line change
@@ -1,21 +1,34 @@
<script setup lang="ts">
import { useToast } from '~/components/ui/toast/use-toast'
import { raceOptions, genderOptions } from '~~/constants/names'

const props = defineProps<{ amount: number }>()
const props = withDefaults(
defineProps<{
amount: number
compact?: boolean
}>(), {
compact: false,
},
)

const { copy } = useClipboard()
const { toast } = useToast()
const { t } = useI18n()

const names = ref<string[]>([])
const selectedRace = ref<DndRace | 'random'>('random')
const selectedGender = ref<Gender | 'random'>('random')

onMounted(() => generate())

function generate(): void {
names.value = []

const race = selectedRace.value === 'random' ? undefined : selectedRace.value
const gender = selectedGender.value === 'random' ? undefined : selectedGender.value

for (let i = 0; i < props.amount; i++) {
names.value.push(randomName())
names.value.push(randomName(race, gender))
}
}

Expand All @@ -27,49 +40,110 @@ function handleCopy(name: string): void {
variant: 'info',
})
}

watch([selectedRace, selectedGender], () => {
generate()
})
</script>

<template>
<MasonryGrid
v-if="names.length"
v-slot="{ column }"
:data="names"
:max-columns="2"
wrapper-style="grid list-disc list-inside gap-x-6"
column-style="flex flex-col gap-1"
element="ol"
>
<li
v-for="name in column"
:key="name"
class="cursor-copy"
@click="handleCopy(name)"
>
{{ name }}
</li>
</MasonryGrid>
<SkeletonList
v-else
:amount="amount"
/>
<div class="flex flex-col gap-4">
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="flex flex-col gap-2">
<label
v-if="!compact"
data-test-label
class="text-sm font-medium"
>
Race
</label>
<UiSelect v-model="selectedRace">
<UiSelectTrigger>
<UiSelectValue />
</UiSelectTrigger>
<UiSelectContent>
<UiSelectItem
v-for="option in raceOptions"
:key="option.value"
:value="option.value"
>
{{ option.label }}
</UiSelectItem>
</UiSelectContent>
</UiSelect>
</div>

<div class="flex flex-col w-full gap-2 mt-4">
<div class="text-muted-foreground flex gap-2 items-center">
<Icon
name="tabler:info-circle"
class="text-info"
/>
<p class="text-xs">
{{ $t('pages.fantasyNameGenerator.tip') }}
</p>
<div class="flex flex-col gap-2">
<label
v-if="!compact"
data-test-label
class="text-sm font-medium"
>
Gender
</label>
<UiSelect v-model="selectedGender">
<UiSelectTrigger>
<UiSelectValue />
</UiSelectTrigger>
<UiSelectContent>
<UiSelectItem
v-for="option in genderOptions"
:key="option.value"
:value="option.value"
>
{{ option.label }}
</UiSelectItem>
</UiSelectContent>
</UiSelect>
</div>
</div>
<UiButton
data-test-generate
:disabled="!names.length"
class="self-end"
@click="generate"

<MasonryGrid
v-if="names.length"
v-slot="{ column }"
:data="names"
:max-columns="compact ? 1 : 2"
wrapper-style="grid list-disc list-inside gap-x-6"
column-style="flex flex-col gap-1"
element="ol"
>
{{ $t('actions.generate') }}
</UiButton>
<li
v-for="name in column"
:key="name"
class="cursor-copy hover:bg-muted rounded px-2 transition-colors duration-300"
:class="compact && 'text-sm'"
@click="handleCopy(name)"
>
{{ name }}
</li>
</MasonryGrid>
<SkeletonList
v-else
:amount="amount"
/>

<div
data-test-actions
class="flex w-full gap-2"
:class="compact ? '' : 'flex-col mt-4'"
>
<div class="text-muted-foreground flex gap-2 items-center">
<Icon
name="tabler:info-circle"
class="text-info w-4 min-w-4"
/>
<p class="text-xs">
{{ $t('pages.fantasyNameGenerator.tip') }}
</p>
</div>
<UiButton
data-test-generate
:disabled="!names.length"
class="self-end"
@click="generate"
>
{{ $t('actions.generate') }}
</UiButton>
</div>
</div>
</template>
Loading