[hub][vue] Saved Products Page, remote item card

- also undo unfavouriting items
This commit is contained in:
Prateeksha Singh 2018-08-25 13:18:30 +05:30
parent fe41713974
commit 00175eab8e
8 changed files with 170 additions and 159 deletions

View File

@ -1,24 +1,24 @@
<template>
<div class="col-md-3 col-sm-4 col-xs-6 hub-card-container">
<div class="hub-card is_local"
<div v-if="item.seen" class="col-md-3 col-sm-4 col-xs-6 hub-card-container">
<div class="hub-card"
@click="on_click(item_id)"
>
<div class="hub-card-header flex justify-between">
<div>
<div class="ellipsis" :style="{ width: '85%' }">
<div class="hub-card-title ellipsis bold">{{ title }}</div>
<div class="hub-card-subtitle ellipsis text-muted" v-html='subtitle'></div>
</div>
<i v-if="allow_clear"
class="octicon octicon-x text-extra-muted"
@click="$emit('remove-item', item_id)"
@click.stop="$emit('remove-item', item_id)"
>
</i>
</div>
<div class="hub-card-body">
<img class="hub-card-image" :src="item.image"/>
<div class="hub-card-overlay">
<div class="hub-card-overlay-body">
<div class="hub-card-overlay-button" style="right: 15px; bottom: 15px;">
<div v-if="is_local" class="hub-card-overlay-body">
<div class="hub-card-overlay-button">
<button class="btn btn-default zoom-view">
<i class="octicon octicon-pencil text-muted"></i>
</button>
@ -34,13 +34,28 @@
export default {
name: 'item-card',
props: ['item', 'item_id_fieldname', 'on_click', 'allow_clear'],
props: ['item', 'item_id_fieldname', 'is_local', 'on_click', 'allow_clear'],
computed: {
title() {
return this.item.item_name
const item_name = this.item.item_name || this.item.name;
return strip_html(item_name);
},
subtitle() {
return comment_when(this.item.creation);
const dot_spacer = '<span aria-hidden="true"> · </span>';
if(this.is_local){
return comment_when(this.item.creation);
} else {
let subtitle_items = [comment_when(this.item.creation)];
const rating = this.item.average_rating;
if (rating > 0) {
subtitle_items.push(rating + `<i class='fa fa-fw fa-star-o'></i>`)
}
subtitle_items.push(this.item.company);
return subtitle_items.join(dot_spacer);
}
},
item_id() {
return this.item[this.item_id_fieldname];
@ -58,6 +73,7 @@ export default {
border: 1px solid @border-color;
border-radius: 4px;
overflow: hidden;
cursor: pointer;
&:hover .hub-card-overlay {
display: block;

View File

@ -12,6 +12,7 @@
:key="item[item_id_fieldname]"
:item="item"
:item_id_fieldname="item_id_fieldname"
:is_local="is_local"
:on_click="on_click"
:allow_clear="editable"
@remove-item="$emit('remove-item', item[item_id_fieldname])"
@ -29,6 +30,7 @@ export default {
props: {
items: Array,
item_id_fieldname: String,
is_local: Boolean,
on_click: Function,
editable: Boolean,

View File

@ -23,6 +23,7 @@
<item-cards-container
:items="selected_items"
:item_id_fieldname="item_id_fieldname"
:is_local="true"
:editable="true"
@remove-item="remove_item_from_selection"
@ -44,6 +45,7 @@
<item-cards-container
:items="valid_items"
:item_id_fieldname="item_id_fieldname"
:is_local="true"
:on_click="show_publishing_dialog_for_item"
>
</item-cards-container>

View File

@ -0,0 +1,120 @@
<template>
<div
class="marketplace-page"
:data-page-name="page_name"
>
<h5>{{ page_title }}</h5>
<item-cards-container
:items="items"
:item_id_fieldname="item_id_fieldname"
:on_click="go_to_item_details_page"
:editable="true"
@remove-item="on_item_remove"
:empty_state_message="empty_state_message"
>
</item-cards-container>
</div>
</template>
<script>
import ItemCardsContainer from './ItemCardsContainer.vue';
export default {
name: 'saved-products-page',
data() {
return {
page_name: frappe.get_route()[1],
items: [],
all_items: [],
item_id_fieldname: 'hub_item_code',
// Constants
page_title: __('Saved Products'),
empty_state_message: __(`You haven't favourited any items yet.`)
};
},
components: {
ItemCardsContainer
},
created() {
this.get_items();
},
methods: {
get_items() {
hub.call(
'get_favourite_items_of_seller',
{
hub_seller: hub.settings.company_email
},
'action:item_favourite'
)
.then((items) => {
this.items = items.map(item => {
item.seen = true;
return item;
});
})
},
go_to_item_details_page(hub_item_code) {
frappe.set_route(`marketplace/item/${hub_item_code}`);
},
on_item_remove(hub_item_code) {
const grace_period = 5000;
let reverted = false;
let alert;
const undo_remove = () => {
this.toggle_item(hub_item_code);;
reverted = true;
alert.hide();
return false;
}
alert = frappe.show_alert(__(`<span>${hub_item_code} removed.
<a href="#" class="undo-remove" data-action="undo-remove"><b>Undo</b></a></span>`),
grace_period/1000,
{
'undo-remove': undo_remove.bind(this)
}
);
this.toggle_item(hub_item_code, false);
setTimeout(() => {
if(!reverted) {
this.remove_item_from_saved_products(hub_item_code);
}
}, grace_period);
},
remove_item_from_saved_products(hub_item_code) {
erpnext.hub.trigger('action:item_favourite');
hub.call('remove_item_from_seller_favourites', {
hub_item_code,
hub_seller: hub.settings.company_email
})
.then(() => {
this.get_items();
})
.catch(e => {
console.log(e);
});
},
// By default show
toggle_item(hub_item_code, show=true) {
this.items = this.items.map(item => {
if(item.hub_item_code === hub_item_code) {
item.seen = show;
}
return item;
});
}
}
}
</script>
<style scoped></style>

View File

@ -2,6 +2,7 @@ function get_item_card_html(item) {
if (item.recent_message) {
return get_item_message_card_html(item);
}
const item_name = item.item_name || item.name;
const title = strip_html(item_name);
const img_url = item.image;
@ -14,7 +15,7 @@ function get_item_card_html(item) {
if (rating > 0) {
subtitle.push(rating + `<i class='fa fa-fw fa-star-o'></i>`)
}
subtitle.push(company_name);
let dot_spacer = '<span aria-hidden="true"> · </span>';
@ -51,61 +52,6 @@ function get_item_card_html(item) {
return item_html;
}
function get_local_item_card_html(item) {
const item_name = item.item_name || item.name;
const title = strip_html(item_name);
const img_url = item.image;
const company_name = item.company;
const is_active = item.publish_in_hub;
const id = item.hub_item_code || item.item_code;
// Subtitle
let subtitle = [comment_when(item.creation)];
const rating = item.average_rating;
if (rating > 0) {
subtitle.push(rating + `<i class='fa fa-fw fa-star-o'></i>`)
}
if (company_name) {
subtitle.push(company_name);
}
let dot_spacer = '<span aria-hidden="true"> · </span>';
subtitle = subtitle.join(dot_spacer);
const edit_item_button = `<div class="hub-card-overlay-button" style="right: 15px; bottom: 15px;" data-route="Form/Item/${item.item_name}">
<button class="btn btn-default zoom-view">
<i class="octicon octicon-pencil text-muted"></i>
</button>
</div>`;
const item_html = `
<div class="col-md-3 col-sm-4 col-xs-6 hub-card-container">
<div class="hub-card is-local ${is_active ? 'active' : ''}" data-id="${id}">
<div class="hub-card-header flex">
<div class="ellipsis">
<div class="hub-card-title ellipsis bold">${title}</div>
<div class="hub-card-subtitle ellipsis text-muted">${subtitle}</div>
</div>
<i class="octicon octicon-check text-success"></i>
</div>
<div class="hub-card-body">
<img class="hub-card-image" src="${img_url}" />
<div class="hub-card-overlay">
<div class="hub-card-overlay-body">
${edit_item_button}
</div>
</div>
</div>
</div>
</div>
`;
return item_html;
}
function get_item_message_card_html(item) {
const item_name = item.item_name || item.name;
const title = strip_html(item_name);
@ -138,6 +84,5 @@ function get_item_message_card_html(item) {
}
export {
get_item_card_html,
get_local_item_card_html
get_item_card_html
}

View File

@ -80,8 +80,8 @@ erpnext.hub.Marketplace = class Marketplace {
$nav_group.empty();
const user_specific_items_html = this.registered
? `<li class="hub-sidebar-item" data-route="marketplace/favourites">
${__('Favorites')}
? `<li class="hub-sidebar-item" data-route="marketplace/saved-products">
${__('Saved Products')}
</li>
<li class="hub-sidebar-item text-muted" data-route="marketplace/profile">
${__('Your Profile')}
@ -196,8 +196,8 @@ erpnext.hub.Marketplace = class Marketplace {
}
// registered seller routes
if (route[1] === 'favourites' && !this.subpages.favourites) {
this.subpages.favourites = new erpnext.hub.Favourites(this.$body);
if (route[1] === 'saved-products' && !this.subpages['saved-products']) {
this.subpages['saved-products'] = new erpnext.hub.SavedProducts(this.$body);
}
if (route[1] === 'profile' && !this.subpages.profile) {
@ -221,7 +221,7 @@ erpnext.hub.Marketplace = class Marketplace {
}
// dont allow unregistered users to access registered routes
const registered_routes = ['favourites', 'profile', 'publish', 'my-products', 'messages'];
const registered_routes = ['saved-products', 'profile', 'publish', 'my-products', 'messages'];
if (!hub.settings.registered && registered_routes.includes(route[1])) {
frappe.set_route('marketplace', 'home');
return;

View File

@ -1,82 +1,20 @@
import SubPage from './subpage';
import { get_item_card_container_html } from '../components/items_container';
import SavedProductsPage from '../components/SavedProductsPage.vue';
import Vue from 'vue/dist/vue.js';
erpnext.hub.Favourites = class Favourites extends SubPage {
make_wrapper() {
super.make_wrapper();
this.bind_events();
erpnext.hub.SavedProducts = class {
constructor(parent) {
this.$wrapper = $(`<div id="vue-area-saved">`).appendTo($(parent));
new Vue({
render: h => h(SavedProductsPage)
}).$mount('#vue-area-saved');
}
bind_events() {
this.$wrapper.on('click', '.hub-card', (e) => {
const $target = $(e.target);
if($target.hasClass('octicon-x')) {
e.stopPropagation();
const hub_item_code = $target.attr('data-hub-item-code');
this.on_item_remove(hub_item_code);
}
});
show() {
$('[data-page-name="saved-products"]').show();
}
refresh() {
this.get_favourites()
.then(items => {
this.render(items);
});
hide() {
$('[data-page-name="saved-products"]').hide();
}
get_favourites() {
return hub.call('get_favourite_items_of_seller', {
hub_seller: hub.settings.company_email
}, 'action:item_favourite');
}
render(items) {
this.$wrapper.find('.hub-items-container').empty();
const html = get_item_card_container_html(items, __('Favourites'));
this.$wrapper.html(html);
this.$wrapper.find('.hub-card').addClass('closable');
if (!items.length) {
this.render_empty_state();
}
}
render_empty_state() {
this.$wrapper.find('.hub-items-container').append(`
<div class="col-md-12">${__("You don't have any favourites yet.")}</div>
`)
}
on_item_remove(hub_item_code, $hub_card = '') {
const $message = $(__(`<span>${hub_item_code} removed.
<a href="#" data-action="undo-remove"><b>Undo</b></a></span>`));
frappe.show_alert($message);
$hub_card = this.$wrapper.find(`.hub-card[data-hub-item-code="${hub_item_code}"]`);
$hub_card.hide();
const grace_period = 5000;
setTimeout(() => {
this.remove_item(hub_item_code, $hub_card);
}, grace_period);
}
remove_item(hub_item_code, $hub_card) {
hub.call('remove_item_from_seller_favourites', {
hub_item_code,
hub_seller: hub.settings.company_email
})
.then(() => {
$hub_card.remove();
})
.catch(e => {
console.log(e);
});
}
undo_remove(hub_item_code) { }
}

View File

@ -44,18 +44,6 @@ body[data-route^="marketplace/"] {
&:hover .hub-card-overlay {
display: block;
}
.octicon-x {
display: none;
margin-left: 10px;
font-size: 20px;
}
}
.hub-card.closable {
.octicon-x {
display: block;
}
}
.hub-card.is-local {