| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154 |
- <template>
- <div class="min-h-screen bg-white">
- <Header />
- <div class="container mx-auto px-4 py-32">
- <div class="max-w-6xl mx-auto">
- <!-- Header -->
- <div class="mb-12">
- <h1 class="text-4xl md:text-5xl font-display font-bold text-foreground mb-4">
- {{ t("footer.blog") }}
- </h1>
- <p class="text-lg text-foreground/60">
- {{ t("blog.subtitle") }}
- </p>
- </div>
- <!-- Loading State -->
- <div v-if="isLoading" class="flex flex-col items-center justify-center py-24 gap-4 opacity-50">
- <RefreshCw class="w-8 h-8 text-primary animate-spin mb-4" />
- <p class="font-bold tracking-widest uppercase text-[10px]">{{ t("blog.loading") }}</p>
- </div>
- <template v-else>
- <!-- Featured Post (First one) -->
- <section v-if="featuredPost" class="mb-12">
- <div class="bg-gray-50 rounded-2xl overflow-hidden border border-black/[0.03]">
- <div class="md:flex">
- <div class="md:w-1/2">
- <div class="h-64 md:h-full bg-muted/20 overflow-hidden">
- <img v-if="featuredPost.image_url" :src="featuredPost.image_url" class="w-full h-full object-cover" />
- </div>
- </div>
- <div class="md:w-1/2 p-8 md:p-12">
- <div class="flex items-center gap-2 mb-4">
- <span class="px-3 py-1 bg-primary text-white text-[10px] font-bold rounded-full uppercase tracking-widest">
- {{ t("blog.featured") }}
- </span>
- <span class="text-xs font-bold text-foreground/40 uppercase">
- {{ new Date(featuredPost.created_at).toLocaleDateString() }}
- </span>
- </div>
- <h2 class="text-2xl md:text-3xl font-display font-bold text-foreground mb-4">
- {{ getLoc(featuredPost, 'title') }}
- </h2>
- <p class="text-foreground/70 mb-6 line-clamp-3">
- {{ getLoc(featuredPost, 'excerpt') }}
- </p>
- <router-link
- :to="`/blog/${featuredPost.slug}`"
- class="inline-flex items-center text-primary font-bold hover:text-primary/80 transition-colors"
- >
- {{ t("blog.readMore") }}
- <ArrowRight class="w-4 h-4 ml-2" />
- </router-link>
- </div>
- </div>
- </div>
- </section>
- <!-- Blog Posts Grid -->
- <section v-if="remainingPosts.length > 0">
- <h2 class="text-xl font-display font-bold text-foreground mb-8 uppercase tracking-widest opacity-40">
- {{ t("blog.latestPosts") }}
- </h2>
- <div class="grid md:grid-cols-2 lg:grid-cols-3 gap-8">
- <article v-for="post in remainingPosts" :key="post.id" class="flex flex-col border border-black/[0.05] rounded-2xl overflow-hidden hover:border-primary/30 transition-all duration-300 group">
- <div class="h-48 bg-muted/20 overflow-hidden">
- <img v-if="post.image_url" :src="post.image_url" class="w-full h-full object-cover group-hover:scale-105 transition-transform duration-500" />
- </div>
- <div class="p-6 flex-1 flex flex-col">
- <div class="flex items-center justify-between mb-4">
- <span class="text-[10px] font-bold text-foreground/30 uppercase">
- {{ new Date(post.created_at).toLocaleDateString() }}
- </span>
- <span class="px-2 py-0.5 bg-gray-50 text-foreground/40 text-[9px] font-bold rounded-md border border-black/[0.03] uppercase tracking-wider">
- {{ post.category }}
- </span>
- </div>
- <h3 class="text-lg font-display font-bold text-foreground mb-3 line-clamp-2">
- {{ getLoc(post, 'title') }}
- </h3>
- <p class="text-foreground/60 text-sm mb-6 line-clamp-3 flex-1">
- {{ getLoc(post, 'excerpt') }}
- </p>
- <router-link
- :to="`/blog/${post.slug}`"
- class="inline-flex items-center text-primary font-bold hover:text-primary-foreground hover:bg-primary px-4 py-2 rounded-xl transition-all self-start text-xs border border-primary/20"
- >
- {{ t("blog.readMore") }}
- </router-link>
- </div>
- </article>
- </div>
- </section>
- </template>
- <!-- Newsletter CTA -->
- <section class="mt-20 bg-primary/[0.02] border border-primary/10 rounded-3xl p-8 md:p-12 text-center">
- <h2 class="text-2xl font-display font-bold text-foreground mb-4">
- {{ t("blog.newsletter.title") }}
- </h2>
- <p class="text-foreground/70 mb-8 max-w-xl mx-auto text-sm leading-relaxed">
- {{ t("blog.newsletter.content") }}
- </p>
- <div class="max-w-md mx-auto">
- <div class="flex flex-col sm:flex-row gap-2">
- <input
- type="email"
- :placeholder="t('blog.newsletter.placeholder')"
- class="flex-1 px-5 py-3 bg-white border border-black/[0.05] rounded-2xl focus:outline-none focus:ring-2 focus:ring-primary/20 transition-all text-sm"
- />
- <Button variant="hero" class="px-8 whitespace-nowrap">
- {{ t("blog.newsletter.subscribe") }}
- </Button>
- </div>
- </div>
- </section>
- </div>
- </div>
- <Footer />
- </div>
- </template>
- <script setup lang="ts">
- import { ref, onMounted, computed } from "vue";
- import { useI18n } from "vue-i18n";
- import { ArrowRight, RefreshCw } from "lucide-vue-next";
- import Header from "@/components/Header.vue";
- import Footer from "@/components/Footer.vue";
- import Button from "@/components/ui/button.vue";
- import { getBlogPosts } from "@/lib/api";
- const { t, locale } = useI18n();
- const posts = ref<any[]>([]);
- const isLoading = ref(true);
- const featuredPost = computed(() => posts.value[0]);
- const remainingPosts = computed(() => posts.value.slice(1));
- function getLoc(item: any, field: string) {
- const currentLocale = locale.value;
- // Try current locale, fallback to EN
- return item[`${field}_${currentLocale}`] || item[`${field}_en`] || "";
- }
- onMounted(async () => {
- try {
- posts.value = await getBlogPosts();
- } catch (e) {
- console.error("Failed to fetch posts", e);
- } finally {
- isLoading.value = false;
- }
- });
- </script>
|