forked from jsnbuchanan/crowd-funder-for-time-pwa
feat(EntityGrid): Split contacts into recent and alphabetical sections
When displaying contacts (not search results), show the 3 most recently added contacts at the top with a "Recently Added" heading, followed by the rest sorted alphabetically with an "Everyone Else" heading. - Add recentContacts and alphabeticalContacts computed properties - Hide "You" and "Unnamed" special entities during search - Only show search spinner when actively searching with a term - Style section headings with uppercase, improved spacing, and borders
This commit is contained in:
@@ -13,7 +13,7 @@ projects, and special entities with selection. * * @author Matthew Raymer */
|
||||
@keydown.enter="performSearch"
|
||||
/>
|
||||
<div
|
||||
v-show="isSearching"
|
||||
v-show="isSearching && searchTerm"
|
||||
class="border-y border-slate-400 ps-2 py-1.5 text-center text-slate-400"
|
||||
>
|
||||
<font-awesome
|
||||
@@ -47,7 +47,7 @@ projects, and special entities with selection. * * @author Matthew Raymer */
|
||||
<template v-if="entityType === 'people'">
|
||||
<!-- "You" entity -->
|
||||
<SpecialEntityCard
|
||||
v-if="showYouEntity"
|
||||
v-if="showYouEntity && !searchTerm.trim()"
|
||||
entity-type="you"
|
||||
label="You"
|
||||
icon="hand"
|
||||
@@ -61,7 +61,7 @@ projects, and special entities with selection. * * @author Matthew Raymer */
|
||||
|
||||
<!-- "Unnamed" entity -->
|
||||
<SpecialEntityCard
|
||||
v-if="showUnnamedEntity"
|
||||
v-if="showUnnamedEntity && !searchTerm.trim()"
|
||||
entity-type="unnamed"
|
||||
:label="unnamedEntityName"
|
||||
icon="circle-question"
|
||||
@@ -79,16 +79,60 @@ projects, and special entities with selection. * * @author Matthew Raymer */
|
||||
|
||||
<!-- Entity cards (people or projects) -->
|
||||
<template v-if="entityType === 'people'">
|
||||
<PersonCard
|
||||
v-for="person in displayedEntities as Contact[]"
|
||||
:key="person.did"
|
||||
:person="person"
|
||||
:conflicted="isPersonConflicted(person.did)"
|
||||
:show-time-icon="true"
|
||||
:notify="notify"
|
||||
:conflict-context="conflictContext"
|
||||
@person-selected="handlePersonSelected"
|
||||
/>
|
||||
<!-- When showing contacts without search: split into recent and alphabetical -->
|
||||
<template v-if="!searchTerm.trim()">
|
||||
<!-- Recently Added Section -->
|
||||
<template v-if="recentContacts.length > 0">
|
||||
<li
|
||||
class="text-xs font-semibold text-slate-500 uppercase pt-5 pb-1.5 px-2 border-b border-slate-300"
|
||||
>
|
||||
Recently Added
|
||||
</li>
|
||||
<PersonCard
|
||||
v-for="person in recentContacts"
|
||||
:key="person.did"
|
||||
:person="person"
|
||||
:conflicted="isPersonConflicted(person.did)"
|
||||
:show-time-icon="true"
|
||||
:notify="notify"
|
||||
:conflict-context="conflictContext"
|
||||
@person-selected="handlePersonSelected"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<!-- Alphabetical Section -->
|
||||
<template v-if="alphabeticalContacts.length > 0">
|
||||
<li
|
||||
class="text-xs font-semibold text-slate-500 uppercase pt-5 pb-1.5 px-2 border-b border-slate-300"
|
||||
>
|
||||
Everyone Else
|
||||
</li>
|
||||
<PersonCard
|
||||
v-for="person in alphabeticalContacts"
|
||||
:key="person.did"
|
||||
:person="person"
|
||||
:conflicted="isPersonConflicted(person.did)"
|
||||
:show-time-icon="true"
|
||||
:notify="notify"
|
||||
:conflict-context="conflictContext"
|
||||
@person-selected="handlePersonSelected"
|
||||
/>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<!-- When searching: show filtered results normally -->
|
||||
<template v-else>
|
||||
<PersonCard
|
||||
v-for="person in displayedEntities as Contact[]"
|
||||
:key="person.did"
|
||||
:person="person"
|
||||
:conflicted="isPersonConflicted(person.did)"
|
||||
:show-time-icon="true"
|
||||
:notify="notify"
|
||||
:conflict-context="conflictContext"
|
||||
@person-selected="handlePersonSelected"
|
||||
/>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<template v-else-if="entityType === 'projects'">
|
||||
@@ -257,6 +301,35 @@ export default class EntityGrid extends Vue {
|
||||
return this.entities.slice(0, maxDisplay);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the 3 most recently added contacts (when showing contacts and not searching)
|
||||
*/
|
||||
get recentContacts(): Contact[] {
|
||||
if (this.entityType !== "people" || this.searchTerm.trim()) {
|
||||
return [];
|
||||
}
|
||||
// Entities are already sorted by date added (newest first)
|
||||
return (this.entities as Contact[]).slice(0, 3);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the remaining contacts sorted alphabetically (when showing contacts and not searching)
|
||||
*/
|
||||
get alphabeticalContacts(): Contact[] {
|
||||
if (this.entityType !== "people" || this.searchTerm.trim()) {
|
||||
return [];
|
||||
}
|
||||
// Skip the first 3 (recent contacts) and sort the rest alphabetically
|
||||
// Create a copy to avoid mutating the original array
|
||||
const remaining = (this.entities as Contact[]).slice(3);
|
||||
return [...remaining].sort((a: Contact, b: Contact) => {
|
||||
// Sort alphabetically by name, falling back to DID if name is missing
|
||||
const nameA = (a.name || a.did).toLowerCase();
|
||||
const nameB = (b.name || b.did).toLowerCase();
|
||||
return nameA.localeCompare(nameB);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Computed empty state message based on entity type
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user