Browse Source

Add client search feature with filtering functionality

pull/1978/head
mavrag 2 months ago
parent
commit
e79ae048b3
  1. 2
      src/app/components/Clients/List.vue
  2. 40
      src/app/components/Clients/Search.vue
  3. 2
      src/app/components/Panel/head/Boat.vue
  4. 1
      src/app/pages/index.vue
  5. 33
      src/app/stores/clients.ts
  6. 1
      src/i18n/locales/en.json

2
src/app/components/Clients/List.vue

@ -1,6 +1,6 @@
<template>
<div
v-for="client in clientsStore.clients"
v-for="client in clientsStore.filteredClients"
:key="client.id"
class="relative overflow-hidden border-b border-solid border-gray-100 last:border-b-0 dark:border-neutral-600"
>

40
src/app/components/Clients/Search.vue

@ -0,0 +1,40 @@
<template>
<div class="relative w-60 md:mr-2">
<div class="relative flex h-full items-center">
<MagnifyingGlassIcon class="absolute left-2.5 h-4 w-4 text-gray-400 dark:text-neutral-500" />
<input
v-model="searchQuery"
type="text"
:placeholder="$t('client.search')"
class="w-full rounded bg-white py-2 pl-8 pr-8 text-sm text-gray-900 shadow-sm ring-1 ring-gray-300 transition-all placeholder:text-gray-400 focus:border-transparent focus:outline-none focus:ring-2 focus:ring-red-600 dark:bg-neutral-800 dark:text-white dark:ring-neutral-700 dark:placeholder:text-neutral-500 dark:focus:ring-red-700"
@input="updateSearch"
/>
<button
v-if="searchQuery"
class="absolute right-2 flex h-5 w-5 items-center justify-center rounded-full bg-gray-200 text-gray-600 hover:bg-gray-300 hover:text-gray-800 dark:bg-neutral-700 dark:text-neutral-300 dark:hover:bg-neutral-600 dark:hover:text-neutral-100"
@click="clearSearch"
aria-label="Clear search"
>
<IconsClose class="h-3 w-3" />
</button>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { MagnifyingGlassIcon } from '@heroicons/vue/24/outline';
import IconsClose from '../Icons/Close.vue';
const clientsStore = useClientsStore();
const searchQuery = ref('');
function updateSearch() {
clientsStore.setSearchQuery(searchQuery.value);
}
function clearSearch() {
searchQuery.value = '';
clientsStore.setSearchQuery('');
}
</script>

2
src/app/components/Panel/head/Boat.vue

@ -1,5 +1,5 @@
<template>
<div class="flex flex-shrink-0 space-x-1 md:block">
<div class="flex flex-shrink-0 items-center space-x-2">
<slot />
</div>
</template>

1
src/app/pages/index.vue

@ -4,6 +4,7 @@
<PanelHead>
<PanelHeadTitle :text="$t('pages.clients')" />
<PanelHeadBoat>
<ClientsSearch />
<ClientsSort />
<ClientsNew />
</PanelHeadBoat>

33
src/app/stores/clients.ts

@ -29,7 +29,9 @@ export type ClientPersist = {
export const useClientsStore = defineStore('Clients', () => {
const globalStore = useGlobalStore();
const clients = ref<null | LocalClient[]>(null);
const filteredClients = ref<null | LocalClient[]>(null);
const clientsPersist = ref<Record<string, ClientPersist>>({});
const searchQuery = ref('');
const { data: _clients, refresh: _refresh } = useFetch('/api/client', {
method: 'get',
@ -129,6 +131,35 @@ export const useClientsStore = defineStore('Clients', () => {
}
clients.value = transformedClients ?? null;
// Update filtered clients whenever clients change
updateFilteredClients();
}
return { clients, clientsPersist, refresh, _clients };
// Function to set search query and filter clients
function setSearchQuery(query: string) {
searchQuery.value = query;
updateFilteredClients();
}
// Function to filter clients based on search query
function updateFilteredClients() {
if (!clients.value || searchQuery.value === '') {
filteredClients.value = clients.value;
return;
}
const query = searchQuery.value.toLowerCase();
filteredClients.value = clients.value.filter(client =>
client.name.toLowerCase().includes(query)
);
}
return {
clients,
clientsPersist,
refresh,
_clients,
filteredClients,
searchQuery,
setSearchQuery
};
});

1
src/i18n/locales/en.json

@ -81,6 +81,7 @@
"client": {
"empty": "There are no clients yet.",
"newShort": "New",
"search": "Search clients...",
"sort": "Sort",
"create": "Create Client",
"created": "Client created",

Loading…
Cancel
Save