@dargmuesli/nuxt-vio 2.0.1 → 3.0.0-beta.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. package/app.config.ts +78 -34
  2. package/components/{VioApp.vue → vio/_/VioApp.vue} +31 -8
  3. package/components/{VioError.vue → vio/_/VioError.vue} +1 -1
  4. package/components/vio/button/VioButtonColored.vue +52 -0
  5. package/components/vio/card/VioCard.vue +19 -0
  6. package/components/vio/card/state/VioCardState.vue +20 -0
  7. package/components/vio/card/state/VioCardStateAlert.vue +14 -0
  8. package/components/vio/form/VioForm.vue +84 -0
  9. package/components/vio/form/VioFormCheckbox.vue +27 -0
  10. package/components/vio/form/input/VioFormInput.vue +192 -0
  11. package/components/vio/form/input/VioFormInputIconWrapper.vue +7 -0
  12. package/components/vio/form/input/VioFormInputUrl.vue +54 -0
  13. package/components/vio/form/input/state/VioFormInputState.vue +5 -0
  14. package/components/vio/form/input/state/VioFormInputStateError.vue +32 -0
  15. package/components/vio/form/input/state/VioFormInputStateInfo.vue +32 -0
  16. package/components/vio/icon/IconArrowRight.vue +31 -0
  17. package/components/vio/icon/IconCalendar.vue +31 -0
  18. package/components/vio/icon/IconChatOutline.vue +27 -0
  19. package/components/vio/icon/IconChatSolid.vue +26 -0
  20. package/components/vio/icon/IconCheckCircle.vue +29 -0
  21. package/components/vio/icon/IconContainer.vue +15 -0
  22. package/components/vio/icon/IconDownload.vue +31 -0
  23. package/components/vio/icon/IconExclamationCircle.vue +29 -0
  24. package/components/vio/icon/IconHome.vue +31 -0
  25. package/components/vio/icon/IconHourglass.vue +32 -0
  26. package/components/vio/icon/IconLightbulb.vue +27 -0
  27. package/components/vio/icon/IconLogo.vue +17 -0
  28. package/components/vio/icon/IconMixcloud.vue +23 -0
  29. package/components/vio/icon/IconMusic.vue +27 -0
  30. package/components/vio/icon/IconPlay.vue +25 -0
  31. package/components/vio/icon/IconShare.vue +27 -0
  32. package/components/{VioLayout.vue → vio/layout/VioLayout.vue} +1 -1
  33. package/components/vio/layout/VioLayoutBreadcrumbs.vue +83 -0
  34. package/components/vio/layout/VioLayoutFooter.vue +40 -0
  35. package/components/vio/layout/VioLayoutFooterCategory.vue +17 -0
  36. package/components/vio/layout/VioLayoutHeader.vue +98 -0
  37. package/components/vio/layout/VioLayoutSpanList.vue +20 -0
  38. package/components/vio/loader/indicator/VioLoaderIndicator.vue +14 -0
  39. package/components/vio/loader/indicator/VioLoaderIndicatorPing.vue +12 -0
  40. package/components/vio/loader/indicator/VioLoaderIndicatorSpinner.vue +24 -0
  41. package/components/{VioLegalNotice.vue → vio/page/VioPageLegalNotice.vue} +1 -1
  42. package/components/{VioPrivacyPolicy.vue → vio/page/VioPagePrivacyPolicy.vue} +1 -1
  43. package/composables/useAppLayout.ts +2 -2
  44. package/composables/useDateTime.ts +17 -0
  45. package/composables/useFavicons.ts +2 -2
  46. package/composables/useFireError.ts +17 -0
  47. package/composables/useGetServiceHref.ts +47 -0
  48. package/composables/useHeadDefault.ts +34 -0
  49. package/composables/useStrapiFetch.ts +10 -0
  50. package/error.vue +5 -2
  51. package/locales/de.json +7 -1
  52. package/locales/en.json +7 -1
  53. package/nuxt.config.ts +38 -2
  54. package/package.json +23 -10
  55. package/pages/legal-notice.vue +1 -1
  56. package/pages/privacy-policy.vue +1 -1
  57. package/plugins/dayjs.ts +34 -0
  58. package/plugins/i18n.ts +5 -0
  59. package/plugins/marked.ts +9 -0
  60. package/server/middleware/headers.ts +41 -10
  61. package/tailwind.config.ts +129 -8
  62. package/utils/constants.ts +9 -1
  63. package/utils/form.ts +19 -0
  64. package/utils/networking.ts +82 -0
  65. package/utils/text.ts +20 -0
  66. /package/components/{VioLink.vue → vio/_/VioLink.vue} +0 -0
  67. /package/components/{VioButton.vue → vio/button/VioButton.vue} +0 -0
  68. /package/components/{VioHr.vue → vio/layout/VioLayoutHr.vue} +0 -0
