Skip to content

Commit d74812e

Browse files
userquinautofix-ci[bot]danielroe
authored
feat(i18n,ui): add privacy policy page (#900)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Daniel Roe <daniel@roe.dev>
1 parent 65212b1 commit d74812e

File tree

9 files changed

+629
-2
lines changed

9 files changed

+629
-2
lines changed

app/components/AppFooter.vue

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,12 @@ const isHome = computed(() => route.name === 'index')
1818
<NuxtLink to="/about" class="link-subtle font-mono text-xs flex items-center">
1919
{{ $t('footer.about') }}
2020
</NuxtLink>
21+
<NuxtLink
22+
to="/privacy"
23+
class="link-subtle font-mono text-xs min-h-11 flex items-center gap-1 lowercase"
24+
>
25+
{{ $t('privacy_policy.title') }}
26+
</NuxtLink>
2127
<a
2228
href="https://docs.npmx.dev"
2329
target="_blank"

app/components/Header/MobileMenu.client.vue

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,15 @@ onUnmounted(deactivate)
112112
{{ $t('footer.about') }}
113113
</NuxtLink>
114114

115+
<NuxtLink
116+
to="/privacy"
117+
class="flex items-center gap-3 px-3 py-3 rounded-md font-mono text-sm text-fg hover:bg-bg-subtle transition-colors duration-200"
118+
@click="closeMenu"
119+
>
120+
<span class="i-carbon:security w-5 h-5 text-fg-muted" aria-hidden="true" />
121+
{{ $t('privacy_policy.title') }}
122+
</NuxtLink>
123+
115124
<NuxtLink
116125
to="/compare"
117126
class="flex items-center gap-3 px-3 py-3 rounded-md font-mono text-sm text-fg hover:bg-bg-subtle transition-colors duration-200"

app/pages/privacy.vue

Lines changed: 354 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,354 @@
1+
<script setup lang="ts">
2+
definePageMeta({
3+
name: 'privacy',
4+
})
5+
6+
useSeoMeta({
7+
title: () => `${$t('privacy_policy.title')} - npmx`,
8+
description: () => $t('privacy_policy.welcome', { app: 'npmx' }),
9+
})
10+
11+
defineOgImageComponent('Default', {
12+
title: () => $t('privacy_policy.title'),
13+
description: () => $t('privacy_policy.welcome', { app: 'npmx' }),
14+
})
15+
16+
const router = useRouter()
17+
const buildInfo = useAppConfig().buildInfo
18+
const { locale } = useI18n()
19+
</script>
20+
21+
<template>
22+
<main class="container flex-1 py-12 sm:py-16 overflow-x-hidden">
23+
<article class="max-w-2xl mx-auto">
24+
<header class="mb-12">
25+
<div class="flex items-baseline justify-between gap-4 mb-4">
26+
<h1 class="font-mono text-3xl sm:text-4xl font-medium">
27+
{{ $t('privacy_policy.title') }}
28+
</h1>
29+
<button
30+
type="button"
31+
:title="$t('nav.back')"
32+
class="inline-flex items-center gap-2 font-mono text-sm text-fg-muted hover:text-fg transition-colors duration-200 rounded focus-visible:outline-accent/70 shrink-0"
33+
@click="router.back()"
34+
>
35+
<span class="i-carbon:arrow-left rtl-flip w-4 h-4" aria-hidden="true" />
36+
<span class="hidden sm:inline">{{ $t('nav.back') }}</span>
37+
</button>
38+
</div>
39+
<i18n-t
40+
keypath="privacy_policy.last_updated"
41+
tag="p"
42+
scope="global"
43+
class="text-fg-muted text-lg"
44+
>
45+
<template #date>
46+
<NuxtTime
47+
:locale
48+
:datetime="buildInfo.privacyPolicyDate"
49+
date-style="long"
50+
time-style="medium"
51+
/>
52+
</template>
53+
</i18n-t>
54+
</header>
55+
56+
<section class="prose prose-invert max-w-none space-y-8">
57+
<p class="text-fg-muted leading-relaxed">
58+
<i18n-t keypath="privacy_policy.welcome" tag="span" scope="global">
59+
<template #app>
60+
<strong class="text-fg">npmx</strong>
61+
</template>
62+
</i18n-t>
63+
</p>
64+
65+
<!-- What are cookies -->
66+
<div>
67+
<h2 class="text-lg text-fg-subtle uppercase tracking-wider mb-4">
68+
{{ $t('privacy_policy.cookies.what_are.title') }}
69+
</h2>
70+
<p class="text-fg-muted leading-relaxed">
71+
{{ $t('privacy_policy.cookies.what_are.p1') }}
72+
</p>
73+
</div>
74+
75+
<!-- What cookies do we use -->
76+
<div>
77+
<h2 class="text-lg text-fg-subtle uppercase tracking-wider mb-4">
78+
{{ $t('privacy_policy.cookies.types.title') }}
79+
</h2>
80+
<p class="text-fg-muted leading-relaxed mb-4">
81+
<i18n-t keypath="privacy_policy.cookies.types.p1" tag="span" scope="global">
82+
<template #bold>
83+
<strong class="text-fg">{{ $t('privacy_policy.cookies.types.bold') }}</strong>
84+
</template>
85+
</i18n-t>
86+
</p>
87+
<ul class="space-y-3 text-fg-muted list-none p-0">
88+
<li class="flex items-start gap-3">
89+
<span class="text-fg-subtle shrink-0 mt-1">&mdash;</span>
90+
<span>
91+
<i18n-t keypath="privacy_policy.cookies.types.li1" tag="span">
92+
<template #li11>
93+
<strong class="text-fg font-mono text-sm">
94+
<bdi>{{ $t('privacy_policy.cookies.types.cookie_vdpl') }}</bdi>
95+
</strong>
96+
</template>
97+
<template #separator>
98+
<bdi>{{ $t('privacy_policy.cookies.types.separator') }}</bdi>
99+
</template>
100+
<template #li12>
101+
<bdi>{{ $t('privacy_policy.cookies.types.cookie_vdpl_desc') }}</bdi>
102+
</template>
103+
</i18n-t>
104+
</span>
105+
</li>
106+
<li class="flex items-start gap-3">
107+
<span class="text-fg-subtle shrink-0 mt-1">&mdash;</span>
108+
<span>
109+
<i18n-t keypath="privacy_policy.cookies.types.li2" tag="span">
110+
<template #li21>
111+
<strong class="text-fg font-mono text-sm">
112+
<bdi>{{ $t('privacy_policy.cookies.types.cookie_h3') }}</bdi>
113+
</strong>
114+
</template>
115+
<template #separator>
116+
<bdi>{{ $t('privacy_policy.cookies.types.separator') }}</bdi>
117+
</template>
118+
<template #li22>
119+
<bdi>{{ $t('privacy_policy.cookies.types.cookie_h3_desc') }}</bdi>
120+
</template>
121+
</i18n-t>
122+
</span>
123+
</li>
124+
</ul>
125+
</div>
126+
127+
<!-- Local storage -->
128+
<div>
129+
<h2 class="text-lg text-fg-subtle uppercase tracking-wider mb-4">
130+
{{ $t('privacy_policy.cookies.local_storage.title') }}
131+
</h2>
132+
<p class="text-fg-muted leading-relaxed mb-4">
133+
<i18n-t keypath="privacy_policy.cookies.local_storage.p1" tag="span" scope="global">
134+
<template #bold>
135+
<strong class="text-fg">{{
136+
$t('privacy_policy.cookies.local_storage.bold')
137+
}}</strong>
138+
</template>
139+
<template #settings>
140+
<NuxtLink
141+
to="/settings"
142+
class="text-fg-muted hover:text-fg underline decoration-fg-subtle/50 hover:decoration-fg"
143+
>
144+
{{ $t('privacy_policy.cookies.local_storage.settings') }}
145+
</NuxtLink>
146+
</template>
147+
</i18n-t>
148+
</p>
149+
<p class="text-fg-muted leading-relaxed">
150+
<i18n-t keypath="privacy_policy.cookies.local_storage.p2" tag="span" scope="global">
151+
<template #bold2>
152+
<strong class="text-fg">{{
153+
$t('privacy_policy.cookies.local_storage.bold2')
154+
}}</strong>
155+
</template>
156+
</i18n-t>
157+
</p>
158+
</div>
159+
160+
<!-- Managing cookies -->
161+
<div>
162+
<h2 class="text-lg text-fg-subtle uppercase tracking-wider mb-4">
163+
{{ $t('privacy_policy.cookies.management.title') }}
164+
</h2>
165+
<p class="text-fg-muted leading-relaxed mb-4">
166+
<i18n-t keypath="privacy_policy.cookies.management.p1" tag="span" scope="global">
167+
<template #bold>
168+
<strong class="text-fg">{{ $t('privacy_policy.cookies.management.bold') }}</strong>
169+
</template>
170+
</i18n-t>
171+
</p>
172+
<p class="text-fg-muted leading-relaxed mb-4">
173+
{{ $t('privacy_policy.cookies.management.p2') }}
174+
</p>
175+
<ul class="space-y-3 text-fg-muted list-none p-0">
176+
<li class="flex items-start gap-3">
177+
<span class="text-fg-subtle shrink-0 mt-1">&mdash;</span>
178+
<a
179+
href="https://support.google.com/chrome/answer/95647?hl=en"
180+
target="_blank"
181+
rel="noopener noreferrer"
182+
class="inline-flex items-center gap-1 text-fg-muted hover:text-fg underline decoration-fg-subtle/50 hover:decoration-fg"
183+
>
184+
{{ $t('privacy_policy.cookies.management.chrome') }}
185+
<span class="i-carbon:launch rtl-flip w-4 h-4" aria-hidden="true" />
186+
</a>
187+
</li>
188+
<li class="flex items-start gap-3">
189+
<span class="text-fg-subtle shrink-0 mt-1">&mdash;</span>
190+
<a
191+
href="https://support.mozilla.org/en-US/kb/clear-cookies-and-site-data-firefox"
192+
target="_blank"
193+
rel="noopener noreferrer"
194+
class="inline-flex items-center gap-1 text-fg-muted hover:text-fg underline decoration-fg-subtle/50 hover:decoration-fg"
195+
>
196+
{{ $t('privacy_policy.cookies.management.firefox') }}
197+
<span class="i-carbon:launch rtl-flip w-4 h-4" aria-hidden="true" />
198+
</a>
199+
</li>
200+
<li class="flex items-start gap-3">
201+
<span class="text-fg-subtle shrink-0 mt-1">&mdash;</span>
202+
<a
203+
href="https://support.microsoft.com/en-us/windows/manage-cookies-in-microsoft-edge-view-allow-block-delete-and-use-168dab11-0753-043d-7c16-ede5947fc64d"
204+
target="_blank"
205+
rel="noopener noreferrer"
206+
class="inline-flex items-center gap-1 text-fg-muted hover:text-fg underline decoration-fg-subtle/50 hover:decoration-fg"
207+
>
208+
{{ $t('privacy_policy.cookies.management.edge') }}
209+
<span class="i-carbon:launch rtl-flip w-4 h-4" aria-hidden="true" />
210+
</a>
211+
</li>
212+
</ul>
213+
</div>
214+
215+
<!-- Analytics -->
216+
<div>
217+
<h2 class="text-lg text-fg-subtle uppercase tracking-wider mb-4">
218+
{{ $t('privacy_policy.analytics.title') }}
219+
</h2>
220+
<p class="text-fg-muted leading-relaxed mb-4">
221+
<i18n-t keypath="privacy_policy.analytics.p1" tag="span" scope="global">
222+
<template #bold>
223+
<strong class="text-fg">{{ $t('privacy_policy.analytics.bold') }}</strong>
224+
</template>
225+
</i18n-t>
226+
</p>
227+
<p class="text-fg-muted leading-relaxed mb-4">
228+
{{ $t('privacy_policy.analytics.p2') }}
229+
</p>
230+
<ul class="space-y-3 text-fg-muted list-none p-0 mb-4">
231+
<li class="flex items-start gap-3">
232+
<span class="text-fg-subtle shrink-0 mt-1">&mdash;</span>
233+
<span>{{ $t('privacy_policy.analytics.li1') }}</span>
234+
</li>
235+
<li class="flex items-start gap-3">
236+
<span class="text-fg-subtle shrink-0 mt-1">&mdash;</span>
237+
<span>{{ $t('privacy_policy.analytics.li2') }}</span>
238+
</li>
239+
<li class="flex items-start gap-3">
240+
<span class="text-fg-subtle shrink-0 mt-1">&mdash;</span>
241+
<span>{{ $t('privacy_policy.analytics.li3') }}</span>
242+
</li>
243+
<li class="flex items-start gap-3">
244+
<span class="text-fg-subtle shrink-0 mt-1">&mdash;</span>
245+
<span>{{ $t('privacy_policy.analytics.li4') }}</span>
246+
</li>
247+
</ul>
248+
<p class="text-fg-muted leading-relaxed">
249+
{{ $t('privacy_policy.analytics.p3') }}
250+
</p>
251+
</div>
252+
253+
<!-- Authenticated Users -->
254+
<div>
255+
<h2 class="text-lg text-fg-subtle uppercase tracking-wider mb-4">
256+
{{ $t('privacy_policy.authenticated.title') }}
257+
</h2>
258+
<p class="text-fg-muted leading-relaxed mb-4">
259+
<i18n-t keypath="privacy_policy.authenticated.p1" tag="span" scope="global">
260+
<template #bold>
261+
<strong class="text-fg">{{ $t('privacy_policy.authenticated.bold') }}</strong>
262+
</template>
263+
</i18n-t>
264+
</p>
265+
<p class="text-fg-muted leading-relaxed">
266+
<i18n-t keypath="privacy_policy.authenticated.p2" tag="span" scope="global">
267+
<template #settings>
268+
<NuxtLink
269+
to="/settings"
270+
class="text-fg-muted hover:text-fg underline decoration-fg-subtle/50 hover:decoration-fg"
271+
>
272+
{{ $t('privacy_policy.authenticated.settings') }}
273+
</NuxtLink>
274+
</template>
275+
</i18n-t>
276+
</p>
277+
</div>
278+
279+
<!-- Data Retention -->
280+
<div>
281+
<h2 class="text-lg text-fg-subtle uppercase tracking-wider mb-4">
282+
{{ $t('privacy_policy.data_retention.title') }}
283+
</h2>
284+
<p class="text-fg-muted leading-relaxed">
285+
{{ $t('privacy_policy.data_retention.p1') }}
286+
</p>
287+
</div>
288+
289+
<!-- Your Rights -->
290+
<div>
291+
<h2 class="text-lg text-fg-subtle uppercase tracking-wider mb-4">
292+
{{ $t('privacy_policy.your_rights.title') }}
293+
</h2>
294+
<p class="text-fg-muted leading-relaxed mb-4">
295+
{{ $t('privacy_policy.your_rights.p1') }}
296+
</p>
297+
<ul class="space-y-3 text-fg-muted list-none p-0 mb-4">
298+
<li class="flex items-start gap-3">
299+
<span class="text-fg-subtle shrink-0 mt-1">&mdash;</span>
300+
<span>{{ $t('privacy_policy.your_rights.li1') }}</span>
301+
</li>
302+
<li class="flex items-start gap-3">
303+
<span class="text-fg-subtle shrink-0 mt-1">&mdash;</span>
304+
<span>{{ $t('privacy_policy.your_rights.li2') }}</span>
305+
</li>
306+
<li class="flex items-start gap-3">
307+
<span class="text-fg-subtle shrink-0 mt-1">&mdash;</span>
308+
<span>{{ $t('privacy_policy.your_rights.li3') }}</span>
309+
</li>
310+
<li class="flex items-start gap-3">
311+
<span class="text-fg-subtle shrink-0 mt-1">&mdash;</span>
312+
<span>{{ $t('privacy_policy.your_rights.li4') }}</span>
313+
</li>
314+
</ul>
315+
<p class="text-fg-muted leading-relaxed">
316+
{{ $t('privacy_policy.your_rights.p2') }}
317+
</p>
318+
</div>
319+
320+
<!-- Contact -->
321+
<div>
322+
<h2 class="text-lg text-fg-subtle uppercase tracking-wider mb-4">
323+
{{ $t('privacy_policy.contact.title') }}
324+
</h2>
325+
<p class="text-fg-muted leading-relaxed">
326+
<i18n-t keypath="privacy_policy.contact.p1" tag="span" scope="global">
327+
<template #link>
328+
<a
329+
href="https://github.com/npmx-dev/npmx.dev/issues"
330+
target="_blank"
331+
rel="noopener noreferrer"
332+
class="inline-flex items-center gap-1 text-fg-muted hover:text-fg underline decoration-fg-subtle/50 hover:decoration-fg"
333+
>
334+
{{ $t('privacy_policy.contact.link') }}
335+
<span class="i-carbon:launch rtl-flip w-4 h-4" aria-hidden="true" />
336+
</a>
337+
</template>
338+
</i18n-t>
339+
</p>
340+
</div>
341+
342+
<!-- Changes -->
343+
<div>
344+
<h2 class="text-lg text-fg-subtle uppercase tracking-wider mb-4">
345+
{{ $t('privacy_policy.changes.title') }}
346+
</h2>
347+
<p class="text-fg-muted leading-relaxed">
348+
{{ $t('privacy_policy.changes.p1') }}
349+
</p>
350+
</div>
351+
</section>
352+
</article>
353+
</main>
354+
</template>

0 commit comments

Comments
 (0)