mirror of
https://github.com/scinote-eln/scinote-web.git
synced 2025-12-10 14:16:28 +08:00
Merge pull request #8490 from aignatov-bio/ai-sci-11854-add-favorites-widget
Add favorites widget [SCI-11854]
This commit is contained in:
commit
5a0ebdef67
10 changed files with 215 additions and 5 deletions
|
|
@ -27,6 +27,13 @@ var DasboardCurrentTasksWidget = (function() {
|
|||
return values;
|
||||
}
|
||||
|
||||
function initFavorites() {
|
||||
$('.current-tasks-widget .actions-container').addClass('hidden');
|
||||
$('.current-tasks-list-wrapper').addClass('hidden');
|
||||
$('#favoritesWidget').removeClass('hidden');
|
||||
window.favoritesWidget.$refs.widget.resetFavorites();
|
||||
}
|
||||
|
||||
function resetMarkAppliedFilters() {
|
||||
$('.filter-container').removeClass('filters-applied');
|
||||
}
|
||||
|
|
@ -84,6 +91,10 @@ var DasboardCurrentTasksWidget = (function() {
|
|||
$('.current-tasks-navbar .navbar-link').removeClass('active');
|
||||
$('.current-tasks-navbar').find(`[data-mode='${parsedFilterState.mode}']`).addClass('active');
|
||||
markAppliedFilters();
|
||||
|
||||
if (parsedFilterState.mode === 'favorites') {
|
||||
initFavorites();
|
||||
}
|
||||
} catch (e) {
|
||||
dropdownSelector.selectValues(statusFilter, getDefaultStatusValues());
|
||||
resetMarkAppliedFilters();
|
||||
|
|
@ -235,7 +246,14 @@ var DasboardCurrentTasksWidget = (function() {
|
|||
$('.current-tasks-navbar .navbar-link').on('click', function() {
|
||||
$(this).parent().find('.navbar-link').removeClass('active');
|
||||
$(this).addClass('active');
|
||||
loadCurrentTasksList(true);
|
||||
if (this.dataset.mode === 'favorites') {
|
||||
initFavorites();
|
||||
} else {
|
||||
$('.current-tasks-widget .actions-container').removeClass('hidden');
|
||||
$('.current-tasks-list-wrapper').removeClass('hidden');
|
||||
$('#favoritesWidget').addClass('hidden');
|
||||
loadCurrentTasksList(true);
|
||||
}
|
||||
filterStateSave();
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,11 @@ module Dashboard
|
|||
render json: { data: tasks_list, next_page: tasks.next_page }
|
||||
end
|
||||
|
||||
def favorites
|
||||
favorites = current_user.favorites.includes(:item).order(created_at: :desc).page(params[:page]).per(Constants::INFINITE_SCROLL_LIMIT)
|
||||
render json: favorites, each_serializer: FavoriteSerializer, meta: pagination_dict(favorites)
|
||||
end
|
||||
|
||||
def project_filter
|
||||
projects = current_team.projects
|
||||
.where(archived: false)
|
||||
|
|
|
|||
8
app/javascript/packs/vue/favorites_widget.js
Normal file
8
app/javascript/packs/vue/favorites_widget.js
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
import { createApp } from 'vue/dist/vue.esm-bundler.js';
|
||||
import FavoritesWidget from '../../vue/favorites/widget.vue';
|
||||
import { mountWithTurbolinks } from './helpers/turbolinks.js';
|
||||
|
||||
const app = createApp();
|
||||
app.component('FavoritesWidget', FavoritesWidget);
|
||||
app.config.globalProperties.i18n = window.I18n;
|
||||
window.favoritesWidget = mountWithTurbolinks(app, '#favoritesWidget');
|
||||
95
app/javascript/vue/favorites/widget.vue
Normal file
95
app/javascript/vue/favorites/widget.vue
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
<template>
|
||||
<div ref="scrollContainer" class="h-full overflow-y-auto">
|
||||
<div v-if="favorites.length > 0" v-for="favorite in favorites" class="px-4">
|
||||
<a :href="favorite.attributes.url" class="flex text-black items-center gap-2 py-2 hover:no-underline">
|
||||
<i class="sn-icon sn-icon-star-filled text-sn-alert-brittlebush" style="font-size: 32px !important"></i>
|
||||
<div>
|
||||
<div class="flex items-center gap-2 text-sn-grey">
|
||||
<template v-for="(breadcrumb, index) in favorite.attributes.breadcrumbs" :key="index" >
|
||||
<div v-if="index > 0">
|
||||
<span v-if="index > 1"> / </span>
|
||||
<span v-if="index + 1 < favorite.attributes.breadcrumbs.length" class="text-xs">
|
||||
{{ breadcrumb.name }}
|
||||
</span>
|
||||
<span v-else class="text-xs">
|
||||
{{ favorite.attributes.code }}
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div class="font-bold text-base">{{ favorite.attributes.name }}</div>
|
||||
</div>
|
||||
<div
|
||||
class="ml-auto rounded px-1.5 py-1 mt-4"
|
||||
:class="{
|
||||
'text-black border': favorite.attributes.status.light_color,
|
||||
'text-white': !favorite.attributes.status.light_color,
|
||||
}"
|
||||
:style="{ backgroundColor: favorite.attributes.status.color }"
|
||||
>
|
||||
{{ favorite.attributes.status.name }}
|
||||
</div>
|
||||
</a>
|
||||
<hr class="my-0">
|
||||
</div>
|
||||
<h2 v-else-if="!loading" class="ml-6">
|
||||
{{ i18n.t('dashboard.current_tasks.no_tasks.favorites') }}
|
||||
</h2>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import axios from '../../packs/custom_axios.js';
|
||||
|
||||
export default {
|
||||
name: 'FavoritesWidget',
|
||||
props: {
|
||||
favoritesUrl: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
favorites: [],
|
||||
page: 1,
|
||||
loading: true
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
const activeTab = document.querySelector('.current-tasks-navbar .navbar-link.active');
|
||||
if (activeTab.dataset.mode === 'favorites') {
|
||||
this.resetFavorites();
|
||||
}
|
||||
|
||||
this.$refs.scrollContainer.addEventListener('scroll', () => {
|
||||
if (this.$refs.scrollContainer.scrollTop + this.$refs.scrollContainer.clientHeight >= this.$refs.scrollContainer.scrollHeight - 100) {
|
||||
this.loadFavorites();
|
||||
}
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
resetFavorites() {
|
||||
this.favorites = [];
|
||||
this.page = 1;
|
||||
this.loading = false;
|
||||
this.loadFavorites();
|
||||
},
|
||||
loadFavorites() {
|
||||
if (this.loading || !this.page) return;
|
||||
|
||||
this.loading = true;
|
||||
axios.get(this.favoritesUrl, { params: { page: this.page } })
|
||||
.then((response) => {
|
||||
this.favorites = this.favorites.concat(response.data.data);
|
||||
if (response.data.data.length > 0) {
|
||||
this.page += 1;
|
||||
} else {
|
||||
this.page = null;
|
||||
}
|
||||
this.loading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
65
app/serializers/favorite_serializer.rb
Normal file
65
app/serializers/favorite_serializer.rb
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class FavoriteSerializer < ActiveModel::Serializer
|
||||
include Canaid::Helpers::PermissionsHelper
|
||||
include Rails.application.routes.url_helpers
|
||||
include BreadcrumbsHelper
|
||||
|
||||
attributes :id, :name, :code, :status, :breadcrumbs, :url
|
||||
|
||||
def code
|
||||
object.item.code
|
||||
end
|
||||
|
||||
def name
|
||||
object.item.name
|
||||
end
|
||||
|
||||
def breadcrumbs
|
||||
subject = object.item
|
||||
generate_breadcrumbs(subject, []) if subject
|
||||
end
|
||||
|
||||
def url
|
||||
case object.item_type
|
||||
when 'Project'
|
||||
experiments_path(project_id: object.item)
|
||||
when 'Experiment'
|
||||
my_modules_experiment_path(object.item)
|
||||
when 'MyModule'
|
||||
protocols_my_module_path(object.item)
|
||||
end
|
||||
end
|
||||
|
||||
def status
|
||||
case object.item_type
|
||||
when 'MyModule'
|
||||
{
|
||||
name: object.item.my_module_status.name,
|
||||
color: object.item.my_module_status.color,
|
||||
light_color: object.item.my_module_status.light_color?
|
||||
}
|
||||
else
|
||||
case object.item.status
|
||||
when :not_started
|
||||
{
|
||||
name: I18n.t('projects.index.status.not_started'),
|
||||
color: Constants::STATUS_COLORS[:not_started],
|
||||
light_color: true
|
||||
}
|
||||
when :started
|
||||
{
|
||||
name: I18n.t('projects.index.status.started'),
|
||||
color: Constants::STATUS_COLORS[:started],
|
||||
light_color: false
|
||||
}
|
||||
when :completed
|
||||
{
|
||||
name: I18n.t('projects.index.status.completed'),
|
||||
color: Constants::STATUS_COLORS[:completed],
|
||||
light_color: false
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -69,7 +69,8 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sci-secondary-navbar current-tasks-navbar">
|
||||
<div class="sci-secondary-navbar current-tasks-navbar ml-auto">
|
||||
<span class="navbar-link" data-mode="favorites"><%= t("dashboard.current_tasks.navbar.favorites") %></span>
|
||||
<span class="navbar-link active" data-mode="assigned"><%= t("dashboard.current_tasks.navbar.assigned") %></span>
|
||||
<span class="navbar-link" data-mode="team"><%= t("dashboard.current_tasks.navbar.all") %></span>
|
||||
</div>
|
||||
|
|
@ -81,6 +82,12 @@
|
|||
data-tasks-list-url="<%= dashboard_current_tasks_path %>">
|
||||
</div>
|
||||
</div>
|
||||
<div id="favoritesWidget" class="hidden h-full">
|
||||
<favorites-widget
|
||||
ref="widget"
|
||||
favorites-url="<%= favorites_dashboard_current_tasks_path %>"
|
||||
></favorites-widget>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -97,3 +104,5 @@
|
|||
<p class="widget-placeholder-title"><%= I18n.t('dashboard.current_tasks.no_tasks.search_result.title') %></p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<%= javascript_include_tag 'vue_favorites_widget' %>
|
||||
|
|
|
|||
|
|
@ -443,6 +443,12 @@ class Constants
|
|||
FAST_STATUS_POLLING_INTERVAL = 5000
|
||||
SLOW_STATUS_POLLING_INTERVAL = 10000
|
||||
|
||||
STATUS_COLORS = {
|
||||
not_started: '#fffff',
|
||||
started: '#3070ED',
|
||||
completed: '#5EC66F'
|
||||
}
|
||||
|
||||
# Interval time for polling asset changes when editing with SciNote Edit
|
||||
ASSET_POLLING_INTERVAL = 5000
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
en:
|
||||
dashboard:
|
||||
current_tasks:
|
||||
title: "Current tasks"
|
||||
title: "Current work"
|
||||
search: "Search tasks"
|
||||
navbar:
|
||||
favorites: "Favorites"
|
||||
assigned: "Assigned to me"
|
||||
all: "All"
|
||||
filter:
|
||||
|
|
@ -32,6 +33,7 @@ en:
|
|||
apply: "Apply"
|
||||
show_results: "Show results"
|
||||
no_tasks:
|
||||
favorites: "Projects, experiments and tasks you mark as favorites appear here."
|
||||
assigned_tasks:
|
||||
title: "There are no tasks assigned to you at the moment."
|
||||
description: "Use the “+ New task” button above to create a new one, or assign yourself to an existing task."
|
||||
|
|
@ -83,7 +85,7 @@ en:
|
|||
new_project: "Create \"%{name}\" Project"
|
||||
new_experiment: "Create \"%{name}\" Experiment"
|
||||
recent_work:
|
||||
title: "Recent work"
|
||||
title: "My recent activity"
|
||||
no_results:
|
||||
title: "You have not worked on anything recently."
|
||||
description: "All tasks, projects, inventories protocols and reports, you last worked on will appear here, when you make some changes."
|
||||
|
|
|
|||
|
|
@ -301,6 +301,7 @@ Rails.application.routes.draw do
|
|||
resource :current_tasks, module: 'dashboard', only: :show do
|
||||
get :project_filter
|
||||
get :experiment_filter
|
||||
get :favorites
|
||||
end
|
||||
|
||||
namespace :quick_start, module: :dashboard, controller: :quick_start do
|
||||
|
|
|
|||
|
|
@ -72,7 +72,8 @@ const entryList = {
|
|||
vue_form_table: './app/javascript/packs/vue/forms_table.js',
|
||||
vue_my_module_assigned_items: './app/javascript/packs/vue/my_module_assigned_items.js',
|
||||
vue_design_system_inputs: './app/javascript/packs/vue/design_system/inputs.js',
|
||||
vue_design_system_table: './app/javascript/packs/vue/design_system/table.js'
|
||||
vue_design_system_table: './app/javascript/packs/vue/design_system/table.js',
|
||||
vue_favorites_widget: './app/javascript/packs/vue/favorites_widget.js'
|
||||
};
|
||||
|
||||
// Engine pack loading based on https://github.com/rails/webpacker/issues/348#issuecomment-635480949
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue