Add projects search results component [SCI-10469]

This commit is contained in:
Anton 2024-03-22 16:37:59 +01:00
parent 30318da3f1
commit 478da52272
7 changed files with 226 additions and 30 deletions

View file

@ -11,37 +11,56 @@ class SearchController < ApplicationController
format.json do
redirect_to new_search_path unless @search_query
@search_id = params[:search_id] ? params[:search_id] : generate_search_id
case params[:group]
when 'projects'
@project_search_count = fetch_cached_count Project
search_projects
if params[:preview] == 'true'
results = @project_results.limit(4)
else
results = @project_results.page(params[:page]).per(Constants::SEARCH_LIMIT)
end
count_search_results
search_projects if @search_category == :projects
search_project_folders if @search_category == :project_folders
search_experiments if @search_category == :experiments
search_modules if @search_category == :modules
search_results if @search_category == :results
search_tags if @search_category == :tags
search_reports if @search_category == :reports
search_protocols if @search_category == :protocols
search_steps if @search_category == :steps
search_checklists if @search_category == :checklists
if @search_category == :repositories && params[:repository]
search_repository
render json: results,
each_serializer: GlobalSearch::ProjectSerializer,
meta: {
total: @search_count,
next_page: (results.next_page if results.respond_to?(:next_page)),
}
return
end
search_assets if @search_category == :assets
search_tables if @search_category == :tables
search_comments if @search_category == :comments
@search_pages = (@search_count.to_f / Constants::SEARCH_LIMIT.to_f).ceil
@start_page = @search_page - 2
@start_page = 1 if @start_page < 1
@end_page = @start_page + 4
#@search_id = params[:search_id] ? params[:search_id] : generate_search_id
#
#count_search_results
#
#search_projects if @search_category == :projects
#search_project_folders if @search_category == :project_folders
#search_experiments if @search_category == :experiments
#search_modules if @search_category == :modules
#search_results if @search_category == :results
#search_tags if @search_category == :tags
#search_reports if @search_category == :reports
#search_protocols if @search_category == :protocols
#search_steps if @search_category == :steps
#search_checklists if @search_category == :checklists
#if @search_category == :repositories && params[:repository]
# search_repository
#end
#search_assets if @search_category == :assets
#search_tables if @search_category == :tables
#search_comments if @search_category == :comments
if @end_page > @search_pages
@end_page = @search_pages
@start_page = @end_page - 4
@start_page = 1 if @start_page < 1
end
#@search_pages = (@search_count.to_f / Constants::SEARCH_LIMIT.to_f).ceil
#@start_page = @search_page - 2
#@start_page = 1 if @start_page < 1
#@end_page = @start_page + 4
#if @end_page > @search_pages
# @end_page = @search_pages
# @start_page = @end_page - 4
# @start_page = 1 if @start_page < 1
#end
end
end
end

View file

@ -7,9 +7,9 @@
</h1>
</div>
</div>
<div class="bg-white rounded p-4 flex gap-2.5 items-center mb-4">
<div class="bg-white rounded p-4 flex gap-2.5 items-center mb-4 sticky top-0">
<div class="left-icon sci-input-container-v2 w-72 input-sm" :title="i18n.t('nav.search')">
<input ref="searchField" type="text" class="!pr-9" v-model="localQuery" :placeholder="i18n.t('nav.search')" @keyup.enter="saveQuery"/>
<input ref="searchField" type="text" class="!pr-9" :value="localQuery" @change="changeQuery" :placeholder="i18n.t('nav.search')"/>
<i class="sn-icon sn-icon-search"></i>
<i v-if="localQuery.length > 0" class="sn-icon cursor-pointer sn-icon-close absolute right-0 -top-0.5" @click="localQuery = ''"></i>
</div>
@ -43,6 +43,8 @@
v-if="activeGroup === group || !activeGroup"
:selected="activeGroup === group"
:query="localQuery"
:searchUrl="searchUrl"
@selectGroup="setActiveGroup"
/>
</template>
</div>
@ -67,6 +69,10 @@ export default {
query: {
type: String,
required: true
},
searchUrl: {
type: String,
required: true
}
},
components: {
@ -108,6 +114,9 @@ export default {
} else {
this.activeGroup = group;
}
},
changeQuery(event) {
this.localQuery = event.target.value;
}
}
};

View file