@@ -0,0 +1,98 @@
1
+ <template>
2
+ <header class="flex items-center justify-between gap-4 mb-8">
3
+ <VioButton :aria-label="t('creal')" :to="localePath('/')">
4
+ <span class="text-lg font-bold">{{ t('creal') }}</span>
5
+ <template #prefix>
6
+ <IconLogo class="h-10 w-10" />
7
+ </template>
8
+ </VioButton>
9
+ <VioLink
10
+ v-if="eventsCurrentCount"
11
+ class="flex items-center gap-2 rounded-full border px-4 py-2 font-bold focus:rounded-full sm:px-4"
12
+ :is-colored="false"
13
+ :to="localePath('/events')"
14
+ >
15
+ <VioLayoutLivePulse />
16
+ <span class="hidden whitespace-nowrap sm:inline">
17
+ {{ t('live') }}
18
+ </span>
19
+ </VioLink>
20
+ <VioLink
21
+ v-else-if="eventsFutureCount"
22
+ class="flex items-center gap-2 rounded-full border px-2 py-2 font-bold focus:rounded-full sm:px-4"
23
+ :is-colored="false"
24
+ :to="localePath('/events')"
25
+ >
26
+ <VioLayoutLivePulse />
27
+ <span class="hidden whitespace-nowrap sm:inline">
28
+ {{ t('eventsFuture') }}
29
+ </span>
30
+ </VioLink>
31
+ <VioButton
32
+ :aria-label="t('bookCreal')"
33
+ class="basis-0 text-lg font-bold"
34
+ :is-colored="false"
35
+ :to="`mailto:e-mail+creal@jonas-thelemann.de?subject=${encodeURIComponent(
36
+ t('bookingSubject'),
37
+ )}`"
38
+ >
39
+ <span class="basis-0 whitespace-nowrap">{{ t('bookCreal') }}</span>
40
+ <template #suffix>
41
+ <IconArrowRight />
42
+ </template>
43
+ </VioButton>
44
+ </header>
45
+ </template>
46
+
47
+ <script setup lang="ts">
48
+ const { $dayjs } = useNuxtApp()
49
+ const { t } = useI18n()
50
+ const localePath = useLocalePath()
51
+ const strapiFetch = useStrapiFetch()
52
+
53
+ // async data
54
+ let eventsCurrentCount = 0
55
+ let eventsFutureCount = 0
56
+
57
+ // methods
58
+ const init = async () => {
59
+ eventsCurrentCount = (
60
+ (await strapiFetch('/events', {
61
+ query: {
62
+ 'filters[$and][0][dateStart][$lte]': $dayjs().toISOString(),
63
+ 'filters[$and][1][$or][0][dateEnd][$gt]': $dayjs().toISOString(),
64
+ 'filters[$and][1][$or][1][dateStart][$gte]': $dayjs()
65
+ .startOf('day')
66
+ .toISOString(),
67
+ },
68
+ })) as any
69
+ ).meta.pagination.total
70
+ eventsFutureCount = (
71
+ (await strapiFetch('/events', {
72
+ query: {
73
+ 'filters[dateStart][$gt]': $dayjs().toISOString(),
74
+ },
75
+ })) as any
76
+ ).meta.pagination.total
77
+ }
78
+
79
+ // initialization
80
+ try {
81
+ await init()
82
+ } catch (error: any) {}
83
+ </script>
84
+
85
+ <i18n lang="yaml">
86
+ en:
87
+ bookCreal: Book cReal
88
+ bookingSubject: Booking Request
89
+ creal: cReal
90
+ eventsFuture: Upcoming events
91
+ live: Live
92
+ de:
93
+ bookCreal: cReal buchen
94
+ bookingSubject: Buchungsanfrage
95
+ creal: cReal
96
+ eventsFuture: Kommende Veranstaltungen
97
+ live: Live
98
+ </i18n>
@@ -0,0 +1,20 @@
1
+ <template>
2
+ <span v-if="span instanceof String">
3
+ {{ span }}
4
+ </span>
5
+ <span v-else-if="Array.isArray(span) && span.length === 1">
6
+ {{ span[0] }}
7
+ </span>
8
+ <ol v-else-if="Array.isArray(span)" class="list-decimal">
9
+ <li v-for="spanItem in span" :key="spanItem.toString()">
10
+ {{ spanItem }}
11
+ </li>
12
+ </ol>
13
+ </template>
14
+
15
+ <script setup lang="ts">
16
+ export interface Props {
17
+ span: Array<string | Array<string>>
18
+ }
19
+ withDefaults(defineProps<Props>(), {})
20
+ </script>
@@ -0,0 +1,14 @@
1
+ <template>
2
+ <div
3
+ class="flex h-full items-center justify-center"
4
+ :title="t('globalLoading')"
5
+ >
6
+ <div class="flex items-center justify-center w-1/2">
7
+ <slot />
8
+ </div>
9
+ </div>
10
+ </template>
11
+
12
+ <script setup lang="ts">
13
+ const { t } = useI18n()
14
+ </script>
@@ -0,0 +1,12 @@
1
+ <template>
2
+ <VioLoaderIndicator>
3
+ <div
4
+ class="w-1/2 aspect-square animate-ping rounded-full bg-gray-500"
5
+ :title="t('globalLoading')"
6
+ />
7
+ </VioLoaderIndicator>
8
+ </template>
9
+
10
+ <script setup lang="ts">
11
+ const { t } = useI18n()
12
+ </script>
@@ -0,0 +1,24 @@
1
+ <template>
2
+ <VioLoaderIndicator>
3
+ <svg
4
+ class="animate-spin text-black dark:text-white"
5
+ xmlns="http://www.w3.org/2000/svg"
6
+ fill="none"
7
+ viewBox="0 0 24 24"
8
+ >
9
+ <circle
10
+ class="opacity-25"
11
+ cx="12"
12
+ cy="12"
13
+ r="10"
14
+ stroke="currentColor"
15
+ stroke-width="4"
16
+ />
17
+ <path
18
+ class="opacity-75"
19
+ fill="currentColor"
20
+ d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
21
+ />
22
+ </svg>
23
+ </VioLoaderIndicator>
24
+ </template>
@@ -73,7 +73,7 @@ const { t } = useI18n()
73
73
  const title = t('title')
74
74
 
75
75
  // initialization
76
- useSeoMeta({ title })
76
+ useServerSeoMeta({ title })
77
77
  </script>
78
78
 
79
79
  <i18n lang="yaml">
@@ -502,7 +502,7 @@ const { t } = useI18n()
502
502
  const title = t('title')
503
503
 
504
504
  // initialization
505
- useSeoMeta({ title })
505
+ useServerSeoMeta({ title })
506
506
  </script>
507
507
 
508
508
  <i18n lang="yaml">
@@ -1,7 +1,7 @@
1
1
  export const useAppLayout = () => {
2
2
  const appConfig = useAppConfig()
3
3
 
4
- useHead({
4
+ useServerHeadSafe({
5
5
  ...useLocaleHead({ addSeoAttributes: true }).value,
6
6
  bodyAttrs: {
7
7
  class:
@@ -24,6 +24,6 @@ export const useAppLayout = () => {
24
24
  })
25
25
 
26
26
  if (appConfig.seoMeta) {
27
- useSeoMeta(appConfig.seoMeta)
27
+ useServerSeoMeta(appConfig.seoMeta)
28
28
  }
29
29
  }
@@ -0,0 +1,17 @@
1
+ import { Dayjs } from 'dayjs'
2
+
3
+ export const useDateTime = () => {
4
+ const event = useRequestEvent()
5
+ const { $dayjs } = useNuxtApp()
6
+ const timezoneCookie = useCookie(TIMEZONE_COOKIE_NAME)
7
+
8
+ const timezoneHeader = event?.node.req.headers[TIMEZONE_HEADER_KEY]
9
+ const timezone =
10
+ timezoneHeader && !Array.isArray(timezoneHeader)
11
+ ? timezoneHeader
12
+ : timezoneCookie.value || undefined
13
+
14
+ return (dateTime?: string | number | Dayjs | Date | null) =>
15
+ // @ts-ignore `tz` should be part of `$dayjs` (https://github.com/iamkun/dayjs/issues/2106)
16
+ $dayjs(dateTime).tz(timezone)
17
+ }
@@ -1,7 +1,7 @@
1
1
  export const useFavicons = () => {
2
2
  const appConfig = useAppConfig()
3
3
 
4
- useHead({
4
+ useServerHeadSafe({
5
5
  link: [
6
6
  {
7
7
  href: '/assets/static/favicon/apple-touch-icon.png?v=bOXMwoKlJr',
@@ -30,7 +30,7 @@ export const useFavicons = () => {
30
30
  rel: 'manifest',
31
31
  },
32
32
  {
33
- color: appConfig.themeColor,
33
+ color: appConfig.vio.themeColor,
34
34
  href: '/assets/static/favicon/safari-pinned-tab.svg?v=bOXMwoKlJr',
35
35
  rel: 'mask-icon',
36
36
  },
@@ -0,0 +1,17 @@
1
+ import { consola } from 'consola'
2
+ import Swal from 'sweetalert2'
3
+ import { Ref } from 'vue'
4
+
5
+ export const useFireError = () => {
6
+ const { t } = useI18n()
7
+
8
+ return ({ error }: { error: Error }, api?: Ref<any>) => {
9
+ Swal.fire({
10
+ icon: 'error',
11
+ title: t('globalStatusError'),
12
+ text: error.message,
13
+ })
14
+ api?.value.errors.push(error)
15
+ consola.error(error)
16
+ }
17
+ }
@@ -0,0 +1,47 @@
1
+ export const useGetServiceHref = () => {
2
+ const host = useHost()
3
+ const config = useRuntimeConfig()
4
+
5
+ return ({
6
+ isSsr = true,
7
+ name,
8
+ port,
9
+ }: {
10
+ isSsr?: boolean
11
+ name?: string
12
+ port?: number
13
+ }) =>
14
+ getServiceHref({
15
+ host,
16
+ isSsr,
17
+ name,
18
+ port,
19
+ stagingHost: config.public.stagingHost,
20
+ })
21
+ }
22
+
23
+ export const getServiceHref = ({
24
+ host,
25
+ isSsr = true,
26
+ name,
27
+ port,
28
+ stagingHost,
29
+ }: {
30
+ host: string
31
+ isSsr?: boolean
32
+ name?: string
33
+ port?: number
34
+ stagingHost?: string
35
+ }) => {
36
+ const nameSubdomain = name?.replaceAll('_', '-')
37
+ const nameSubdomainString = nameSubdomain ? `${nameSubdomain}.` : ''
38
+ const portString = port ? `:${port}` : ''
39
+
40
+ if (stagingHost) {
41
+ return `https://${nameSubdomainString}${stagingHost}`
42
+ } else if (isSsr && process.server) {
43
+ return `http://${name}${portString}`
44
+ } else {
45
+ return `https://${nameSubdomainString}${getDomainTldPort(host)}`
46
+ }
47
+ }
@@ -0,0 +1,34 @@
1
+ import { defu } from 'defu'
2
+ import type { UseHeadSafeInput } from '@unhead/vue'
3
+ import type { ComputedRef } from 'vue'
4
+
5
+ export const useHeadDefault = (
6
+ title: string | ComputedRef<string>,
7
+ extension?: UseHeadSafeInput,
8
+ ) => {
9
+ const host = useHost()
10
+ const router = useRouter()
11
+
12
+ const defaults: UseHeadSafeInput = {
13
+ meta: [
14
+ {
15
+ id: 'og:title',
16
+ property: 'og:title',
17
+ content: title,
18
+ },
19
+ {
20
+ id: 'og:url',
21
+ property: 'og:url',
22
+ content: `https://${host}${router.currentRoute.value.fullPath}`,
23
+ },
24
+ {
25
+ id: 'twitter:title',
26
+ property: 'twitter:title',
27
+ content: title,
28
+ },
29
+ ],
30
+ title,
31
+ }
32
+
33
+ return useServerHeadSafe(defu(extension, defaults))
34
+ }
@@ -0,0 +1,10 @@
1
+ import { ofetch } from 'ofetch'
2
+
3
+ export const useStrapiFetch = () => {
4
+ const getServiceHref = useGetServiceHref()
5
+
6
+ // TODO: switch back to $fetch (https://github.com/unjs/nitro/issues/470)
7
+ return ofetch.create({
8
+ baseURL: getServiceHref({ name: 'creal_strapi', port: 1337 }) + '/api',
9
+ })
10
+ }
package/error.vue CHANGED
@@ -16,8 +16,11 @@ export interface Props {
16
16
  error: NuxtError
17
17
  }
