Added 'Terms of Service' and 'Privacy policy' pages
All checks were successful
Production Build and Deploy / Build (push) Successful in 1m9s
Production Build and Deploy / Deploy (push) Successful in 28s

This commit is contained in:
Liviu Burcusel 2026-01-15 15:53:35 +01:00
parent 0add58254d
commit 176665d7d4
Signed by: liviu
GPG key ID: 6CDB37A4AD2C610C
14 changed files with 98 additions and 24 deletions

View file

@ -58,8 +58,8 @@ const doLogin = async () => {
</CardContent>
</Card>
<FieldDescription class="px-6 text-center">
<NuxtLink to="/"><Button variant="link">Terms of Service</Button></NuxtLink>
<NuxtLink to="/"><Button variant="link">Privacy Policy</Button></NuxtLink>
<NuxtLink to="/legal/terms-of-service"><Button variant="link">Terms of Service</Button></NuxtLink>
<NuxtLink to="/legal/privacy-policy"><Button variant="link">Privacy Policy</Button></NuxtLink>
</FieldDescription>
</div>
</template>

View file

@ -74,8 +74,8 @@ const createAccount = async () => {
</Card>
</ClientOnly>
<FieldDescription class="px-6 text-center">
<NuxtLink to="/"><Button variant="link">Terms of Service</Button></NuxtLink>
<NuxtLink to="/"><Button variant="link">Privacy Policy</Button></NuxtLink>
<NuxtLink to="/legal/terms-of-service"><Button variant="link">Terms of Service</Button></NuxtLink>
<NuxtLink to="/legal/privacy-policy"><Button variant="link">Privacy Policy</Button></NuxtLink>
</FieldDescription>
</div>
</template>

View file

@ -8,12 +8,8 @@ import { SidebarInset, SidebarProvider, SidebarTrigger } from "~/components/ui/s
import { Separator } from "~/components/ui/separator";
import { useRuntimeConfig } from "#app";
const currentYear = new Date().getFullYear();
const config = useRuntimeConfig();
const authStore = useAuthStore();
await authStore.init();
</script>
@ -31,12 +27,8 @@ await authStore.init();
<slot />
</main>
<footer class="flex h-12 shrink-0 items-center gap-2 border-b px-4" data-testid="footer">
<div v-if="currentYear === 2025" class="bg-muted/50 flex-1 rounded-xl p-2 text-center">
Glowing Fiesta 2025 <span class="text-muted-foreground">({{ config.public.appVersion }})</span>
</div>
<div v-else class="bg-muted/50 flex-1 rounded-xl p-2 text-center">
Glowing Fiesta 2025 - {{ currentYear }} <span class="text-muted-foreground">({{ config.public.appVersion }})</span>
</div>
<div v-if="currentYear === 2025" class="bg-muted/50 flex-1 rounded-xl p-2 text-center">Glowing Fiesta 2025</div>
<div v-else class="bg-muted/50 flex-1 rounded-xl p-2 text-center">Glowing Fiesta 2025 - {{ currentYear }}</div>
</footer>
</SidebarInset>
</SidebarProvider>

View file

@ -1,6 +1,7 @@
<script setup lang="ts">
import { computed } from "vue";
import { BookOpen, ChevronRight, HandCoins, Settings2, SquareTerminal } from "lucide-vue-next";
import { useRuntimeConfig } from "#app";
import { BookOpen, BookOpenText, ChevronRight, HandCoins, Settings2, SquareTerminal } from "lucide-vue-next";
import {
Sidebar,
@ -53,6 +54,16 @@ const data = {
{ title: "Changelog", url: "#" },
],
},
{
title: "Legal",
url: "#",
icon: BookOpenText,
isActive: true,
items: [
{ title: "Terms of Service", url: "/legal/terms-of-service" },
{ title: "Privacy Policy", url: "/legal/privacy-policy" },
],
},
{
title: "Settings",
url: "#",
@ -85,6 +96,8 @@ const props = withDefaults(defineProps<SidebarLayoutProps>(), {
});
const navMain = computed(() => props.navItems || data.navMain);
const config = useRuntimeConfig();
</script>
<template>
@ -101,7 +114,7 @@ const navMain = computed(() => props.navItems || data.navMain);
</div>
<div class="flex flex-col gap-0.5 leading-none">
<span class="font-bold text-primary">Glowing Fiesta</span>
<span>v1.0.0</span>
<span>v{{ config.public.appVersion }}</span>
</div>
</NuxtLink>
</SidebarMenuButton>

View file

@ -41,8 +41,8 @@ useSeoMeta({
</Card>
<FieldDescription class="px-6 text-center">
<NuxtLink to="/"><Button variant="link">Home</Button></NuxtLink>
<NuxtLink to="/"><Button variant="link">Terms of Service</Button></NuxtLink>
<NuxtLink to="/"><Button variant="link">Privacy Policy</Button></NuxtLink>
<NuxtLink to="/legal/terms-of-service"><Button variant="link">Terms of Service</Button></NuxtLink>
<NuxtLink to="/legal/privacy-policy"><Button variant="link">Privacy Policy</Button></NuxtLink>
</FieldDescription>
</div>
</div>

View file

@ -0,0 +1,22 @@
<script setup lang="ts">
import { useRuntimeConfig, useSeoMeta } from "#app";
import { useBreadcrumbStore } from "~/stores/breadcrumbs";
const breadcrumbStore = useBreadcrumbStore();
breadcrumbStore.setBreadcrumbs([{ label: "Legal" }, { label: "Privacy Policy", to: "/legal/privacy-policy" }]);
const config = useRuntimeConfig();
useSeoMeta({
title: "Privacy Policy",
description: "Privacy Policy page of a very nice all-purpose application",
ogTitle: "Privacy Policy",
ogDescription: "Privacy Policy page of a very nice all-purpose application",
ogImage: config.public.siteUrl + "/images/human.png",
});
</script>
<template>
<div class="bg-muted/50 min-h-screen flex-1 rounded-xl md:min-h-min flex px-4 py-2 gap-2">
<h1 class="text-2xl font-bold text-primary">Privacy Policy</h1>
</div>
</template>

View file

@ -0,0 +1,22 @@
<script setup lang="ts">
import { useRuntimeConfig, useSeoMeta } from "#app";
import { useBreadcrumbStore } from "~/stores/breadcrumbs";
const breadcrumbStore = useBreadcrumbStore();
breadcrumbStore.setBreadcrumbs([{ label: "Legal" }, { label: "Terms of Service", to: "/legal/terms-of-service" }]);
const config = useRuntimeConfig();
useSeoMeta({
title: "Terms of Service",
description: "Terms of Service page of a very nice all-purpose application",
ogTitle: "Terms of Service",
ogDescription: "Terms of Service page of a very nice all-purpose application",
ogImage: config.public.siteUrl + "/images/human.png",
});
</script>
<template>
<div class="bg-muted/50 min-h-screen flex-1 rounded-xl md:min-h-min flex px-4 py-2 gap-2">
<h1 class="text-2xl font-bold text-primary">Terms of Service</h1>
</div>
</template>

4
package-lock.json generated
View file

@ -1,12 +1,12 @@
{
"name": "glowing-fiesta",
"version": "0.0.0",
"version": "0.0.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "glowing-fiesta",
"version": "0.0.0",
"version": "0.0.1",
"hasInstallScript": true,
"dependencies": {
"@pinia/nuxt": "^0.11.3",

View file

@ -1,6 +1,6 @@
{
"name": "glowing-fiesta",
"version": "0.0.0",
"version": "0.0.1",
"type": "module",
"private": true,
"scripts": {

View file

@ -63,7 +63,7 @@ describe("Default.vue", () => {
});
await flushPromises();
const footer = wrapper.find("[data-testid='footer']");
expect(footer.text()).toBe("Glowing Fiesta 2025 (1.0.1)");
expect(footer.text()).toBe("Glowing Fiesta 2025");
});
it("footer shows range when current year is not 2025", async () => {
@ -75,7 +75,7 @@ describe("Default.vue", () => {
});
await flushPromises();
const footer = wrapper.find("[data-testid='footer']");
expect(footer.text()).toBe("Glowing Fiesta 2025 - 2069 (1.0.1)");
expect(footer.text()).toBe("Glowing Fiesta 2025 - 2069");
});
});
});

View file

@ -3,6 +3,7 @@ import { describe, expect, it, vi, beforeAll, afterAll } from "vitest";
import SidebarLayout from "~/layouts/default/Sidebar.vue";
import { ref } from "vue";
import type * as SidebarUI from "~/components/ui/sidebar";
import { useRuntimeConfig } from "#app";
const { useSidebarMock } = vi.hoisted(() => ({
useSidebarMock: vi.fn(),
@ -112,6 +113,7 @@ vi.mock("lucide-vue-next", () => {
ChevronsUpDown: MockIcon,
CreditCard: MockIcon,
BookOpen: MockIcon,
BookOpenText: MockIcon,
HandCoins: MockIcon,
LogIn: MockIcon,
LogOut: MockIcon,
@ -162,7 +164,7 @@ describe("SidebarLayout", () => {
});
await flushPromises();
expect(wrapper.text()).toContain("Glowing Fiesta");
expect(wrapper.text()).toContain("v1.0.0");
expect(wrapper.text()).toContain(`v${useRuntimeConfig().public.appVersion as string}`);
});
it("renders sidebar content correctly", async () => {

View file

@ -0,0 +1,11 @@
import { describe, expect, it } from "vitest";
import { mount, type VueWrapper } from "@vue/test-utils";
import PrivacyPolicyPage from "~/pages/legal/privacy-policy.vue";
describe("pages/legal/privacy-policy.vue", () => {
it("loads without crashing", () => {
const wrapper: VueWrapper = mount(PrivacyPolicyPage);
expect(wrapper.exists()).toBe(true);
expect(wrapper.text()).toContain("Privacy Policy");
});
});

View file

@ -0,0 +1,11 @@
import { describe, expect, it } from "vitest";
import { mount, type VueWrapper } from "@vue/test-utils";
import TermsOfServicePage from "~/pages/legal/terms-of-service.vue";
describe("pages/legal/terms-of-service.vue", () => {
it("loads without crashing", () => {
const wrapper: VueWrapper = mount(TermsOfServicePage);
expect(wrapper.exists()).toBe(true);
expect(wrapper.text()).toContain("Terms of Service");
});
});

View file

@ -12,6 +12,7 @@
"@/*": ["./app/*"],
"#shared": ["./shared"],
"#shared/*": ["./shared/*"],
"#app": ["./node_modules/nuxt/dist/app"],
}
},
"include": ["./tests/**/*"]