@ -3,12 +3,57 @@
<h2 class="flex items-center gap-2 mt-0 mb-4">
<i class="sn-icon sn-icon-projects"></i>
{{ i18n.t('search.index.projects') }}
[{{ total }}]
</h2>
<div>
<div class="grid grid-cols-[auto_80px_auto_auto_auto] items-center">
<template v-for="row in preparedResults" :key="row.id">
<a :href="row.attributes.url" class="h-full py-2 px-4 overflow-hidden font-bold border-0 border-b border-solid border-sn-light-grey">
<StringWithEllipsis class="w-full" :text="row.attributes.name"></StringWithEllipsis>
</a>
<div class="h-full py-2 px-4 flex items-center gap-1 text-xs border-0 border-b border-solid border-sn-light-grey">
<b class="shrink-0">{{ i18n.t('search.index.id') }}:</b>
<span class="shrink-0">{{ row.attributes.code }}</span>
</div>
<div class="h-full py-2 px-4 flex items-center gap-1 text-xs border-0 border-b border-solid border-sn-light-grey">
<b class="shrink-0">{{ i18n.t('search.index.created_at') }}:</b>
<span class="shrink-0">{{ row.attributes.created_at }}</span>
</div>
<div class="h-full py-2 px-4 grid grid-cols-[auto_1fr] items-center gap-1 text-xs border-0 border-b border-solid border-sn-light-grey">
<b class="shrink-0">{{ i18n.t('search.index.team') }}:</b>
<a :href="row.attributes.team.url" class="shrink-0 overflow-hidden">
<StringWithEllipsis class="w-full" :text="row.attributes.team.name"></StringWithEllipsis>
</a>
</div>
<div class="h-full py-2 px-4 border-0 border-b border-solid border-sn-light-grey">
<template v-if="row.attributes.folder">
<div class="grid grid-cols-[auto_1fr] items-center gap-1 text-xs w-full">
<b class="shrink-0">{{ i18n.t('search.index.folder') }}:</b>
<a :href="row.attributes.folder.url" class="shrink-0 overflow-hidden">
<StringWithEllipsis class="w-full" :text="row.attributes.folder.name"></StringWithEllipsis>
</a>
</div>
</template>
</div>
</template>
</div>
<div v-if="!selected && total > 4" class="mt-4">
<button class="btn btn-light" @click="$emit('selectGroup', 'ProjectsComponent')">View all</button>
</div>
</div>
</div>
</template>
<script>
import searchMixin from './search_mixin';
export default {
name: 'ProjectsComponent'
name: 'ProjectsComponent',
mixins: [searchMixin],
data() {
return {
group: 'projects'
};
}
};
</script>

View file

@ -0,0 +1,87 @@
import axios from '../../../packs/custom_axios.js';
import StringWithEllipsis from '../../shared/string_with_ellipsis.vue';
export default {
props: {
searchUrl: String,
query: String,
selected: Boolean
},
components: {
StringWithEllipsis
},
data() {
return {
results: [],
total: 0,
loading: false,
page: 1,
fullDataLoaded: false
};
},
watch: {
selected() {
if (this.selected) {
if (!this.fullDataLoaded) {
this.results = [];
this.loadData();
}
}
},
query() {
this.results = [];
this.page = 1;
this.fullDataLoaded = false;
this.loadData();
}
},
mounted() {
this.loadData();
window.addEventListener('scroll', this.handleScroll);
},
unmounted() {
window.removeEventListener('scroll', this.handleScroll);
},
computed: {
preparedResults() {
if (this.selected) {
return this.results;
}
return this.results.slice(0, 4);
}
},
methods: {
handleScroll() {
if (this.loading) return;
if ((window.innerHeight + window.scrollY) >= document.body.offsetHeight) {
if (this.results.length < this.total) {
this.loadData();
}
}
},
loadData() {
if (this.loading && this.page) return;
this.loading = true;
axios.get(this.searchUrl, {
params: {
q: this.query,
group: this.group,
preview: !this.selected,
page: this.page
}
})
.then((response) => {
if (this.selected) this.fullDataLoaded = true;
this.results = this.results.concat(response.data.data);
this.total = response.data.meta.total;
this.loading = false;
this.page = response.data.meta.next_page;
})
.finally(() => {
this.loading = false;
});
}
}
};

View file

@ -0,0 +1,31 @@
# frozen_string_literal: true
class GlobalSearch::ProjectSerializer < ActiveModel::Serializer
include Rails.application.routes.url_helpers
attributes :id, :name, :code, :created_at, :team, :folder, :archived, :url
def team
{
name: object.team.name,
url: projects_path(team: object.team)
}
end
def created_at
I18n.l(object.created_at, format: :full_date)
end
def url
project_path(object)
end
def folder
if object.project_folder
{
name: object.project_folder.name,
url: project_folder_path(object.project_folder)
}
end
end
end

View file

@ -3,6 +3,7 @@
<div id="GlobalSearch" class="contents">
<global_search
:query="'<%= @display_query %>'"
:search-url="'<%= search_path(format: :json) %>'"
/>
</div>

View file

@ -455,6 +455,10 @@ en:
reports: "Reports"
more_search_options: "More search options"
clear_filters: "Clear filters"
id: "ID"
created_at: "Created on"
team: "Team"
folder: "Folder"
comments:
save_changes: "Save changes"
empty_state: