diff --git a/public/app/features/apiserver/types.ts b/public/app/features/apiserver/types.ts index 7ee1eae5f37..3e77035f13c 100644 --- a/public/app/features/apiserver/types.ts +++ b/public/app/features/apiserver/types.ts @@ -27,6 +27,8 @@ export interface ObjectMeta { generation?: number; // The first time this was saved creationTimestamp: string; + // The time this resource was marked for deletion + deletionTimestamp?: string; // General resource annotations -- including the common grafana.app values annotations?: GrafanaAnnotations & GrafanaClientAnnotations; // General application level key+value pairs diff --git a/public/app/features/browse-dashboards/api/useRecentlyDeletedStateManager.ts b/public/app/features/browse-dashboards/api/useRecentlyDeletedStateManager.ts index 3dd83fe2580..fa2d252c322 100644 --- a/public/app/features/browse-dashboards/api/useRecentlyDeletedStateManager.ts +++ b/public/app/features/browse-dashboards/api/useRecentlyDeletedStateManager.ts @@ -70,7 +70,6 @@ export class TrashStateManager extends SearchStateManager { } }; - // Only alphabetical sorting is supported for deleted dashboards getSortOptions = async (): Promise => { return Promise.resolve([ { @@ -81,6 +80,14 @@ export class TrashStateManager extends SearchStateManager { label: t('browse-dashboards.trash-state-manager.label.alphabetically-za', 'Alphabetically (Z–A)'), value: 'alpha-desc', }, + { + label: t('browse-dashboards.trash-state-manager.label.deleted-oldest', 'Deleted (oldest first)'), + value: 'deleted-asc', + }, + { + label: t('browse-dashboards.trash-state-manager.label.deleted-newest', 'Deleted (newest first)'), + value: 'deleted-desc', + }, ]); }; } diff --git a/public/app/features/search/service/utils.ts b/public/app/features/search/service/utils.ts index 981127f6688..cf93994d823 100644 --- a/public/app/features/search/service/utils.ts +++ b/public/app/features/search/service/utils.ts @@ -124,6 +124,11 @@ export function queryResultToViewItem( export function resourceToSearchResult(resource: ResourceList): SearchHit[] { return resource.items.map((item) => { + const field: Record = {}; + if (item.metadata.deletionTimestamp) { + field.deletionTimestamp = item.metadata.deletionTimestamp; + } + const hit = { resource: 'dashboards', name: item.metadata.name, @@ -131,7 +136,7 @@ export function resourceToSearchResult(resource: ResourceList) location: 'general', folder: item?.metadata?.annotations?.[AnnoKeyFolder] ?? 'general', tags: item.spec?.tags || [], - field: {}, + field, url: '', }; if (!hit.folder) { @@ -165,7 +170,7 @@ export function searchHitsToDashboardSearchHits(searchHits: SearchHit[]): Dashbo /** * Filters search results based on query parameters * This is used when backend filtering is not available (e.g., for deleted dashboards) - * Supports fuzzy search for tags and titles and alphabetical sorting + * Supports fuzzy search for tags and titles, alphabetical sorting, and deletion timestamp sorting */ export function filterSearchResults( results: SearchHit[], @@ -185,9 +190,33 @@ export function filterSearchResults( } if (query.sort) { - const collator = new Intl.Collator(); - const mult = query.sort === 'alpha-desc' ? -1 : 1; - filtered.sort((a, b) => mult * collator.compare(a.title, b.title)); + if (query.sort === 'deleted-asc' || query.sort === 'deleted-desc') { + const mult = query.sort === 'deleted-desc' ? -1 : 1; + filtered.sort((a, b) => { + const timestampA = a.field.deletionTimestamp; + const timestampB = b.field.deletionTimestamp; + + // Handle missing or invalid timestamps - items without timestamps go to the end + if (typeof timestampA !== 'string' && typeof timestampB !== 'string') { + return 0; + } + if (typeof timestampA !== 'string') { + return 1; + } + if (typeof timestampB !== 'string') { + return -1; + } + + const timeA = Date.parse(timestampA); + const timeB = Date.parse(timestampB); + return mult * (timeA - timeB); + }); + } else { + // Alphabetical sorting + const collator = new Intl.Collator(); + const mult = query.sort === 'alpha-desc' ? -1 : 1; + filtered.sort((a, b) => mult * collator.compare(a.title, b.title)); + } } return filtered; diff --git a/public/locales/en-US/grafana.json b/public/locales/en-US/grafana.json index 794dab7bbf5..662711d4270 100644 --- a/public/locales/en-US/grafana.json +++ b/public/locales/en-US/grafana.json @@ -3692,7 +3692,9 @@ "trash-state-manager": { "label": { "alphabetically-az": "Alphabetically (A–Z)", - "alphabetically-za": "Alphabetically (Z–A)" + "alphabetically-za": "Alphabetically (Z–A)", + "deleted-newest": "Deleted (newest first)", + "deleted-oldest": "Deleted (oldest first)" } } },