| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134 |
- <template>
- <div class="space-y-8">
- <!-- Filter bar -->
- <div class="flex flex-wrap items-center gap-4 bg-card/30 p-4 rounded-2xl border border-border/50">
- <div class="flex items-center gap-2">
- <Filter class="w-4 h-4 text-muted-foreground" />
- <span class="text-xs font-bold uppercase tracking-widest text-muted-foreground">{{ t("admin.filters") }}</span>
- </div>
-
- <select v-model="statusFilter" class="bg-background border border-border/50 rounded-xl px-4 py-2 text-xs min-w-[140px] focus:ring-2 ring-primary/20 outline-none">
- <option value="all">{{ t("admin.allStatuses") }}</option>
- <option v-for="s in statusOptions" :key="s" :value="s">{{ t("statuses." + s) }}</option>
- </select>
- <div class="flex items-center gap-2 bg-background border border-border/50 rounded-xl px-3 py-1.5">
- <span class="text-[10px] font-bold text-muted-foreground uppercase mr-1">{{ t("admin.from") }}</span>
- <input type="date" v-model="dateFrom" class="bg-transparent border-none text-xs focus:ring-0 p-0" />
- </div>
- <div class="flex items-center gap-2 bg-background border border-border/50 rounded-xl px-3 py-1.5">
- <span class="text-[10px] font-bold text-muted-foreground uppercase mr-1">{{ t("admin.to") }}</span>
- <input type="date" v-model="dateTo" class="bg-transparent border-none text-xs focus:ring-0 p-0" />
- </div>
- <button @click="resetFilters" class="text-xs text-muted-foreground hover:text-primary transition-colors underline ml-auto">{{ t("admin.reset") }}</button>
- </div>
- <!-- Orders Grid -->
- <div v-if="filtered.length > 0" class="grid gap-6">
- <OrderCard
- v-for="order in filtered"
- :key="order.id"
- :order="order"
- :statusConfig="statusConfig"
- :resourcesBaseUrl="resourcesBaseUrl"
- :isFocused="focusedOrderId === order.id"
- :isAdminChatOpen="adminChatId === order.id"
- :notifyStatus="notifyStatusMap[order.id]"
- :fiscalData="fiscalFormMap[order.id]"
- @focus="id => focusedOrderId = id"
- @update-status="(id, s) => $emit('update-status', id, s)"
- @delete-order="id => $emit('delete-order', id)"
- @attach-file="(id, f) => $emit('attach-file', id, f)"
- @upload-photo="(id, f) => $emit('upload-photo', id, f)"
- @delete-file="(id, fid, fname) => $emit('delete-file', id, fid, fname)"
- @delete-photo="pid => $emit('delete-photo', pid)"
- @toggle-photo-public="(pid, pub) => $emit('toggle-photo-public', pid, pub)"
- @approve-review="id => $emit('approve-review', id)"
- @open-chat="id => $emit('open-chat', id)"
- @close-chat="$emit('close-chat')"
- @update-notify="(id, val) => $emit('update-notify', id, val)"
- @update-fiscal="(id, data) => $emit('update-fiscal', id, data)"
- @edit-order="o => $emit('edit-order', o)"
- />
- </div>
- <!-- No results -->
- <div v-else class="flex flex-col items-center justify-center py-20 bg-card/40 border border-border/50 rounded-3xl opacity-50">
- <Package class="w-12 h-12 text-muted-foreground/20 mb-4" />
- <p class="text-sm text-muted-foreground">{{ t("admin.noOrdersFound") }}</p>
- </div>
- </div>
- </template>
- <script setup lang="ts">
- import { ref, computed } from "vue";
- import { useI18n } from "vue-i18n";
- import { Search, Filter, RefreshCw, Plus, Package } from "lucide-vue-next";
- import Button from "@/components/ui/button.vue";
- import OrderCard from "./OrderCard.vue";
- const { t } = useI18n();
- const props = defineProps<{
- orders: any[];
- statusConfig: Record<string, any>;
- resourcesBaseUrl: string;
- adminChatId: any;
- notifyStatusMap: Record<number, boolean>;
- fiscalFormMap: Record<number, any>;
- searchQuery: string;
- }>();
- const emit = defineEmits([
- 'update-status', 'delete-order', 'attach-file', 'upload-photo',
- 'delete-file', 'delete-photo', 'toggle-photo-public',
- 'approve-review', 'open-chat', 'close-chat',
- 'update-notify', 'update-fiscal', 'edit-order'
- ]);
- const statusFilter = ref("all");
- const dateFrom = ref("");
- const dateTo = ref("");
- const focusedOrderId = ref<number | null>(null);
- const statusOptions = Object.keys(props.statusConfig);
- const filtered = computed(() => {
- let list = [...props.orders];
-
- if (statusFilter.value !== "all") {
- list = list.filter(o => o.status === statusFilter.value);
- }
-
- if (dateFrom.value) {
- const from = new Date(dateFrom.value);
- list = list.filter(o => new Date(o.created_at) >= from);
- }
-
- if (dateTo.value) {
- const to = new Date(dateTo.value);
- to.setHours(23, 59, 59);
- list = list.filter(o => new Date(o.created_at) <= to);
- }
- if (props.searchQuery) {
- const q = props.searchQuery.toLowerCase();
- list = list.filter(o =>
- o.id.toString().includes(q) ||
- o.first_name?.toLowerCase().includes(q) ||
- o.last_name?.toLowerCase().includes(q) ||
- o.email?.toLowerCase().includes(q)
- );
- }
- return list.sort((a, b) => b.id - a.id);
- });
- const resetFilters = () => {
- statusFilter.value = 'all';
- dateFrom.value = '';
- dateTo.value = '';
- };
- </script>
|