Browse Source

SEO: Fix canonical tag duplication, move to dynamic useHead management, and expand prerender routes

unknown 2 months ago
parent
commit
bb7c3254c9

+ 1 - 17
index.html

@@ -7,23 +7,7 @@
     
     <link rel="stylesheet" href="/fonts/fonts.css">
     
-    <title>Radionica 3D | Professional 3D Printing in Montenegro</title>
-    <link rel="canonical" href="https://radionica3d.me/" />
-    <meta name="description" content="Professional 3D printing and rapid prototyping services in Montenegro. Instant quotes, industrial materials, and high-precision results." />
-    
-    <!-- Open Graph / Facebook -->
-    <meta property="og:type" content="website" />
-    <meta property="og:url" content="https://radionica3d.me/" />
-    <meta property="og:title" content="Radionica 3D | Professional 3D Printing in Montenegro" />
-    <meta property="og:description" content="Instant 3D printing quotes and high-quality prototyping in Herceg Novi, Montenegro." />
-    <meta property="og:image" content="https://radionica3d.me/og-image.jpg" />
-
-    <!-- Twitter -->
-    <meta property="twitter:card" content="summary_large_image" />
-    <meta property="twitter:url" content="https://radionica3d.me/" />
-    <meta property="twitter:title" content="Radionica 3D | Professional 3D Printing in Montenegro" />
-    <meta property="twitter:description" content="Instant 3D printing quotes and high-quality prototyping in Montenegro." />
-    <meta property="twitter:image" content="https://radionica3d.me/og-image.jpg" />
+    <title>Radionica 3D</title>
   </head>
   <body>
     <div id="root"></div>

+ 15 - 0
src/App.vue

@@ -16,12 +16,21 @@ import { useAuthStore } from "@/stores/auth";
 import { defineAsyncComponent, computed } from "vue";
 import { useHead } from "@unhead/vue";
 import { useI18n } from "vue-i18n";
+import { useRoute } from "vue-router";
 
 const CompleteProfileModal = defineAsyncComponent(() => import("@/components/CompleteProfileModal.vue"));
 const CookieBanner = defineAsyncComponent(() => import("@/components/CookieBanner.vue"));
 
 const { t, locale } = useI18n();
 const authStore = useAuthStore();
+const route = useRoute();
+
+const canonicalUrl = computed(() => {
+  const domain = 'https://radionica3d.me';
+  // Remove trailing slash except for root
+  const path = route.path === '/' ? '/' : route.path.replace(/\/$/, '');
+  return `${domain}${path}`;
+});
 
 // Skip auth initialization and unnecessary pings during prerendering
 const isPrerendering = (window as any).__PRERENDER_INJECTED !== undefined;
@@ -35,11 +44,17 @@ useHead({
   htmlAttrs: {
     lang: computed(() => locale.value)
   },
+  link: [
+    { rel: 'canonical', href: canonicalUrl }
+  ],
   meta: [
+    { name: 'description', content: computed(() => t('hero.description')) },
     { property: 'og:type', content: 'website' },
     { property: 'og:site_name', content: 'Radionica 3D' },
+    { property: 'og:url', content: canonicalUrl },
     { property: 'og:image', content: 'https://radionica3d.me/og-image.jpg' },
     { name: 'twitter:card', content: 'summary_large_image' },
+    { name: 'twitter:url', content: canonicalUrl },
   ]
 });
 </script>

+ 1 - 1
src/pages/About.vue

