2026-05-18 15:21:01 +00:00
|
|
|
<style scoped>
|
|
|
|
|
.user-filters {
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: 1rem;
|
|
|
|
|
align-items: center;
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.user-filters input[type="text"],
|
|
|
|
|
.user-filters select {
|
|
|
|
|
padding: 0.5rem;
|
|
|
|
|
border: 1px solid #ddd;
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.user-filters button {
|
|
|
|
|
padding: 0.5rem 1rem;
|
|
|
|
|
background: #dc3545;
|
|
|
|
|
color: white;
|
|
|
|
|
border: none;
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.user-footer {
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
color: #666;
|
|
|
|
|
}
|
|
|
|
|
</style>
|
|
|
|
|
|
2026-05-15 17:19:06 +00:00
|
|
|
<template>
|
2026-05-18 15:40:02 +00:00
|
|
|
<ListCard
|
|
|
|
|
:items="items"
|
|
|
|
|
:loading="loading"
|
|
|
|
|
selection-mode="single"
|
|
|
|
|
@selection-change="handleSelectionChange"
|
|
|
|
|
>
|
2026-05-18 15:21:01 +00:00
|
|
|
<!-- Filters Slot -->
|
|
|
|
|
<template #filters="{ applyFilter, clearFilters, activeFilters }">
|
|
|
|
|
<div class="user-filters">
|
|
|
|
|
<input
|
|
|
|
|
v-model="searchTerm"
|
|
|
|
|
type="text"
|
|
|
|
|
placeholder="Search by name..."
|
|
|
|
|
@input="handleSearch(applyFilter)"
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
<button
|
|
|
|
|
@click="handleClearFilters(clearFilters)"
|
|
|
|
|
v-if="activeFilters > 0"
|
|
|
|
|
>
|
|
|
|
|
Clear Filters ({{ activeFilters }})
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<!-- Item Slot -->
|
2026-05-18 15:40:02 +00:00
|
|
|
<template #item="{ item, isSelected, toggleSelection }">
|
2026-05-18 15:21:01 +00:00
|
|
|
<ListCardContact
|
|
|
|
|
:contact="item"
|
2026-05-18 15:40:02 +00:00
|
|
|
@click="toggleSelection"
|
|
|
|
|
:is-selected="isSelected"
|
2026-05-18 15:21:01 +00:00
|
|
|
/>
|
|
|
|
|
</template>
|
|
|
|
|
</ListCard>
|
2026-05-15 17:19:06 +00:00
|
|
|
</template>
|
2026-05-18 15:21:01 +00:00
|
|
|
<script setup lang="ts">
|
|
|
|
|
import { computed, ref } from "vue";
|
|
|
|
|
|
|
|
|
|
import ListCard from "@/components/layout/ListCard.vue";
|
|
|
|
|
import ListCardContact from "@/components/ListCardContact.vue";
|
|
|
|
|
import { Contact } from "@/type/api";
|
|
|
|
|
|
|
|
|
|
interface Props {
|
|
|
|
|
contacts: Contact[] | undefined;
|
|
|
|
|
loading: boolean;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const loading = ref(false);
|
|
|
|
|
const props = defineProps<Props>();
|
|
|
|
|
const searchTerm = ref("");
|
|
|
|
|
const roleFilter = ref("");
|
|
|
|
|
const activeOnly = ref(false);
|
|
|
|
|
|
|
|
|
|
// Filter handlers
|
|
|
|
|
const handleSearch = (
|
|
|
|
|
applyFilter: (fn: (contact: Contact) => boolean) => void,
|
|
|
|
|
) => {
|
|
|
|
|
applyFilter((contact: Contact) =>
|
|
|
|
|
contact.name.toLowerCase().includes(searchTerm.value.toLowerCase()),
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleClearFilters = (clearFilters: () => void) => {
|
|
|
|
|
searchTerm.value = "";
|
|
|
|
|
roleFilter.value = "";
|
|
|
|
|
activeOnly.value = false;
|
|
|
|
|
clearFilters();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleContactClick = (contact: any) => {
|
|
|
|
|
console.log("Clicked contact:", contact);
|
|
|
|
|
};
|
2026-05-18 15:40:02 +00:00
|
|
|
const handleSelectionChange = (selectedContacts: Contact[]) => {
|
|
|
|
|
console.log("Selected contacts:", selectedContacts);
|
|
|
|
|
// Do something with the selected contacts
|
|
|
|
|
};
|
2026-05-18 15:21:01 +00:00
|
|
|
|
|
|
|
|
const items = computed((): Contact[] => {
|
|
|
|
|
if (props.contacts) {
|
|
|
|
|
return props.contacts;
|
|
|
|
|
}
|
|
|
|
|
return [];
|
|
|
|
|
});
|
|
|
|
|
</script>
|