|
|
@ -164,6 +164,10 @@ import { Contact } from "../db/tables/contacts"; |
|
|
import { PlanData } from "../interfaces/records"; |
|
|
import { PlanData } from "../interfaces/records"; |
|
|
import { NotificationIface } from "../constants/app"; |
|
|
import { NotificationIface } from "../constants/app"; |
|
|
import { UNNAMED_ENTITY_NAME } from "@/constants/entities"; |
|
|
import { UNNAMED_ENTITY_NAME } from "@/constants/entities"; |
|
|
|
|
|
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin"; |
|
|
|
|
|
import { getHeaders } from "../libs/endorserServer"; |
|
|
|
|
|
import { logger } from "../utils/logger"; |
|
|
|
|
|
import { TIMEOUTS } from "@/utils/notify"; |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* Constants for infinite scroll configuration |
|
|
* Constants for infinite scroll configuration |
|
|
@ -191,6 +195,7 @@ const RECENT_CONTACTS_COUNT = 3; |
|
|
ProjectCard, |
|
|
ProjectCard, |
|
|
SpecialEntityCard, |
|
|
SpecialEntityCard, |
|
|
}, |
|
|
}, |
|
|
|
|
|
mixins: [PlatformServiceMixin], |
|
|
}) |
|
|
}) |
|
|
export default class EntityGrid extends Vue { |
|
|
export default class EntityGrid extends Vue { |
|
|
/** Type of entities to display */ |
|
|
/** Type of entities to display */ |
|
|
@ -202,23 +207,30 @@ export default class EntityGrid extends Vue { |
|
|
isSearching = false; |
|
|
isSearching = false; |
|
|
searchTimeout: NodeJS.Timeout | null = null; |
|
|
searchTimeout: NodeJS.Timeout | null = null; |
|
|
filteredEntities: Contact[] | PlanData[] = []; |
|
|
filteredEntities: Contact[] | PlanData[] = []; |
|
|
|
|
|
searchBeforeId: string | undefined = undefined; |
|
|
|
|
|
isLoadingSearchMore = false; |
|
|
|
|
|
|
|
|
|
|
|
// API server for project searches |
|
|
|
|
|
apiServer = ""; |
|
|
|
|
|
|
|
|
// Infinite scroll state |
|
|
// Infinite scroll state |
|
|
displayedCount = INITIAL_BATCH_SIZE; |
|
|
displayedCount = INITIAL_BATCH_SIZE; |
|
|
infiniteScrollReset?: () => void; |
|
|
infiniteScrollReset?: () => void; |
|
|
scrollContainer?: HTMLElement; |
|
|
scrollContainer?: HTMLElement; |
|
|
|
|
|
isLoadingMore = false; // Prevent duplicate callback calls |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* Array of entities to display |
|
|
* Array of entities to display |
|
|
* |
|
|
* |
|
|
|
|
|
* For contacts: Must be a COMPLETE list from local database. |
|
|
|
|
|
* Use $contactsByDateAdded() to ensure all contacts are included. |
|
|
|
|
|
* Client-side filtering assumes the complete list is available. |
|
|
* IMPORTANT: When passing Contact[] arrays, they must be sorted by date added |
|
|
* IMPORTANT: When passing Contact[] arrays, they must be sorted by date added |
|
|
* (newest first) for the "Recently Added" section to display correctly. |
|
|
* (newest first) for the "Recently Added" section to display correctly. |
|
|
* Use $contactsByDateAdded() instead of $getAllContacts() or $contacts(). |
|
|
|
|
|
* |
|
|
* |
|
|
* The recentContacts computed property assumes contacts are already sorted |
|
|
* For projects: Can be partial list (pagination supported). |
|
|
* by date added and simply takes the first 3. If contacts are sorted |
|
|
* Server-side search will fetch matching results with pagination, |
|
|
* alphabetically or in another order, the wrong contacts will appear in |
|
|
* regardless of what's in this prop. |
|
|
* "Recently Added". |
|
|
|
|
|
*/ |
|
|
*/ |
|
|
@Prop({ required: true }) |
|
|
@Prop({ required: true }) |
|
|
entities!: Contact[] | PlanData[]; |
|
|
entities!: Contact[] | PlanData[]; |
|
|
@ -286,6 +298,23 @@ export default class EntityGrid extends Vue { |
|
|
entityType: "people" | "projects", |
|
|
entityType: "people" | "projects", |
|
|
) => Contact[] | PlanData[]; |
|
|
) => Contact[] | PlanData[]; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* Optional callback function to load more entities from server |
|
|
|
|
|
* Called when infinite scroll reaches end and more data is available |
|
|
|
|
|
* Required for projects when using server-side pagination |
|
|
|
|
|
* |
|
|
|
|
|
* @param entities - Current array of entities |
|
|
|
|
|
* @returns Promise that resolves when more entities are loaded |
|
|
|
|
|
* |
|
|
|
|
|
* @example |
|
|
|
|
|
* :load-more-callback="async (entities) => { |
|
|
|
|
|
* const lastEntity = entities[entities.length - 1]; |
|
|
|
|
|
* await loadMoreFromServer(lastEntity.rowId); |
|
|
|
|
|
* }" |
|
|
|
|
|
*/ |
|
|
|
|
|
@Prop({ default: null }) |
|
|
|
|
|
loadMoreCallback?: (entities: Contact[] | PlanData[]) => Promise<void>; |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* CSS classes for the empty state message |
|
|
* CSS classes for the empty state message |
|
|
*/ |
|
|
*/ |
|
|
@ -457,24 +486,162 @@ export default class EntityGrid extends Vue { |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* Perform the actual search |
|
|
* Perform the actual search |
|
|
|
|
|
* Routes to server-side search for projects or client-side filtering for contacts |
|
|
*/ |
|
|
*/ |
|
|
async performSearch(): Promise<void> { |
|
|
async performSearch(): Promise<void> { |
|
|
if (!this.searchTerm.trim()) { |
|
|
if (!this.searchTerm.trim()) { |
|
|
this.filteredEntities = []; |
|
|
this.filteredEntities = []; |
|
|
this.displayedCount = INITIAL_BATCH_SIZE; |
|
|
this.displayedCount = INITIAL_BATCH_SIZE; |
|
|
|
|
|
this.searchBeforeId = undefined; |
|
|
this.infiniteScrollReset?.(); |
|
|
this.infiniteScrollReset?.(); |
|
|
return; |
|
|
return; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
this.isSearching = true; |
|
|
this.isSearching = true; |
|
|
|
|
|
this.searchBeforeId = undefined; // Reset pagination for new search |
|
|
|
|
|
|
|
|
|
|
|
try { |
|
|
|
|
|
if (this.entityType === "projects") { |
|
|
|
|
|
// Server-side search for projects (initial load, no beforeId) |
|
|
|
|
|
await this.performProjectSearch(); |
|
|
|
|
|
} else { |
|
|
|
|
|
// Client-side filtering for contacts (complete list) |
|
|
|
|
|
await this.performContactSearch(); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Reset displayed count when search completes |
|
|
|
|
|
this.displayedCount = INITIAL_BATCH_SIZE; |
|
|
|
|
|
this.infiniteScrollReset?.(); |
|
|
|
|
|
} finally { |
|
|
|
|
|
this.isSearching = false; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* Perform server-side project search with optional pagination |
|
|
|
|
|
* Uses claimContents parameter for search and beforeId for pagination. |
|
|
|
|
|
* Results are appended when paginating, replaced on initial search. |
|
|
|
|
|
* |
|
|
|
|
|
* @param beforeId - Optional rowId for pagination (loads projects before this ID) |
|
|
|
|
|
*/ |
|
|
|
|
|
async performProjectSearch(beforeId?: string): Promise<void> { |
|
|
|
|
|
if (!this.apiServer) { |
|
|
|
|
|
this.filteredEntities = []; |
|
|
|
|
|
if (this.notify) { |
|
|
|
|
|
this.notify( |
|
|
|
|
|
{ |
|
|
|
|
|
group: "alert", |
|
|
|
|
|
type: "danger", |
|
|
|
|
|
title: "Error", |
|
|
|
|
|
text: "API server not configured", |
|
|
|
|
|
}, |
|
|
|
|
|
TIMEOUTS.SHORT, |
|
|
|
|
|
); |
|
|
|
|
|
} |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const searchLower = this.searchTerm.toLowerCase().trim(); |
|
|
|
|
|
let url = `${this.apiServer}/api/v2/report/plans?claimContents=${encodeURIComponent(searchLower)}`; |
|
|
|
|
|
|
|
|
|
|
|
if (beforeId) { |
|
|
|
|
|
url += `&beforeId=${encodeURIComponent(beforeId)}`; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
try { |
|
|
try { |
|
|
// Simulate async search (in case we need to add API calls later) |
|
|
const response = await fetch(url, { |
|
|
|
|
|
method: "GET", |
|
|
|
|
|
headers: await getHeaders(this.activeDid), |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
if (response.status !== 200) { |
|
|
|
|
|
throw new Error("Failed to search projects"); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const results = await response.json(); |
|
|
|
|
|
if (results.data) { |
|
|
|
|
|
const newProjects = results.data.map( |
|
|
|
|
|
(plan: PlanData & { rowId?: string }) => ({ |
|
|
|
|
|
...plan, |
|
|
|
|
|
rowId: plan.rowId, |
|
|
|
|
|
}), |
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
logger.debug("[EntityGrid] Project search results", { |
|
|
|
|
|
beforeId, |
|
|
|
|
|
newProjectsCount: newProjects.length, |
|
|
|
|
|
hasRowId: |
|
|
|
|
|
newProjects.length > 0 |
|
|
|
|
|
? !!newProjects[newProjects.length - 1]?.rowId |
|
|
|
|
|
: false, |
|
|
|
|
|
lastRowId: |
|
|
|
|
|
newProjects.length > 0 |
|
|
|
|
|
? newProjects[newProjects.length - 1]?.rowId |
|
|
|
|
|
: undefined, |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
if (beforeId) { |
|
|
|
|
|
// Pagination: append new projects to existing search results |
|
|
|
|
|
this.filteredEntities.push(...newProjects); |
|
|
|
|
|
} else { |
|
|
|
|
|
// Initial search: replace array |
|
|
|
|
|
this.filteredEntities = newProjects; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Update searchBeforeId for next pagination |
|
|
|
|
|
// Use the last project's rowId, or undefined if no more results |
|
|
|
|
|
if (newProjects.length > 0) { |
|
|
|
|
|
const lastProject = newProjects[newProjects.length - 1]; |
|
|
|
|
|
// Only set searchBeforeId if rowId exists (indicates more results available) |
|
|
|
|
|
this.searchBeforeId = lastProject.rowId || undefined; |
|
|
|
|
|
logger.debug("[EntityGrid] Updated searchBeforeId", { |
|
|
|
|
|
searchBeforeId: this.searchBeforeId, |
|
|
|
|
|
filteredEntitiesCount: this.filteredEntities.length, |
|
|
|
|
|
}); |
|
|
|
|
|
} else { |
|
|
|
|
|
this.searchBeforeId = undefined; // No more results |
|
|
|
|
|
logger.debug("[EntityGrid] No more search results", { |
|
|
|
|
|
filteredEntitiesCount: this.filteredEntities.length, |
|
|
|
|
|
}); |
|
|
|
|
|
} |
|
|
|
|
|
} else { |
|
|
|
|
|
if (!beforeId) { |
|
|
|
|
|
// Only clear on initial search, not pagination |
|
|
|
|
|
this.filteredEntities = []; |
|
|
|
|
|
} |
|
|
|
|
|
this.searchBeforeId = undefined; |
|
|
|
|
|
} |
|
|
|
|
|
} catch (error) { |
|
|
|
|
|
logger.error("Error searching projects:", error); |
|
|
|
|
|
if (!beforeId) { |
|
|
|
|
|
// Only clear on initial search error, not pagination error |
|
|
|
|
|
this.filteredEntities = []; |
|
|
|
|
|
} |
|
|
|
|
|
this.searchBeforeId = undefined; |
|
|
|
|
|
if (this.notify) { |
|
|
|
|
|
this.notify( |
|
|
|
|
|
{ |
|
|
|
|
|
group: "alert", |
|
|
|
|
|
type: "danger", |
|
|
|
|
|
title: "Error", |
|
|
|
|
|
text: "Failed to search projects. Please try again.", |
|
|
|
|
|
}, |
|
|
|
|
|
TIMEOUTS.STANDARD, |
|
|
|
|
|
); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* Client-side contact search |
|
|
|
|
|
* Assumes entities prop contains complete contact list from local database |
|
|
|
|
|
*/ |
|
|
|
|
|
async performContactSearch(): Promise<void> { |
|
|
|
|
|
// Simulate async (for consistency with project search) |
|
|
await new Promise((resolve) => setTimeout(resolve, 100)); |
|
|
await new Promise((resolve) => setTimeout(resolve, 100)); |
|
|
|
|
|
|
|
|
const searchLower = this.searchTerm.toLowerCase().trim(); |
|
|
const searchLower = this.searchTerm.toLowerCase().trim(); |
|
|
|
|
|
|
|
|
if (this.entityType === "people") { |
|
|
|
|
|
this.filteredEntities = (this.entities as Contact[]) |
|
|
this.filteredEntities = (this.entities as Contact[]) |
|
|
.filter((contact: Contact) => { |
|
|
.filter((contact: Contact) => { |
|
|
const name = contact.name?.toLowerCase() || ""; |
|
|
const name = contact.name?.toLowerCase() || ""; |
|
|
@ -487,25 +654,9 @@ export default class EntityGrid extends Vue { |
|
|
const nameB = (b.name || b.did).toLowerCase(); |
|
|
const nameB = (b.name || b.did).toLowerCase(); |
|
|
return nameA.localeCompare(nameB); |
|
|
return nameA.localeCompare(nameB); |
|
|
}); |
|
|
}); |
|
|
} else { |
|
|
|
|
|
this.filteredEntities = (this.entities as PlanData[]) |
|
|
|
|
|
.filter((project: PlanData) => { |
|
|
|
|
|
const name = project.name?.toLowerCase() || ""; |
|
|
|
|
|
const handleId = project.handleId.toLowerCase(); |
|
|
|
|
|
return name.includes(searchLower) || handleId.includes(searchLower); |
|
|
|
|
|
}) |
|
|
|
|
|
.sort((a: PlanData, b: PlanData) => { |
|
|
|
|
|
// Sort alphabetically by name |
|
|
|
|
|
return a.name.toLowerCase().localeCompare(b.name.toLowerCase()); |
|
|
|
|
|
}); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Reset displayed count when search completes |
|
|
// Contacts don't need pagination (complete list) |
|
|
this.displayedCount = INITIAL_BATCH_SIZE; |
|
|
this.searchBeforeId = undefined; |
|
|
this.infiniteScrollReset?.(); |
|
|
|
|
|
} finally { |
|
|
|
|
|
this.isSearching = false; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
@ -516,6 +667,7 @@ export default class EntityGrid extends Vue { |
|
|
this.filteredEntities = []; |
|
|
this.filteredEntities = []; |
|
|
this.isSearching = false; |
|
|
this.isSearching = false; |
|
|
this.displayedCount = INITIAL_BATCH_SIZE; |
|
|
this.displayedCount = INITIAL_BATCH_SIZE; |
|
|
|
|
|
this.searchBeforeId = undefined; |
|
|
this.infiniteScrollReset?.(); |
|
|
this.infiniteScrollReset?.(); |
|
|
|
|
|
|
|
|
// Clear any pending timeout |
|
|
// Clear any pending timeout |
|
|
@ -535,12 +687,34 @@ export default class EntityGrid extends Vue { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (this.searchTerm.trim()) { |
|
|
if (this.searchTerm.trim()) { |
|
|
// Search mode: check filtered entities |
|
|
// Search mode: check if more results available |
|
|
|
|
|
if (this.entityType === "projects") { |
|
|
|
|
|
// Projects: can load more if: |
|
|
|
|
|
// 1. We have more already-loaded results to show, OR |
|
|
|
|
|
// 2. We've shown all loaded results AND there's a searchBeforeId to load more |
|
|
|
|
|
const hasMoreLoaded = |
|
|
|
|
|
this.displayedCount < this.filteredEntities.length; |
|
|
|
|
|
const canLoadMoreFromServer = |
|
|
|
|
|
this.displayedCount >= this.filteredEntities.length && |
|
|
|
|
|
!!this.searchBeforeId && |
|
|
|
|
|
!this.isLoadingSearchMore; |
|
|
|
|
|
return hasMoreLoaded || canLoadMoreFromServer; |
|
|
|
|
|
} else { |
|
|
|
|
|
// Contacts: client-side filtering returns all results at once |
|
|
return this.displayedCount < this.filteredEntities.length; |
|
|
return this.displayedCount < this.filteredEntities.length; |
|
|
} |
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Non-search mode: existing logic |
|
|
if (this.entityType === "projects") { |
|
|
if (this.entityType === "projects") { |
|
|
// Projects: check if more available |
|
|
// Projects: if we've shown all loaded entities and callback exists, callback handles server-side availability |
|
|
|
|
|
if ( |
|
|
|
|
|
this.displayedCount >= this.entities.length && |
|
|
|
|
|
this.loadMoreCallback |
|
|
|
|
|
) { |
|
|
|
|
|
return !this.isLoadingMore; // Only return true if not already loading |
|
|
|
|
|
} |
|
|
|
|
|
// Otherwise, check if more in memory |
|
|
return this.displayedCount < this.entities.length; |
|
|
return this.displayedCount < this.entities.length; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@ -553,16 +727,74 @@ export default class EntityGrid extends Vue { |
|
|
/** |
|
|
/** |
|
|
* Initialize infinite scroll on mount |
|
|
* Initialize infinite scroll on mount |
|
|
*/ |
|
|
*/ |
|
|
mounted(): void { |
|
|
async mounted(): Promise<void> { |
|
|
|
|
|
// Load apiServer for project searches |
|
|
|
|
|
if (this.entityType === "projects") { |
|
|
|
|
|
const settings = await this.$accountSettings(); |
|
|
|
|
|
this.apiServer = settings.apiServer || ""; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
this.$nextTick(() => { |
|
|
this.$nextTick(() => { |
|
|
const container = this.$refs.scrollContainer as HTMLElement; |
|
|
const container = this.$refs.scrollContainer as HTMLElement; |
|
|
|
|
|
|
|
|
if (container) { |
|
|
if (container) { |
|
|
const { reset } = useInfiniteScroll( |
|
|
const { reset } = useInfiniteScroll( |
|
|
container, |
|
|
container, |
|
|
() => { |
|
|
async () => { |
|
|
// Load more: increment displayedCount |
|
|
// Search mode: handle search pagination |
|
|
|
|
|
if (this.searchTerm.trim()) { |
|
|
|
|
|
if (this.entityType === "projects") { |
|
|
|
|
|
// Projects: load more search results if available |
|
|
|
|
|
if ( |
|
|
|
|
|
this.displayedCount >= this.filteredEntities.length && |
|
|
|
|
|
this.searchBeforeId && |
|
|
|
|
|
!this.isLoadingSearchMore |
|
|
|
|
|
) { |
|
|
|
|
|
this.isLoadingSearchMore = true; |
|
|
|
|
|
try { |
|
|
|
|
|
await this.performProjectSearch(this.searchBeforeId); |
|
|
|
|
|
// After loading more, reset scroll state to allow further loading |
|
|
|
|
|
this.infiniteScrollReset?.(); |
|
|
|
|
|
} catch (error) { |
|
|
|
|
|
logger.error("Error loading more search results:", error); |
|
|
|
|
|
// Error already handled in performProjectSearch |
|
|
|
|
|
} finally { |
|
|
|
|
|
this.isLoadingSearchMore = false; |
|
|
|
|
|
} |
|
|
|
|
|
} else { |
|
|
|
|
|
// Show more from already-loaded search results |
|
|
|
|
|
this.displayedCount += INCREMENT_SIZE; |
|
|
|
|
|
} |
|
|
|
|
|
} else { |
|
|
|
|
|
// Contacts: show more from already-filtered results |
|
|
this.displayedCount += INCREMENT_SIZE; |
|
|
this.displayedCount += INCREMENT_SIZE; |
|
|
|
|
|
} |
|
|
|
|
|
} else { |
|
|
|
|
|
// Non-search mode: existing logic |
|
|
|
|
|
// For projects: if we've shown all entities and callback exists, call it |
|
|
|
|
|
if ( |
|
|
|
|
|
this.entityType === "projects" && |
|
|
|
|
|
this.displayedCount >= this.entities.length && |
|
|
|
|
|
this.loadMoreCallback && |
|
|
|
|
|
!this.isLoadingMore |
|
|
|
|
|
) { |
|
|
|
|
|
this.isLoadingMore = true; |
|
|
|
|
|
try { |
|
|
|
|
|
await this.loadMoreCallback(this.entities); |
|
|
|
|
|
// After callback, entities prop will update via Vue reactivity |
|
|
|
|
|
// Reset scroll state to allow further loading |
|
|
|
|
|
this.infiniteScrollReset?.(); |
|
|
|
|
|
} catch (error) { |
|
|
|
|
|
// Error handling is up to the callback, but we should reset loading state |
|
|
|
|
|
console.error("Error in loadMoreCallback:", error); |
|
|
|
|
|
} finally { |
|
|
|
|
|
this.isLoadingMore = false; |
|
|
|
|
|
} |
|
|
|
|
|
} else { |
|
|
|
|
|
// Normal case: increment displayedCount to show more from memory |
|
|
|
|
|
this.displayedCount += INCREMENT_SIZE; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
}, |
|
|
}, |
|
|
{ |
|
|
{ |
|
|
distance: 50, // pixels from bottom |
|
|
distance: 50, // pixels from bottom |
|
|
@ -588,19 +820,27 @@ export default class EntityGrid extends Vue { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* Watch for changes in search term to reset displayed count |
|
|
* Watch for changes in search term to reset displayed count and pagination |
|
|
*/ |
|
|
*/ |
|
|
@Watch("searchTerm") |
|
|
@Watch("searchTerm") |
|
|
onSearchTermChange(): void { |
|
|
onSearchTermChange(): void { |
|
|
|
|
|
// Reset displayed count and pagination when search term changes |
|
|
this.displayedCount = INITIAL_BATCH_SIZE; |
|
|
this.displayedCount = INITIAL_BATCH_SIZE; |
|
|
|
|
|
this.searchBeforeId = undefined; |
|
|
this.infiniteScrollReset?.(); |
|
|
this.infiniteScrollReset?.(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* Watch for changes in entities prop to reset displayed count |
|
|
* Watch for changes in entities prop to clear search and reset displayed count |
|
|
*/ |
|
|
*/ |
|
|
@Watch("entities") |
|
|
@Watch("entities") |
|
|
onEntitiesChange(): void { |
|
|
onEntitiesChange(): void { |
|
|
|
|
|
// Clear search when entities change (fresh dialog open) |
|
|
|
|
|
if (this.searchTerm) { |
|
|
|
|
|
this.searchTerm = ""; |
|
|
|
|
|
this.filteredEntities = []; |
|
|
|
|
|
this.searchBeforeId = undefined; |
|
|
|
|
|
} |
|
|
this.displayedCount = INITIAL_BATCH_SIZE; |
|
|
this.displayedCount = INITIAL_BATCH_SIZE; |
|
|
this.infiniteScrollReset?.(); |
|
|
this.infiniteScrollReset?.(); |
|
|
} |
|
|
} |
|
|
|