18
18
  const props = withDefaults(defineProps<Props>(), {})
19
+ const errorProp = toRef(() => props.error)
19
20
 
20
- useHead({
21
- title: `${props.error.statusCode} - ${props.error.message}`,
21
+ // initialization
22
+ useAppLayout()
23
+ useServerHeadSafe({
24
+ title: `${errorProp.value.statusCode} - ${errorProp.value.message}`,
22
25
  })
23
26
  </script>
package/locales/de.json CHANGED
@@ -1,3 +1,9 @@
1
1
  {
2
- "globalLoading": "Lade..."
2
+ "globalLoading": "Lade...",
3
+ "globalPlaceholderUrl": "https://websei.te",
4
+ "globalStatusError": "Fehler",
5
+ "globalValidationFailed": "Bitte überprüfe deine Eingaben 🙈",
6
+ "globalValidationFormatUrlHttps": "Muss mit \"https://\" beginnen",
7
+ "globalValidationLength": "Zu lang",
8
+ "globalValidationRequired": "Pflichtfeld"
3
9
  }
package/locales/en.json CHANGED
@@ -1,3 +1,9 @@
1
1
  {
2
- "globalLoading": "Loading..."
2
+ "globalLoading": "Loading...",
3
+ "globalPlaceholderUrl": "https://websi.te",
4
+ "globalStatusError": "Error",
5
+ "globalValidationFailed": "Please check your input 🙈",
6
+ "globalValidationFormatUrlHttps": "Must start with \"https://\"",
7
+ "globalValidationLength": "Too long",
8
+ "globalValidationRequired": "Required"
3
9
  }
package/nuxt.config.ts CHANGED
@@ -1,7 +1,12 @@
1
1
  import { dirname, join } from 'node:path'
2
2
  import { fileURLToPath } from 'node:url'
3
3
 
4
- import { I18N_MODULE_CONFIG, SITE_NAME } from './utils/constants'
4
+ import {
5
+ I18N_COOKIE_NAME,
6
+ I18N_MODULE_CONFIG,
7
+ TIMEZONE_COOKIE_NAME,
8
+ SITE_NAME,
9
+ } from './utils/constants'
5
10
 
6
11
  const currentDir = dirname(fileURLToPath(import.meta.url))
7
12
 
@@ -27,12 +32,17 @@ export default defineNuxtConfig({
27
32
  titleTemplate: `%s`,
28
33
  title: SITE_NAME, // fallback data to prevent invalid html at generation
29
34
  },
35
+ pageTransition: {
36
+ name: 'layout',
37
+ },
30
38
  },
31
39
  modules: [
32
40
  '@dargmuesli/nuxt-cookie-control',
41
+ '@nuxtjs/color-mode',
33
42
  '@nuxtjs/html-validator',
34
43
  '@nuxtjs/i18n',
35
44
  '@nuxtjs/tailwindcss',
45
+ '@pinia/nuxt',
36
46
  'nuxt-seo-kit-module',
37
47
  ],
38
48
  nitro: {
@@ -46,14 +56,27 @@ export default defineNuxtConfig({
46
56
  },
47
57
  isInProduction: process.env.NODE_ENV === 'production',
48
58
  isTesting: false,
59
+ stagingHost:
60
+ process.env.NODE_ENV !== 'production' &&
61
+ !process.env.NUXT_PUBLIC_STACK_DOMAIN
62
+ ? 'jonas-thelemann.de'
63
+ : undefined,
49
64
  },
50
65
  },
51
66
  typescript: {
52
67
  shim: false,
53
68
  strict: true,
69
+ // tsConfig: {
70
+ // compilerOptions: {
71
+ // esModuleInterop: true,
72
+ // },
73
+ // },
54
74
  },
55
75
 
56
76
  // modules
77
+ colorMode: {
78
+ classSuffix: '',
79
+ },
57
80
  cookieControl: {
58
81
  cookies: {
59
82
  necessary: [
@@ -79,7 +102,19 @@ export default defineNuxtConfig({
79
102
  de: 'Sprache',
80
103
  en: 'Language',
81
104
  },
82
- targetCookieIds: ['i18n_redirected'],
105
+ targetCookieIds: [I18N_COOKIE_NAME],
106
+ },
107
+ {
108
+ description: {
109
+ de: 'Dieser Cookie von uns speichert die Zeitzone, in der sich das Gerät zu befinden scheint.',
110
+ en: 'This cookie of ours saves the timezone in which the device appears to be located.',
111
+ },
112
+ id: 't',
113
+ name: {
114
+ de: 'Zeitzone',
115
+ en: 'Timezone',
116
+ },
117
+ targetCookieIds: [TIMEZONE_COOKIE_NAME],
83
118
  },
84
119
  ],
85
120
  optional: [
@@ -108,6 +143,7 @@ export default defineNuxtConfig({
108
143
  ...I18N_MODULE_CONFIG,
109
144
  defaultLocale: 'en', // Must be set for the default prefix_except_default prefix strategy.
110
145
  detectBrowserLanguage: {
146
+ cookieKey: I18N_COOKIE_NAME,
111
147
  cookieSecure: true,
112
148
  },
113
149
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dargmuesli/nuxt-vio",
3
- "version": "2.0.1",
3
+ "version": "3.0.0-beta.2",
4
4
  "type": "module",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -8,7 +8,7 @@
8
8
  "engines": {
9
9
  "node": "20"
10
10
  },
11
- "packageManager": "pnpm@8.6.11",
11
+ "packageManager": "pnpm@8.6.12",
12
12
  "files": [
13
13
  "assets",
14
14
  "components",
@@ -43,11 +43,25 @@
43
43
  "@dargmuesli/nuxt-cookie-control": "6.1.5",
44
44
  "@dargmuesli/nuxt-vio": "link:",
45
45
  "@http-util/status-i18n": "0.7.0",
46
+ "@nuxtjs/color-mode": "3.3.0",
46
47
  "@nuxtjs/html-validator": "1.5.2",
47
- "@nuxtjs/i18n": "8.0.0-rc.1",
48
+ "@nuxtjs/i18n": "8.0.0-rc.2",
48
49
  "@nuxtjs/tailwindcss": "6.8.0",
50
+ "@pinia/nuxt": "0.4.11",
51
+ "@tailwindcss/forms": "0.5.4",
49
52
  "@tailwindcss/typography": "0.5.9",
53
+ "@urql/core": "4.1.1",
54
+ "@urql/devtools": "2.0.3",
55
+ "@urql/exchange-graphcache": "6.3.1",
56
+ "@urql/vue": "1.1.2",
57
+ "@vuelidate/core": "2.0.3",
58
+ "clipboard": "2.0.11",
59
+ "dayjs": "1.11.9",
60
+ "is-https": "4.0.0",
61
+ "jose": "4.14.4",
62
+ "marked": "7.0.0",
50
63
  "nuxt-seo-kit-module": "2.0.0-beta.9",
64
+ "pinia": "2.1.6",
51
65
  "sweetalert2": "11.7.20",
52
66
  "vue-gtag": "2.0.1"
53
67
  },
@@ -56,25 +70,24 @@
56
70
  "@commitlint/config-conventional": "17.6.7",
57
71
  "@intlify/eslint-plugin-vue-i18n": "3.0.0-next.3",
58
72
  "@nuxtjs/eslint-config-typescript": "12.0.0",
73
+ "@types/marked": "5.0.1",
59
74
  "eslint": "8.46.0",
60
- "eslint-config-prettier": "8.9.0",
75
+ "eslint-config-prettier": "9.0.0",
76
+ "eslint-plugin-compat": "4.1.4",
61
77
  "eslint-plugin-nuxt": "4.0.0",
62
78
  "eslint-plugin-prettier": "5.0.0",
63
79
  "eslint-plugin-yml": "1.8.0",
64
80
  "husky": "8.0.3",
65
81
  "lint-staged": "13.2.3",
66
82
  "nuxt": "3.6.5",
67
- "prettier": "3.0.0",
83
+ "prettier": "3.0.1",
68
84
  "stylelint": "15.10.2",
69
85
  "stylelint-config-recommended-vue": "1.5.0",
70
86
  "stylelint-config-standard": "34.0.0",
71
87
  "stylelint-no-unsupported-browser-features": "7.0.0",
88
+ "tailwindcss": "3.3.3",
72
89
  "typescript": "5.1.6",
90
+ "vue": "3.3.4",
73
91
  "vue-tsc": "1.8.8"
74
- },
75
- "pnpm": {
76
- "overrides": {
77
- "eslint-plugin-vue": "9.15.1"
78
- }
79
92
  }
80
93
  }
@@ -1,3 +1,3 @@
1
1
  <template>
2
- <VioLegalNotice />
2
+ <VioPageLegalNotice />
3
3
  </template>
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <VioPrivacyPolicy
2
+ <VioPagePrivacyPolicy
3
3
  :is-enabled="{
4
4
  summary: {
5
5
  generalNotes: true,
@@ -0,0 +1,34 @@
1
+ import dayjs, { extend, locale } from 'dayjs'
2
+
3
+ // workaround for [1]
4
+ import de from 'dayjs/locale/de'
5
+ // import 'dayjs/locale/de' does not make locale available
6
+
7
+ import isSameOrBefore from 'dayjs/plugin/isSameOrBefore'
8
+ import localizedFormat from 'dayjs/plugin/localizedFormat'
9
+ import timezone from 'dayjs/plugin/timezone'
10
+ import utc from 'dayjs/plugin/utc'
11
+
12
+ export default defineNuxtPlugin((_nuxtApp) => {
13
+ extend(isSameOrBefore)
14
+ extend(localizedFormat)
15
+ extend(timezone)
16
+ extend(utc)
17
+
18
+ // workaround for [1]
19
+ locale(de)
20
+ // dayjs.locale(en) makes `format` error
21
+
22
+ return {
23
+ provide: {
24
+ dayjs,
25
+ },
26
+ }
27
+ })
28
+
29
+ /*
30
+ [1]
31
+ https://github.com/nuxt/framework/issues/7534#issuecomment-1248596609
32
+ https://github.com/nuxt/framework/issues/7206
33
+ https://github.com/maevsi/maevsi/issues/956
34
+ */
@@ -0,0 +1,5 @@
1
+ export default defineNuxtPlugin((nuxtApp) => {
2
+ nuxtApp.hook('i18n:localeSwitched', ({ newLocale }) => {
3
+ nuxtApp.vueApp.$nuxt.$dayjs.locale(newLocale)
4
+ })
5
+ })
@@ -0,0 +1,9 @@
1
+ import { marked } from 'marked'
2
+
3
+ export default defineNuxtPlugin((_nuxtApp) => {
4
+ return {
5
+ provide: {
6
+ marked: marked.parse,
7
+ },
8
+ }
9
+ })