@@ -103,7 +103,7 @@ import Footer from "@/components/Footer.vue";
 const { t } = useI18n();
 
 useHead({
-  title: computed(() => `${t('footer.about')} | Radionica 3D`),
+  title: computed(() => t('footer.about')),
   meta: [
     { name: 'description', content: computed(() => t('about.subtitle')) }
   ]

+ 8 - 0
src/pages/Blog.vue

@@ -128,8 +128,16 @@ import Header from "@/components/Header.vue";
 import Footer from "@/components/Footer.vue";
 import Button from "@/components/ui/button.vue";
 import { getBlogPosts } from "@/lib/api";
+import { useHead } from "@unhead/vue";
 
 const { t, locale } = useI18n();
+
+useHead({
+  title: computed(() => t('footer.blog')),
+  meta: [
+    { name: 'description', content: computed(() => t('blog.subtitle')) }
+  ]
+});
 const posts = ref<any[]>([]);
 const isLoading = ref(true);
 

+ 213 - 204
src/pages/Careers.vue

@@ -1,204 +1,213 @@
-<template>
-  <div class="min-h-screen bg-background flex flex-col">
-    <Header />
-    <main class="flex-grow pt-24 pb-20">
-    <div class="container mx-auto px-4 py-12">
-      <div class="max-w-4xl mx-auto">
-        <!-- Header -->
-        <div class="mb-12">
-          <h1 class="text-4xl md:text-5xl font-display font-bold text-foreground mb-4">
-            {{ t("footer.careers") }}
-          </h1>
-          <p class="text-lg text-foreground/60">
-            {{ t("careers.subtitle") }}
-          </p>
-        </div>
-
-        <!-- Content -->
-        <div class="space-y-8">
-          <!-- Why Work With Us -->
-          <section>
-            <h2 class="text-2xl font-display font-bold text-foreground mb-6">
-              {{ t("careers.whyWork.title") }}
-            </h2>
-            <div class="grid md:grid-cols-2 gap-6">
-              <div class="bg-gray-50 rounded-xl p-6">
-                <h3 class="text-xl font-display font-bold text-foreground mb-3">
-                  {{ t("careers.whyWork.innovation.title") }}
-                </h3>
-                <p class="text-foreground/70">
-                  {{ t("careers.whyWork.innovation.content") }}
-                </p>
-              </div>
-              <div class="bg-gray-50 rounded-xl p-6">
-                <h3 class="text-xl font-display font-bold text-foreground mb-3">
-                  {{ t("careers.whyWork.growth.title") }}
-                </h3>
-                <p class="text-foreground/70">
-                  {{ t("careers.whyWork.growth.content") }}
-                </p>
-              </div>
-              <div class="bg-gray-50 rounded-xl p-6">
-                <h3 class="text-xl font-display font-bold text-foreground mb-3">
-                  {{ t("careers.whyWork.team.title") }}
-                </h3>
-                <p class="text-foreground/70">
-                  {{ t("careers.whyWork.team.content") }}
-                </p>
-              </div>
-              <div class="bg-gray-50 rounded-xl p-6">
-                <h3 class="text-xl font-display font-bold text-foreground mb-3">
-                  {{ t("careers.whyWork.impact.title") }}
-                </h3>
-                <p class="text-foreground/70">
-                  {{ t("careers.whyWork.impact.content") }}
-                </p>
-              </div>
-            </div>
-          </section>
-
-          <!-- Open Positions -->
-          <section>
-            <h2 class="text-2xl font-display font-bold text-foreground mb-6">
-              {{ t("careers.openPositions.title") }}
-            </h2>
-            <div class="space-y-4">
-              <div class="border border-gray-200 rounded-xl p-6 hover:border-primary/30 transition-colors">
-                <div class="flex flex-col md:flex-row md:items-center justify-between gap-4">
-                  <div>
-                    <h3 class="text-xl font-display font-bold text-foreground mb-2">
-                      {{ t("careers.openPositions.position1.title") }}
-                    </h3>
-                    <div class="flex flex-wrap gap-2 mb-3">
-                      <span class="px-3 py-1 bg-gray-100 text-foreground/70 text-xs font-bold rounded-full">
-                        {{ t("careers.openPositions.position1.type") }}
-                      </span>
-                      <span class="px-3 py-1 bg-gray-100 text-foreground/70 text-xs font-bold rounded-full">
-                        {{ t("careers.openPositions.position1.location") }}
-                      </span>
-                    </div>
-                    <p class="text-foreground/70">
-                      {{ t("careers.openPositions.position1.description") }}
-                    </p>
-                  </div>
-                  <router-link 
-                    to="/contact?subject=3D%20Printing%20Technician%20Application"
-                    class="inline-flex items-center justify-center px-6 py-3 bg-primary text-white font-bold rounded-lg hover:bg-primary/90 transition-colors whitespace-nowrap"
-                  >
-                    {{ t("careers.apply") }}
-                  </router-link>
-                </div>
-              </div>
-
-              <div class="border border-gray-200 rounded-xl p-6 hover:border-primary/30 transition-colors">
-                <div class="flex flex-col md:flex-row md:items-center justify-between gap-4">
-                  <div>
-                    <h3 class="text-xl font-display font-bold text-foreground mb-2">
-                      {{ t("careers.openPositions.position2.title") }}
-                    </h3>
-                    <div class="flex flex-wrap gap-2 mb-3">
-                      <span class="px-3 py-1 bg-gray-100 text-foreground/70 text-xs font-bold rounded-full">
-                        {{ t("careers.openPositions.position2.type") }}
-                      </span>
-                      <span class="px-3 py-1 bg-gray-100 text-foreground/70 text-xs font-bold rounded-full">
-                        {{ t("careers.openPositions.position2.location") }}
-                      </span>
-                    </div>
-                    <p class="text-foreground/70">
-                      {{ t("careers.openPositions.position2.description") }}
-                    </p>
-                  </div>
-                  <router-link 
-                    to="/contact?subject=Customer%20Support%20Specialist%20Application"
-                    class="inline-flex items-center justify-center px-6 py-3 bg-primary text-white font-bold rounded-lg hover:bg-primary/90 transition-colors whitespace-nowrap"
-                  >
-                    {{ t("careers.apply") }}
-                  </router-link>
-                </div>
-              </div>
-            </div>
-          </section>
-
-          <!-- Application Process -->
-          <section>
-            <h2 class="text-2xl font-display font-bold text-foreground mb-6">
-              {{ t("careers.process.title") }}
-            </h2>
-            <div class="grid md:grid-cols-4 gap-6">
-              <div class="text-center">
-                <div class="w-12 h-12 bg-primary/10 text-primary rounded-full flex items-center justify-center text-xl font-bold mx-auto mb-4">
-                  1
-                </div>
-                <h3 class="font-display font-bold text-foreground mb-2">
-                  {{ t("careers.process.step1.title") }}
-                </h3>
-                <p class="text-sm text-foreground/70">
-                  {{ t("careers.process.step1.description") }}
-                </p>
-              </div>
-              <div class="text-center">
-                <div class="w-12 h-12 bg-primary/10 text-primary rounded-full flex items-center justify-center text-xl font-bold mx-auto mb-4">
-                  2
-                </div>
-                <h3 class="font-display font-bold text-foreground mb-2">
-                  {{ t("careers.process.step2.title") }}
-                </h3>
-                <p class="text-sm text-foreground/70">
-                  {{ t("careers.process.step2.description") }}
-                </p>
-              </div>
-              <div class="text-center">
-                <div class="w-12 h-12 bg-primary/10 text-primary rounded-full flex items-center justify-center text-xl font-bold mx-auto mb-4">
-                  3
-                </div>
-                <h3 class="font-display font-bold text-foreground mb-2">
-                  {{ t("careers.process.step3.title") }}
-                </h3>
-                <p class="text-sm text-foreground/70">
-                  {{ t("careers.process.step3.description") }}
-                </p>
-              </div>
-              <div class="text-center">
-                <div class="w-12 h-12 bg-primary/10 text-primary rounded-full flex items-center justify-center text-xl font-bold mx-auto mb-4">
-                  4
-                </div>
-                <h3 class="font-display font-bold text-foreground mb-2">
-                  {{ t("careers.process.step4.title") }}
-                </h3>
-                <p class="text-sm text-foreground/70">
-                  {{ t("careers.process.step4.description") }}
-                </p>
-              </div>
-            </div>
-          </section>
-
-          <!-- Contact CTA -->
-          <section class="bg-primary/5 rounded-2xl p-8 text-center">
-            <h2 class="text-2xl font-display font-bold text-foreground mb-4">
-              {{ t("careers.cta.title") }}
-            </h2>
-            <p class="text-foreground/70 mb-6 max-w-2xl mx-auto">
-              {{ t("careers.cta.content") }}
-            </p>
-            <router-link 
-              to="/contact?subject=General%20Career%20Inquiry"
-              class="inline-flex items-center justify-center px-6 py-3 bg-primary text-white font-bold rounded-lg hover:bg-primary/90 transition-colors"
-            >
-              {{ t("careers.contact") }}
-            </router-link>
-          </section>
-        </div>
-      </div>
-    </div>
-  </main>
-  <Footer />
-</div>
-</template>
-
-<script setup lang="ts">
-import { useI18n } from "vue-i18n";
-import Header from "@/components/Header.vue";
-import Footer from "@/components/Footer.vue";
-
-const { t } = useI18n();
-</script>
+<template>
+  <div class="min-h-screen bg-background flex flex-col">
+    <Header />
+    <main class="flex-grow pt-24 pb-20">
+    <div class="container mx-auto px-4 py-12">
+      <div class="max-w-4xl mx-auto">
+        <!-- Header -->
+        <div class="mb-12">
+          <h1 class="text-4xl md:text-5xl font-display font-bold text-foreground mb-4">
+            {{ t("footer.careers") }}
+          </h1>
+          <p class="text-lg text-foreground/60">
+            {{ t("careers.subtitle") }}
+          </p>
+        </div>
+
+        <!-- Content -->
+        <div class="space-y-8">
+          <!-- Why Work With Us -->
+          <section>
+            <h2 class="text-2xl font-display font-bold text-foreground mb-6">
+              {{ t("careers.whyWork.title") }}
+            </h2>
+            <div class="grid md:grid-cols-2 gap-6">
+              <div class="bg-gray-50 rounded-xl p-6">
+                <h3 class="text-xl font-display font-bold text-foreground mb-3">
+                  {{ t("careers.whyWork.innovation.title") }}
+                </h3>
+                <p class="text-foreground/70">
+                  {{ t("careers.whyWork.innovation.content") }}
+                </p>
+              </div>
+              <div class="bg-gray-50 rounded-xl p-6">
+                <h3 class="text-xl font-display font-bold text-foreground mb-3">
+                  {{ t("careers.whyWork.growth.title") }}
+                </h3>
+                <p class="text-foreground/70">
+                  {{ t("careers.whyWork.growth.content") }}
+                </p>
+              </div>
+              <div class="bg-gray-50 rounded-xl p-6">
+                <h3 class="text-xl font-display font-bold text-foreground mb-3">
+                  {{ t("careers.whyWork.team.title") }}
+                </h3>
+                <p class="text-foreground/70">
+                  {{ t("careers.whyWork.team.content") }}
+                </p>
+              </div>
+              <div class="bg-gray-50 rounded-xl p-6">
+                <h3 class="text-xl font-display font-bold text-foreground mb-3">
+                  {{ t("careers.whyWork.impact.title") }}
+                </h3>
+                <p class="text-foreground/70">
+                  {{ t("careers.whyWork.impact.content") }}
+                </p>
+              </div>
+            </div>
+          </section>
+
+          <!-- Open Positions -->
+          <section>
+            <h2 class="text-2xl font-display font-bold text-foreground mb-6">
+              {{ t("careers.openPositions.title") }}
+            </h2>
+            <div class="space-y-4">
+              <div class="border border-gray-200 rounded-xl p-6 hover:border-primary/30 transition-colors">
+                <div class="flex flex-col md:flex-row md:items-center justify-between gap-4">
+                  <div>
+                    <h3 class="text-xl font-display font-bold text-foreground mb-2">
+                      {{ t("careers.openPositions.position1.title") }}
+                    </h3>
+                    <div class="flex flex-wrap gap-2 mb-3">
+                      <span class="px-3 py-1 bg-gray-100 text-foreground/70 text-xs font-bold rounded-full">
+                        {{ t("careers.openPositions.position1.type") }}
+                      </span>
+                      <span class="px-3 py-1 bg-gray-100 text-foreground/70 text-xs font-bold rounded-full">
+                        {{ t("careers.openPositions.position1.location") }}
+                      </span>
+                    </div>
+                    <p class="text-foreground/70">
+                      {{ t("careers.openPositions.position1.description") }}
+                    </p>
+                  </div>
+                  <router-link 
+                    to="/contact?subject=3D%20Printing%20Technician%20Application"
+                    class="inline-flex items-center justify-center px-6 py-3 bg-primary text-white font-bold rounded-lg hover:bg-primary/90 transition-colors whitespace-nowrap"
+                  >
+                    {{ t("careers.apply") }}
+                  </router-link>
+                </div>
+              </div>
+
+              <div class="border border-gray-200 rounded-xl p-6 hover:border-primary/30 transition-colors">
+                <div class="flex flex-col md:flex-row md:items-center justify-between gap-4">
+                  <div>
+                    <h3 class="text-xl font-display font-bold text-foreground mb-2">
+                      {{ t("careers.openPositions.position2.title") }}
+                    </h3>
+                    <div class="flex flex-wrap gap-2 mb-3">
+                      <span class="px-3 py-1 bg-gray-100 text-foreground/70 text-xs font-bold rounded-full">
+                        {{ t("careers.openPositions.position2.type") }}
+                      </span>
+                      <span class="px-3 py-1 bg-gray-100 text-foreground/70 text-xs font-bold rounded-full">
+                        {{ t("careers.openPositions.position2.location") }}
+                      </span>
+                    </div>
+                    <p class="text-foreground/70">
+                      {{ t("careers.openPositions.position2.description") }}
+                    </p>
+                  </div>
+                  <router-link 
+                    to="/contact?subject=Customer%20Support%20Specialist%20Application"
+                    class="inline-flex items-center justify-center px-6 py-3 bg-primary text-white font-bold rounded-lg hover:bg-primary/90 transition-colors whitespace-nowrap"
+                  >
+                    {{ t("careers.apply") }}
+                  </router-link>
+                </div>
+              </div>
+            </div>
+          </section>
+
+          <!-- Application Process -->
+          <section>
+            <h2 class="text-2xl font-display font-bold text-foreground mb-6">
+              {{ t("careers.process.title") }}
+            </h2>
+            <div class="grid md:grid-cols-4 gap-6">
+              <div class="text-center">
+                <div class="w-12 h-12 bg-primary/10 text-primary rounded-full flex items-center justify-center text-xl font-bold mx-auto mb-4">
+                  1
+                </div>
+                <h3 class="font-display font-bold text-foreground mb-2">
+                  {{ t("careers.process.step1.title") }}
+                </h3>
+                <p class="text-sm text-foreground/70">
+                  {{ t("careers.process.step1.description") }}
+                </p>
+              </div>
+              <div class="text-center">
+                <div class="w-12 h-12 bg-primary/10 text-primary rounded-full flex items-center justify-center text-xl font-bold mx-auto mb-4">
+                  2
+                </div>
+                <h3 class="font-display font-bold text-foreground mb-2">
+                  {{ t("careers.process.step2.title") }}
+                </h3>
+                <p class="text-sm text-foreground/70">
+                  {{ t("careers.process.step2.description") }}
+                </p>
+              </div>
+              <div class="text-center">
+                <div class="w-12 h-12 bg-primary/10 text-primary rounded-full flex items-center justify-center text-xl font-bold mx-auto mb-4">
+                  3
+                </div>
+                <h3 class="font-display font-bold text-foreground mb-2">
+                  {{ t("careers.process.step3.title") }}
+                </h3>
+                <p class="text-sm text-foreground/70">
+                  {{ t("careers.process.step3.description") }}
+                </p>
+              </div>
+              <div class="text-center">
+                <div class="w-12 h-12 bg-primary/10 text-primary rounded-full flex items-center justify-center text-xl font-bold mx-auto mb-4">
+                  4
+                </div>
+                <h3 class="font-display font-bold text-foreground mb-2">
+                  {{ t("careers.process.step4.title") }}
+                </h3>
+                <p class="text-sm text-foreground/70">
+                  {{ t("careers.process.step4.description") }}
+                </p>
+              </div>
+            </div>
+          </section>
+
+          <!-- Contact CTA -->
+          <section class="bg-primary/5 rounded-2xl p-8 text-center">
+            <h2 class="text-2xl font-display font-bold text-foreground mb-4">
+              {{ t("careers.cta.title") }}
+            </h2>
+            <p class="text-foreground/70 mb-6 max-w-2xl mx-auto">
+              {{ t("careers.cta.content") }}
+            </p>
+            <router-link 
+              to="/contact?subject=General%20Career%20Inquiry"
+              class="inline-flex items-center justify-center px-6 py-3 bg-primary text-white font-bold rounded-lg hover:bg-primary/90 transition-colors"
+            >
+              {{ t("careers.contact") }}
+            </router-link>
+          </section>
+        </div>
+      </div>
+    </div>
+  </main>
+  <Footer />
+</div>
+</template>
+
+<script setup lang="ts">
+import { useI18n } from "vue-i18n";
+import { useHead } from "@unhead/vue";
+import { computed } from "vue";
+import Header from "@/components/Header.vue";
+import Footer from "@/components/Footer.vue";
+
+const { t } = useI18n();
+
+useHead({
+  title: computed(() => t('footer.careers')),
+  meta: [
+    { name: 'description', content: computed(() => t('careers.subtitle')) }
+  ]
+});
+</script>

+ 1 - 1
src/pages/Contact.vue

@@ -243,7 +243,7 @@ const { t } = useI18n();
 const fileInputRef = ref<HTMLInputElement | null>(null);
 
 useHead({
-  title: computed(() => `${t('footer.contact')} | Radionica 3D`),
+  title: computed(() => t('footer.contact')),
   meta: [
     { name: 'description', content: computed(() => t('contact.subtitle')) }
   ]

+ 9 - 0
src/pages/Guidelines.vue

@@ -204,6 +204,8 @@
 
 <script setup lang="ts">
 import { useI18n } from "vue-i18n";
+import { useHead } from "@unhead/vue";
+import { computed } from "vue";
 import Header from "@/components/Header.vue";
 import Footer from "@/components/Footer.vue";
 import { 
@@ -222,6 +224,13 @@ import {
 
 const { t } = useI18n();
 
+useHead({
+  title: computed(() => t('guidelines.title')),
+  meta: [
+    { name: 'description', content: computed(() => t('nuances.subtitle')) }
+  ]
+});
+
 const nuances = {
   layerStructure: {
     image: "/fdm_layer_lines_photo_1776031229473.webp",

+ 8 - 0
src/pages/HelpCenter.vue

@@ -180,9 +180,17 @@ import { ref, computed } from "vue";
 import { useI18n } from "vue-i18n";
 import Header from "@/components/Header.vue";
 import Footer from "@/components/Footer.vue";
+import { useHead } from "@unhead/vue";
 
 const { t } = useI18n();
 
+useHead({
+  title: computed(() => t('footer.help')),
+  meta: [
+    { name: 'description', content: computed(() => t('help.subtitle')) }
+  ]
+});
+
 const searchQuery = ref("");
 const openFaqs = ref<boolean[]>([]);
 

+ 11 - 0
src/pages/NotFound.vue

@@ -11,7 +11,18 @@
 <script setup lang="ts">
 import { useRoute } from "vue-router";
 import { useI18n } from "vue-i18n";
+import { useHead } from "@unhead/vue";
+import { computed } from "vue";
+
 const route = useRoute();
 const { t } = useI18n();
+
+useHead({
+  title: computed(() => t('errors.404.title')),
+  meta: [
+    { name: 'robots', content: 'noindex, nofollow' }
+  ]
+});
+
 console.error("404 Error: User attempted to access non-existent route:", route.path);
 </script>

+ 9 - 1
src/pages/Orders.vue

@@ -218,7 +218,7 @@
 
 <script setup lang="ts">
 // Orders page - user order history, status tracking and chat
-import { ref, onMounted, defineComponent, h, watch, nextTick } from "vue";
+import { ref, onMounted, defineComponent, h, watch, nextTick, computed } from "vue";
 import { RouterLink, useRouter, useRoute } from "vue-router";
 import { useI18n } from "vue-i18n";
 import { Package, Clock, ShieldCheck, Truck, XCircle, ArrowLeft, Loader2, ExternalLink, Hash, FileText, Image as ImageIcon, MessageCircle, FileBox, Download, Plus, X } from "lucide-vue-next";
@@ -232,8 +232,16 @@ import ModelUploadSection from "@/components/ModelUploadSection.vue";
 import { getMyOrders, API_BASE_URL, RESOURCES_BASE_URL } from "@/lib/api";
 import { useAuthStore } from "@/stores/auth";
 import { toast } from "vue-sonner";
+import { useHead } from "@unhead/vue";
 
 const { t } = useI18n();
+
+useHead({
+  title: computed(() => t('nav.myOrders')),
+  meta: [
+    { name: 'robots', content: 'noindex, nofollow' }
+  ]
+});
 const router = useRouter();
 const route = useRoute();
 const authStore = useAuthStore();

+ 9 - 0
src/pages/Privacy.vue

@@ -65,12 +65,21 @@
 
 <script setup lang="ts">
 import { useI18n } from "vue-i18n";
+import { useHead } from "@unhead/vue";
+import { computed } from "vue";
 import { Mail, ArrowUpRight } from "lucide-vue-next";
 import Header from "@/components/Header.vue";
 import Footer from "@/components/Footer.vue";
 
 const { t } = useI18n();
 
+useHead({
+  title: computed(() => t('privacy.title')),
+  meta: [
+    { name: 'description', content: computed(() => t('privacy.subtitle')) }
+  ]
+});
+
 const privacySections = [
   "01_data", "02_usage", "03_basis", "04_uploads", "05_security", 
   "06_sharing", "07_rights", "08_cookies", "09_international", 

+ 9 - 0
src/pages/Terms.vue

@@ -268,8 +268,17 @@
 
 <script setup lang="ts">
 import { useI18n } from "vue-i18n";
+import { useHead } from "@unhead/vue";
+import { computed } from "vue";
 import Header from "@/components/Header.vue";
 import Footer from "@/components/Footer.vue";
 
 const { t } = useI18n();
+
+useHead({
+  title: computed(() => t('footer.terms')),
+  meta: [
+    { name: 'description', content: computed(() => t('terms.subtitle')) }
+  ]
+});
 </script>

+ 0 - 5
src/router/index.ts

@@ -94,10 +94,5 @@ router.beforeEach(async (to) => {
   }
 });
 
-router.afterEach((to) => {
-  const baseTitle = "Radionica 3D";
-  const pageTitle = to.meta.title ? `${to.meta.title} | ${baseTitle}` : baseTitle;
-  document.title = pageTitle;
-});
 
 export default router;

+ 13 - 1
vite.config.ts

@@ -14,7 +14,19 @@ export default defineConfig(({ mode }) => {
       ...(mode === 'production' ? [
         prerender({
           staticDir: path.join(__dirname, outDir),
-          routes: ['/', '/en/', '/me/', '/ru/', '/ua/'],
+          routes: [
+            '/', '/en/', '/me/', '/ru/', '/ua/',
+            '/en/portfolio', '/me/portfolio', '/ru/portfolio', '/ua/portfolio',
+            '/en/about', '/me/about', '/ru/about', '/ua/about',
+            '/en/blog', '/me/blog', '/ru/blog', '/ua/blog',
+            '/en/contact', '/me/contact', '/ru/contact', '/ua/contact',
+            '/en/help', '/me/help', '/ru/help', '/ua/help',
+            '/en/guidelines', '/me/guidelines', '/ru/guidelines', '/ua/guidelines',
+            '/en/nuances', '/me/nuances', '/ru/nuances', '/ua/nuances',
+            '/en/privacy', '/me/privacy', '/ru/privacy', '/ua/privacy',
+            '/en/terms', '/me/terms', '/ru/terms', '/ua/terms',
+            '/en/careers', '/me/careers', '/ru/careers', '/ua/careers',
+          ],
           renderer: new prerender.PuppeteerRenderer({
             renderAfterDocumentEvent: 'render-event',
             injectProperty: '__PRERENDER_INJECTED',