PostsSection.vue 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566
  1. <template>
  2. <div class="space-y-4">
  3. <div v-if="filteredPosts.length === 0" class="flex flex-col items-center justify-center py-20 bg-card/40 border border-border/50 rounded-3xl opacity-50">
  4. <Newspaper class="w-12 h-12 text-muted-foreground/20 mb-4" />
  5. <p class="text-sm text-muted-foreground">{{ t("admin.fields.noPosts") }}</p>
  6. </div>
  7. <div v-else class="grid gap-4">
  8. <div v-for="p in filteredPosts" :key="p.id"
  9. class="p-4 bg-card/40 border border-border/50 rounded-2xl flex items-center justify-between group hover:border-primary/30 transition-all">
  10. <div class="flex items-center gap-4 min-w-0">
  11. <div class="w-16 h-16 rounded-xl bg-muted/20 overflow-hidden flex-shrink-0 border border-border/50">
  12. <img v-if="p.image_url" :src="p.image_url" class="w-full h-full object-cover" />
  13. <Newspaper v-else class="w-full h-full p-4 text-muted-foreground/30" />
  14. </div>
  15. <div class="min-w-0">
  16. <h4 class="font-bold truncate text-sm">{{ p.title_en }}</h4>
  17. <div class="flex items-center gap-3 text-[10px] text-muted-foreground uppercase font-bold mt-1">
  18. <span class="bg-muted px-1.5 py-0.5 rounded border border-border/50">{{ p.category }}</span>
  19. <span class="opacity-30">•</span>
  20. <span class="font-mono">{{ p.slug }}</span>
  21. </div>
  22. </div>
  23. </div>
  24. <div class="flex items-center gap-2">
  25. <button @click="$emit('edit', p)" class="p-2.5 rounded-xl hover:bg-white/5 text-muted-foreground hover:text-primary transition-all">
  26. <Edit2 class="w-4 h-4" />
  27. </button>
  28. <button @click="$emit('toggle-publish', p)" :class="`p-2.5 rounded-xl transition-all ${p.is_published ? 'text-emerald-500 hover:bg-emerald-500/10' : 'text-rose-500 hover:bg-rose-500/10'}`">
  29. <Eye v-if="p.is_published" class="w-5 h-5" />
  30. <EyeOff v-else class="w-5 h-5" />
  31. </button>
  32. <button @click="$emit('delete', p.id, p.title_en)" class="p-2.5 rounded-xl hover:bg-rose-500/10 text-muted-foreground hover:text-rose-500 transition-all">
  33. <Trash2 class="w-4 h-4" />
  34. </button>
  35. </div>
  36. </div>
  37. </div>
  38. </div>
  39. </template>
  40. <script setup lang="ts">
  41. import { computed } from "vue";
  42. import { useI18n } from "vue-i18n";
  43. import { Newspaper, Edit2, Trash2, Eye, EyeOff } from "lucide-vue-next";
  44. import Button from "@/components/ui/button.vue";
  45. const { t } = useI18n();
  46. const props = defineProps<{
  47. posts: any[];
  48. searchQuery: string;
  49. }>();
  50. defineEmits(['edit', 'delete', 'toggle-publish']);
  51. const filteredPosts = computed(() => {
  52. if (!props.searchQuery) return props.posts;
  53. const q = props.searchQuery.toLowerCase();
  54. return props.posts.filter(p =>
  55. p.title_en?.toLowerCase().includes(q) ||
  56. p.title_ru?.toLowerCase().includes(q) ||
  57. p.slug?.toLowerCase().includes(q)
  58. );
  59. });
  60. </script>