151151 </button >
152152 </div >
153153
154+ <!-- Members section -->
155+ <div v-if =" filteredMembers.length > 0" >
156+ <div
157+ class =" px-3 py-2 text-xs font-semibold text-gray-500 bg-gray-50 border-b"
158+ >
159+ Members
160+ </div >
161+ <button
162+ v-for =" member in filteredMembers"
163+ :key =" `member-${member.id}`"
164+ type =" button"
165+ :class =" [
166+ 'w-full text-left px-3 py-2 border-b border-gray-100 last:border-b-0 flex items-center gap-3',
167+ getItemIndex(member) === highlightedIndex
168+ ? 'bg-blue-50 border-blue-200'
169+ : 'hover:bg-gray-50',
170+ ]"
171+ @click =" selectMember(member)"
172+ >
173+ <Image
174+ :src =" member.img"
175+ :alt =" member.name"
176+ class =" w-8 h-8 rounded object-cover border flex-shrink-0"
177+ />
178+ <div class =" flex-1 min-w-0" >
179+ <div class =" font-medium text-gray-900 truncate" >
180+ {{ member.name }}
181+ </div >
182+ </div >
183+ </button >
184+ </div >
185+
154186 <!-- No results -->
155187 <div
156188 v-if ="
157189 !isLoading &&
158190 filteredCompanies.length === 0 &&
159191 filteredSpeakers.length === 0 &&
192+ filteredMembers.length === 0 &&
160193 searchTerm.trim()
161194 "
162195 class =" p-3 text-gray-500 text-center"
163196 >
164- No companies or speakers found
197+ No companies, speakers or members found
165198 </div >
166199
167200 <!-- Welcome message when no search term -->
170203 !isLoading &&
171204 !searchTerm.trim() &&
172205 filteredCompanies.length === 0 &&
173- filteredSpeakers.length === 0
206+ filteredSpeakers.length === 0 &&
207+ filteredMembers.length === 0
174208 "
175209 class =" p-3 text-gray-500 text-center"
176210 >
177- Start typing to search companies and speakers ...
211+ Start typing to search companies, speakers and members ...
178212 </div >
179213
180214 <!-- Create options -->
@@ -255,6 +289,7 @@ import { ref, computed, watch, nextTick } from "vue";
255289import { useQuery } from " @pinia/colada" ;
256290import { getAllCompanies } from " @/api/companies" ;
257291import { getAllSpeakers } from " @/api/speakers" ;
292+ import { getAllMembers } from " @/api/members" ;
258293import { useEventStore } from " @/stores/event" ;
259294import { Input } from " @/components/ui/input" ;
260295import { Label } from " @/components/ui/label" ;
@@ -271,8 +306,9 @@ import {
271306} from " @/components/ui/alert-dialog" ;
272307import type { Company } from " @/dto/companies" ;
273308import type { Speaker } from " @/dto/speakers" ;
309+ import type { Member } from " @/dto/members" ;
274310
275- type SelectedItem = Company | Speaker ;
311+ type SelectedItem = Company | Speaker | Member ;
276312
277313interface Props {
278314 modelValue? : string ;
@@ -297,6 +333,7 @@ const props = withDefaults(defineProps<Props>(), {
297333const emit = defineEmits <{
298334 companySelected: [value : Company ];
299335 speakerSelected: [value : Speaker ];
336+ memberSelected: [value : Member ];
300337 " update:modelValue" : [value : string ];
301338 companySuccess: [companyId : string ];
302339 speakerSuccess: [speakerId : string ];
@@ -335,8 +372,14 @@ const { data: speakersData, isLoading: speakersLoading } = useQuery({
335372 enabled : () => !! eventStore .selectedEvent ?.id ,
336373});
337374
375+ const { data : membersData, isLoading : membersLoading } = useQuery ({
376+ key : () => [" members" ],
377+ query : () => getAllMembers ({}),
378+ enabled : () => !! eventStore .selectedEvent ?.id ,
379+ });
380+
338381const isLoading = computed (
339- () => companiesLoading .value || speakersLoading .value ,
382+ () => companiesLoading .value || speakersLoading .value || membersLoading . value ,
340383);
341384
342385const filteredCompanies = computed (() => {
@@ -377,12 +420,37 @@ const filteredSpeakers = computed(() => {
377420 .slice (0 , 5 ); // Limit to 5 results
378421});
379422
423+ const filteredMembers = computed (() => {
424+ if (! membersData .value ?.data ) return [];
425+
426+ const term = searchTerm .value .toLowerCase ();
427+
428+ if (! term ) {
429+ // Show recent members when no search term
430+ return membersData .value .data .slice (0 , 5 );
431+ }
432+
433+ return membersData .value .data
434+ .filter ((member : Member ) => member .name .toLowerCase ().includes (term ))
435+ .slice (0 , 5 );
436+ });
437+
380438const results = computed (() => [
381- ... filteredCompanies .value ,
382- ... filteredSpeakers .value ,
439+ ... filteredCompanies .value .map ((company : Company ) => ({
440+ ... company ,
441+ type: " company" ,
442+ })),
443+ ... filteredSpeakers .value .map ((speaker : Speaker ) => ({
444+ ... speaker ,
445+ type: " speaker" ,
446+ })),
447+ ... filteredMembers .value .map ((member : Member ) => ({
448+ ... member ,
449+ type: " member" ,
450+ })),
383451]);
384452
385- const getItemIndex = (item : Company | Speaker ) => {
453+ const getItemIndex = (item : SelectedItem ) => {
386454 return results .value .findIndex ((result ) => result .id === item .id );
387455};
388456
@@ -396,6 +464,10 @@ const getItemImage = (item: SelectedItem) => {
396464 return item .imgs .internal || item .imgs .speaker ;
397465 }
398466 }
467+
468+ // Member may have an `img` property
469+ if ((item as Member ).img ) return (item as Member ).img ;
470+
399471 return " " ;
400472};
401473
@@ -440,11 +512,11 @@ const handleKeydown = (event: KeyboardEvent) => {
440512 highlightedIndex .value < results .value .length
441513 ) {
442514 const selectedResult = results .value [highlightedIndex .value ];
443- if (" companyName" in selectedResult ) {
444- // It's a speaker
515+ if (selectedResult .type === " speaker" ) {
445516 selectSpeaker (selectedResult as Speaker );
446- } else {
447- // It's a company
517+ } else if (selectedResult .type === " member" ) {
518+ selectMember (selectedResult as Member );
519+ } else if (selectedResult .type === " company" ) {
448520 selectCompany (selectedResult as Company );
449521 }
450522 }
@@ -469,6 +541,15 @@ const selectSpeaker = (speaker: Speaker) => {
469541 emit (" update:modelValue" , speaker .name );
470542};
471543
544+ const selectMember = (member : Member ) => {
545+ selectedItem .value = member ;
546+ searchTerm .value = member .name ;
547+ showSuggestions .value = false ;
548+ highlightedIndex .value = - 1 ;
549+ emit (" memberSelected" , member );
550+ emit (" update:modelValue" , member .name );
551+ };
552+
472553const clearSelection = () => {
473554 selectedItem .value = null ;
474555 searchTerm .value = " " ;
0 commit comments