Repository: kb-dk/ds-web
Pull request #289: Display total results and sort icon
Author: login: Pbruun
Created: 2025-03-19T08:46:15Z | Merged: 2025-03-25T13:29:07Z

We wish to display, how many results are in the current page, and what that amounts to out of the total result count.

Added pageNumber to store, because I wished to reuse it, and it just became convoluted math in the html.


Diff:
diff --git a/src/components/search/Pager.vue b/src/components/search/Pager.vue
index 3bddc8d8..96c68ba7 100644
--- a/src/components/search/Pager.vue
+++ b/src/components/search/Pager.vue
@@ -1,7 +1,11 @@
 <template>
 	<div :class="searchResultStore.loading ? 'pager disabled' : 'pager'">
 		<div class="morePagesMessage">
-			<div v-if="totalPages > maxPages && currentPageRef === maxPages">
+			<div
+				v-if="
+					searchResultStore.totalPages > searchResultStore.maxPages && currentPageRef === searchResultStore.maxPages
+				"
+			>
 				<div>
 					{{ $t('search.morePagesMessage.1') }}
 					{{ $t('search.morePagesMessage.2') }}
@@ -54,7 +58,7 @@
 				</button>
 			</span>
 			<button
-				:disabled="currentPageRef === maxPages"
+				:disabled="currentPageRef === searchResultStore.maxPages"
 				:title="$t('search.nextPage')"
 				:aria-label="$t('search.nextPage')"
 				@click="nextPage"
@@ -110,11 +114,7 @@ export default defineComponent({
 		const searchResultStore = useSearchResultStore();
 		const selectPage = ref('');
 		const inputIncorrect = ref(false);
-		const totalPages = computed(() => Math.ceil(props.totalHits / props.itemsPerPage));
-		//We normally display 10 or 40 items per page. This'll make it dynamic2
-		const maxPages = computed(() =>
-			totalPages.value > 1000 / props.itemsPerPage ? 1000 / props.itemsPerPage : totalPages.value,
-		);
+
 		const errorManager = inject('errorManager') as ErrorManagerType;
 		const { t } = useI18n({ useScope: 'global' });
 		const inputRef = ref<HTMLInputElement | null>();
@@ -122,7 +122,7 @@ export default defineComponent({
 			() => selectPage.value,
 			(newVal) => {
 				if (newVal) {
-					if (parseInt(newVal) <= 0 || parseInt(newVal) > maxPages.value) {
+					if (parseInt(newVal) <= 0 || parseInt(newVal) > searchResultStore.maxPages) {
 						inputIncorrect.value = true;
 					} else {
 						inputIncorrect.value = false;
@@ -136,7 +136,7 @@ export default defineComponent({
 			}
 		};
 		const submitPage = () => {
-			if (parseInt(selectPage.value) <= maxPages.value && parseInt(selectPage.value) > 0) {
+			if (parseInt(selectPage.value) <= searchResultStore.maxPages && parseInt(selectPage.value) > 0) {
 				goToPage(parseInt(selectPage.value));
 			} else {
 				errorManager.submitCustomError(
@@ -161,7 +161,7 @@ export default defineComponent({
 		};
 
 		const nextPage = () => {
-			if (currentPageRef.value < maxPages.value) {
+			if (currentPageRef.value < searchResultStore.maxPages) {
 				currentPageRef.value++;
 				navigate();
 			}
@@ -175,7 +175,7 @@ export default defineComponent({
 		};
 
 		const goToPage = (page: number) => {
-			if (page >= 1 && page <= maxPages.value) {
+			if (page >= 1 && page <= searchResultStore.maxPages) {
 				currentPageRef.value = page;
 				navigate();
 			}
@@ -185,8 +185,8 @@ export default defineComponent({
 			//Array to hold them page numbers
 			const pages = [];
 			//If the page count doesn't exceed our number of pages to show we just push all pages
-			if (maxPages.value <= props.numPagesToShow) {
-				for (let i = 1; i <= maxPages.value; i++) {
+			if (searchResultStore.maxPages <= props.numPagesToShow) {
+				for (let i = 1; i <= searchResultStore.maxPages; i++) {
 					pages.push(i);
 				}
 				/*If the page count does exceed our number of pages to show we calculate
@@ -202,8 +202,8 @@ export default defineComponent({
 				 	and the start page is calculated from endPage and 'number of pages to show' values. We +1
 					or else there would be shown one more page in the pager than specified in props.numPagesToShow
 				 */
-				if (endPage > maxPages.value) {
-					endPage = maxPages.value;
+				if (endPage > searchResultStore.maxPages) {
+					endPage = searchResultStore.maxPages;
 					startPage = endPage - props.numPagesToShow + 1;
 				}
 				if (startPage > 1) {
@@ -228,11 +228,11 @@ export default defineComponent({
 				so we push them to the pages array. If this condition is not met we know this is the end of line
 				and push the total page count to the array as the last thing before we return pages
 				*/
-				if (endPage < maxPages.value) {
-					if (endPage < maxPages.value - 1) {
+				if (endPage < searchResultStore.maxPages) {
+					if (endPage < searchResultStore.maxPages - 1) {
 						pages.push('...');
 					}
-					pages.push(maxPages.value);
+					pages.push(searchResultStore.maxPages);
 				}
 			}
 			return pages;
@@ -271,8 +271,6 @@ export default defineComponent({
 		};
 		return {
 			currentPageRef,
-			totalPages,
-			maxPages,
 			nextPage,
 			prevPage,
 			goToPage,
diff --git a/src/components/search/SearchOverhead.vue b/src/components/search/SearchOverhead.vue
index bd03bbdf..9f169594 100644
--- a/src/components/search/SearchOverhead.vue
+++ b/src/components/search/SearchOverhead.vue
@@ -112,24 +112,29 @@
 				>
 					<Sort></Sort>
 					<div class="search-options">
-						<Transition name="fade">
-							<div
-								v-show="!searchResultStore.loading && searchResultStore.numFound > 0"
-								class="page-count"
-							>
-								{{ t('search.page') }}
-								{{
-									(Number(searchResultStore.start) + Number(searchResultStore.rowCount)) /
-									Number(searchResultStore.rowCount)
-								}}
-								{{ t('search.of') }}
-								{{
-									new Intl.NumberFormat('de-DE').format(
-										Math.ceil(searchResultStore.numFound / Number(searchResultStore.rowCount)),
-									)
-								}}
-							</div>
-						</Transition>
+						<div class="page-result-display">
+							<Transition name="fade">
+								<div
+									v-show="!searchResultStore.loading && searchResultStore.numFound > 0"
+									class="page-count"
+								>
+									{{ t('search.page') }}
+									{{ searchResultStore.pageNumber }}
+									{{ t('search.of') }}
+									{{ searchResultStore.maxPages }}
+								</div>
+							</Transition>
+							<Transition name="fade">
+								<div
+									v-show="!searchResultStore.loading && searchResultStore.numFound > 0"
+									class="page-count"
+								>
+									{{ t('search.showing') }} {{ Number(searchResultStore.start) + 1 }} -
+									{{ searchResultStore.pageNumber * Number(searchResultStore.rowCount) }}
+								</div>
+							</Transition>
+						</div>
+
 						<button
 							:class="
 								searchResultStore.loading
@@ -375,6 +380,12 @@ export default defineComponent({
 </script>
 
 <style scoped>
+.page-result-display {
+	display: flex;
+	flex-direction: column;
+	text-align: right;
+}
+
 .display-option {
 	background-color: transparent;
 	border: 0px;
diff --git a/src/components/search/Sort.vue b/src/components/search/Sort.vue
index ea3592d3..92d0a8a1 100644
--- a/src/components/search/Sort.vue
+++ b/src/components/search/Sort.vue
@@ -1,5 +1,6 @@
 <template>
 	<div class="sort">
+		<span class="material-icons sort-icon">sort</span>
 		<p class="sort-by">{{ t('search.sortBy') }}:</p>
 		<button
 			ref="relevanceRef"
@@ -148,7 +149,7 @@ export default defineComponent({
 	box-sizing: border-box;
 }
 
-.sort .material-icons {
+.sort > button > .material-icons {
 	color: rgba(71, 71, 71, 0);
 }
 
@@ -184,6 +185,10 @@ export default defineComponent({
 	border-bottom: 2px solid transparent;
 	padding-right: 5px;
 }
+.sort-icon {
+	color: #002e70;
+}
+
 @media (min-width: 640px) {
 	.sort-by {
 		margin-right: initial;
diff --git a/src/locales/da.json b/src/locales/da.json
index 622b1532..c2e3bcc9 100644
--- a/src/locales/da.json
+++ b/src/locales/da.json
@@ -114,6 +114,7 @@
       "unknownLength":"Ukendt tidsperiode"
    },
   "search":{
+     "showing":"Viser",
      "recordThumbnail":"Miniaturebillede af {title}",
      "searchbtn":"Søg",
      "hitcount": " resultat fundet med | resultater fundet med",
diff --git a/src/locales/en.json b/src/locales/en.json
index 55c0b9b7..cc23665d 100644
--- a/src/locales/en.json
+++ b/src/locales/en.json
@@ -116,6 +116,7 @@
 
    },
   "search":{
+      "showing":"Showing",
      "recordThumbnail":"Thumbnail from {title}",
      "searchbtn":"Search",
      "hitcount": " result fund with | results found with",
diff --git a/src/store/searchResultStore.ts b/src/store/searchResultStore.ts
index 0858c867..0982ba3d 100644
--- a/src/store/searchResultStore.ts
+++ b/src/store/searchResultStore.ts
@@ -3,13 +3,14 @@ import { FacetResultType, FacetsType, GenericSearchResultType } from '@/types/Ge
 import { APIService } from '@/api/api-service';
 import { ErrorManagerType } from '@/types/ErrorManagerType';
 import { AxiosError } from 'axios';
-import { inject, ref } from 'vue';
+import { computed, inject, ref } from 'vue';
 import { useI18n } from 'vue-i18n';
 import { SpellCheckType } from '@/types/SpellCheckType';
 import { LocationQueryValue } from 'vue-router';
 import { APIAutocompleteTerm } from '@/types/APIResponseTypes';
 import { Priority, Severity } from '@/types/NotificationType';
 import { CuratedItemsType } from '@/types/CuratedItemsType';
+import { max } from 'date-fns';
 
 export const useSearchResultStore = defineStore('searchResults', () => {
 	let currentSearchUUID = '';
@@ -47,6 +48,16 @@ export const useSearchResultStore = defineStore('searchResults', () => {
 	const currentChannelFacetString = ref('');
 	const loadingChannels = ref(false);
 	const loadingGenres = ref(false);
+	const totalPages = computed(() => Math.ceil(numFound.value / Number(rowCount.value)));
+	//We normally display 10 or 40 items per page. This'll make it dynamic
+	const maxPages = computed(() =>
+		totalPages.value > 1000 / Number(rowCount.value) ? 1000 / Number(rowCount.value) : totalPages.value,
+	);
+
+	const pageNumber = computed(() => {
+		const pageStart = start.value ? Number(start.value) : 0;
+		return (pageStart + Number(rowCount.value)) / Number(rowCount.value);
+	});
 
 	const setStart = (value: string) => {
 		start.value = value;
@@ -414,5 +425,8 @@ export const useSearchResultStore = defineStore('searchResults', () => {
 		setRowCountFromURL,
 		setRotationalResult,
 		setCuratedContent,
+		pageNumber,
+		totalPages,
+		maxPages,
 	};
 });
diff --git a/src/views/Search.vue b/src/views/Search.vue
index d51547a8..68289469 100644
--- a/src/views/Search.vue
+++ b/src/views/Search.vue
@@ -28,6 +28,16 @@
 									<NoHits />
 								</div>
 							</Transition>
+							<Transition name="fade">
+								<div
+									v-show="!searchResultStore.loading && searchResultStore.numFound > 0"
+									class="page-count"
+								>
+									{{ Number(searchResultStore.start) + 1 }} -
+									{{ searchResultStore.pageNumber * Number(searchResultStore.rowCount) }} {{ $t('search.outOf') }}
+									{{ new Intl.NumberFormat('de-DE').format(searchResultStore.numFound) }}
+								</div>
+							</Transition>
 							<Pagination
 								v-show="searchResultStore.searchResult.length > 0"
 								:items-per-page="Number(searchResultStore.rowCount)"


Reviews:
user: login: jesperlauridsen
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-19T09:54:09Z
user: login: Pbruun
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-19T09:55:16Z

Review comments:
diff_hunk: @@ -148,7 +149,7 @@ export default defineComponent({
 	box-sizing: border-box;
 }
 
-.sort .material-icons {
+.sort > button > .material-icons {
path: src/components/search/Sort.vue
user: login: jesperlauridsen
body: Is there a specific reason why you've chosen this kinda selector?
created_at: 2025-03-19T09:54:09Z
author_association: CONTRIBUTOR
diff_hunk: @@ -148,7 +149,7 @@ export default defineComponent({
 	box-sizing: border-box;
 }
 
-.sort .material-icons {
+.sort > button > .material-icons {
path: src/components/search/Sort.vue
user: login: Pbruun
body: Because we got another .sort .material-icons. The new sort icon, which will be invisible otherwise
created_at: 2025-03-19T09:55:16Z
author_association: CONTRIBUTOR

PR comments:
user: login: jesperlauridsen
created_at: 2025-03-19T09:51:14Z
body: ![image](https://github.com/user-attachments/assets/faefb21e-d6bb-4e47-aaa0-6df52dec4353)
Should the second number not be formatted like the top one?
author_association: CONTRIBUTOR
user: login: jesperlauridsen
created_at: 2025-03-19T09:58:56Z
body: @danielqvistcom 

![image](https://github.com/user-attachments/assets/17ec5c5b-218f-4f75-a8dc-365f32e7852e)


this seems very bloated, because they numbers are almost exactly alike. Could it not be something like "resultatsæt 1-10" or something, without the of XXXXX?
author_association: CONTRIBUTOR
user: login: danielqvistcom
created_at: 2025-03-20T09:22:20Z
body: I think it's a couple of good points. The format's should align. xx.xxxx
And maybe bigger difference - with tekst update like "resultatsæt". I'll talk to UX content later today, and get back with update ASAP.
author_association: CONTRIBUTOR
user: login: danielqvistcom
created_at: 2025-03-25T09:50:38Z
body: Let's go with the simple:
![image](https://github.com/user-attachments/assets/c01cff88-ca99-4892-8109-097d791cd5e9)

author_association: CONTRIBUTOR