[hub][vue] Publish Page
This commit is contained in:
parent
67e0374261
commit
40f7c4663a
@ -39,6 +39,7 @@ def map_fields(items):
|
|||||||
|
|
||||||
item['doctype'] = 'Hub Item'
|
item['doctype'] = 'Hub Item'
|
||||||
item['hub_seller'] = hub_seller
|
item['hub_seller'] = hub_seller
|
||||||
|
item.pop('attachments', None)
|
||||||
|
|
||||||
return items
|
return items
|
||||||
|
|
||||||
@ -71,24 +72,19 @@ def publish_selected_items(items_to_publish):
|
|||||||
if not len(items_to_publish):
|
if not len(items_to_publish):
|
||||||
frappe.throw('No items to publish')
|
frappe.throw('No items to publish')
|
||||||
|
|
||||||
publishing_items = []
|
for item in items_to_publish:
|
||||||
|
item_code = item.get('item_code')
|
||||||
for item_additional_info in items_to_publish:
|
|
||||||
item_code = item_additional_info.get('item_code')
|
|
||||||
frappe.db.set_value('Item', item_code, 'publish_in_hub', 1)
|
frappe.db.set_value('Item', item_code, 'publish_in_hub', 1)
|
||||||
|
|
||||||
frappe.get_doc({
|
frappe.get_doc({
|
||||||
'doctype': 'Hub Tracked Item',
|
'doctype': 'Hub Tracked Item',
|
||||||
'item_code': item_code,
|
'item_code': item_code,
|
||||||
'hub_category': item_additional_info.get('hub_category'),
|
'hub_category': item.get('hub_category'),
|
||||||
'image_list': item_additional_info.get('image_list')
|
'image_list': item.get('image_list')
|
||||||
}).insert()
|
}).insert()
|
||||||
|
|
||||||
item_data = frappe.get_doc("Item", item_code).as_dict().update(item_additional_info)
|
|
||||||
publishing_items.append(item_data)
|
|
||||||
|
|
||||||
|
items = map_fields(items_to_publish)
|
||||||
items = map_fields(publishing_items)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
item_sync_preprocess()
|
item_sync_preprocess()
|
||||||
|
|||||||
@ -25,7 +25,7 @@ export default {
|
|||||||
|
|
||||||
.empty-state {
|
.empty-state {
|
||||||
height: 500px;
|
height: 500px;
|
||||||
margin: 15px 0px;
|
margin: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.empty-state.bordered {
|
.empty-state.bordered {
|
||||||
|
|||||||
@ -1,11 +1,18 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="col-md-3 col-sm-4 col-xs-6 hub-card-container">
|
<div class="col-md-3 col-sm-4 col-xs-6 hub-card-container">
|
||||||
<div class="hub-card is_local">
|
<div class="hub-card is_local"
|
||||||
<div class="hub-card-header flex">
|
@click="on_click(item_id)"
|
||||||
|
>
|
||||||
|
<div class="hub-card-header flex justify-between">
|
||||||
<div>
|
<div>
|
||||||
<div class="hub-card-title ellipsis bold">{{ title }}</div>
|
<div class="hub-card-title ellipsis bold">{{ title }}</div>
|
||||||
<div class="hub-card-subtitle ellipsis text-muted" v-html='subtitle'></div>
|
<div class="hub-card-subtitle ellipsis text-muted" v-html='subtitle'></div>
|
||||||
</div>
|
</div>
|
||||||
|
<i v-if="allow_clear"
|
||||||
|
class="octicon octicon-x text-extra-muted"
|
||||||
|
@click="$emit('remove-item', item_id)"
|
||||||
|
>
|
||||||
|
</i>
|
||||||
</div>
|
</div>
|
||||||
<div class="hub-card-body">
|
<div class="hub-card-body">
|
||||||
<img class="hub-card-image" :src="item.image"/>
|
<img class="hub-card-image" :src="item.image"/>
|
||||||
@ -27,16 +34,93 @@
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'item-card',
|
name: 'item-card',
|
||||||
props: ['item', 'is_local'],
|
props: ['item', 'item_id_fieldname', 'on_click', 'allow_clear'],
|
||||||
computed: {
|
computed: {
|
||||||
title() {
|
title() {
|
||||||
return this.item.item_name
|
return this.item.item_name
|
||||||
},
|
},
|
||||||
subtitle() {
|
subtitle() {
|
||||||
return comment_when(this.item.creation);
|
return comment_when(this.item.creation);
|
||||||
|
},
|
||||||
|
item_id() {
|
||||||
|
return this.item[this.item_id_fieldname];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped></style>
|
<style lang="less" scoped>
|
||||||
|
@import "../../../../../../frappe/frappe/public/less/variables.less";
|
||||||
|
|
||||||
|
.hub-card {
|
||||||
|
margin-bottom: 25px;
|
||||||
|
position: relative;
|
||||||
|
border: 1px solid @border-color;
|
||||||
|
border-radius: 4px;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
&:hover .hub-card-overlay {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.octicon-x {
|
||||||
|
display: block;
|
||||||
|
font-size: 20px;
|
||||||
|
margin-left: 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.hub-card.closable {
|
||||||
|
.octicon-x {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.hub-card.is-local {
|
||||||
|
&.active {
|
||||||
|
.hub-card-header {
|
||||||
|
background-color: #f4ffe5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.hub-card-header {
|
||||||
|
position: relative;
|
||||||
|
padding: 12px 15px;
|
||||||
|
height: 60px;
|
||||||
|
border-bottom: 1px solid @border-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hub-card-body {
|
||||||
|
position: relative;
|
||||||
|
height: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hub-card-overlay {
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-color: rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hub-card-overlay-body {
|
||||||
|
position: relative;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hub-card-overlay-button {
|
||||||
|
position: absolute;
|
||||||
|
right: 15px;
|
||||||
|
bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hub-card-image {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: contain;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div class="item-cards-container">
|
||||||
<empty-state
|
<empty-state
|
||||||
v-if="items.length === 0"
|
v-if="items.length === 0"
|
||||||
:message="empty_state_message"
|
:message="empty_state_message"
|
||||||
@ -9,9 +9,12 @@
|
|||||||
</empty-state>
|
</empty-state>
|
||||||
<item-card
|
<item-card
|
||||||
v-for="item in items"
|
v-for="item in items"
|
||||||
:key="item[item_id]"
|
:key="item[item_id_fieldname]"
|
||||||
:item="item"
|
:item="item"
|
||||||
:is_local="is_local"
|
:item_id_fieldname="item_id_fieldname"
|
||||||
|
:on_click="on_click"
|
||||||
|
:allow_clear="editable"
|
||||||
|
@remove-item="$emit('remove-item', item[item_id_fieldname])"
|
||||||
>
|
>
|
||||||
</item-card>
|
</item-card>
|
||||||
</div>
|
</div>
|
||||||
@ -24,28 +27,31 @@ import EmptyState from './EmptyState.vue';
|
|||||||
export default {
|
export default {
|
||||||
name: 'item-cards-container',
|
name: 'item-cards-container',
|
||||||
props: {
|
props: {
|
||||||
'items': Array,
|
items: Array,
|
||||||
'is_local': Boolean,
|
item_id_fieldname: String,
|
||||||
|
on_click: Function,
|
||||||
|
editable: Boolean,
|
||||||
|
|
||||||
'empty_state_message': String,
|
empty_state_message: String,
|
||||||
'empty_state_height': Number,
|
empty_state_height: Number,
|
||||||
'empty_state_bordered': Boolean
|
empty_state_bordered: Boolean
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
ItemCard,
|
ItemCard,
|
||||||
EmptyState
|
EmptyState
|
||||||
},
|
},
|
||||||
computed: {
|
|
||||||
item_id() {
|
|
||||||
return this.is_local ? 'item_code' : 'hub_item_code';
|
|
||||||
}
|
|
||||||
},
|
|
||||||
watch: {
|
watch: {
|
||||||
items() {
|
items() {
|
||||||
|
// TODO: handling doesn't work
|
||||||
frappe.dom.handle_broken_images($(this.$el));
|
frappe.dom.handle_broken_images($(this.$el));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped></style>
|
<style scoped>
|
||||||
|
.item-cards-container {
|
||||||
|
margin: 0 -15px;
|
||||||
|
overflow: overlay;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@ -13,14 +13,19 @@
|
|||||||
<h5>{{ page_title }}</h5>
|
<h5>{{ page_title }}</h5>
|
||||||
|
|
||||||
<button class="btn btn-primary btn-sm publish-items"
|
<button class="btn btn-primary btn-sm publish-items"
|
||||||
:disabled="no_selected_items">
|
:disabled="no_selected_items"
|
||||||
|
@click="publish_selected_items"
|
||||||
|
>
|
||||||
<span>{{ publish_button_text }}</span>
|
<span>{{ publish_button_text }}</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<item-cards-container
|
<item-cards-container
|
||||||
:items="selected_items"
|
:items="selected_items"
|
||||||
:is_local="true"
|
:item_id_fieldname="item_id_fieldname"
|
||||||
|
:editable="true"
|
||||||
|
@remove-item="remove_item_from_selection"
|
||||||
|
|
||||||
:empty_state_message="empty_state_message"
|
:empty_state_message="empty_state_message"
|
||||||
:empty_state_bordered="true"
|
:empty_state_bordered="true"
|
||||||
:empty_state_height="80"
|
:empty_state_height="80"
|
||||||
@ -38,7 +43,8 @@
|
|||||||
|
|
||||||
<item-cards-container
|
<item-cards-container
|
||||||
:items="valid_items"
|
:items="valid_items"
|
||||||
:is_local="true"
|
:item_id_fieldname="item_id_fieldname"
|
||||||
|
:on_click="show_publishing_dialog_for_item"
|
||||||
>
|
>
|
||||||
</item-cards-container>
|
</item-cards-container>
|
||||||
</div>
|
</div>
|
||||||
@ -48,6 +54,7 @@
|
|||||||
import SearchInput from './SearchInput.vue';
|
import SearchInput from './SearchInput.vue';
|
||||||
import ItemCardsContainer from './ItemCardsContainer.vue';
|
import ItemCardsContainer from './ItemCardsContainer.vue';
|
||||||
import NotificationMessage from './NotificationMessage.vue';
|
import NotificationMessage from './NotificationMessage.vue';
|
||||||
|
import { ItemPublishDialog } from './item_publish_dialog';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'publish-page',
|
name: 'publish-page',
|
||||||
@ -56,7 +63,9 @@ export default {
|
|||||||
page_name: frappe.get_route()[1],
|
page_name: frappe.get_route()[1],
|
||||||
valid_items: [],
|
valid_items: [],
|
||||||
selected_items: [],
|
selected_items: [],
|
||||||
|
items_data_to_publish: {},
|
||||||
search_value: '',
|
search_value: '',
|
||||||
|
item_id_fieldname: 'item_code',
|
||||||
|
|
||||||
// Constants
|
// Constants
|
||||||
page_title: __('Publish Products'),
|
page_title: __('Publish Products'),
|
||||||
@ -95,10 +104,20 @@ export default {
|
|||||||
text = `Publish ${number} Products`;
|
text = `Publish ${number} Products`;
|
||||||
}
|
}
|
||||||
return __(text);
|
return __(text);
|
||||||
}
|
},
|
||||||
|
|
||||||
|
items_dict() {
|
||||||
|
let items_dict = {};
|
||||||
|
this.valid_items.map(item => {
|
||||||
|
items_dict[item[this.item_id_fieldname]] = item
|
||||||
|
})
|
||||||
|
|
||||||
|
return items_dict;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
this.get_valid_items();
|
this.get_valid_items();
|
||||||
|
this.make_publishing_dialog();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
get_valid_items() {
|
get_valid_items() {
|
||||||
@ -113,8 +132,86 @@ export default {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
publish_selected_items() {
|
||||||
|
frappe.call(
|
||||||
|
'erpnext.hub_node.api.publish_selected_items',
|
||||||
|
{
|
||||||
|
items_to_publish: this.selected_items
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.then((r) => {
|
||||||
|
this.selected_items = [];
|
||||||
|
return frappe.db.get_doc('Hub Settings');
|
||||||
|
})
|
||||||
|
.then(doc => {
|
||||||
|
hub.settings = doc;
|
||||||
|
this.add_last_sync_message();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
add_last_sync_message() {
|
||||||
|
this.last_sync_message = __(`Last sync was
|
||||||
|
<a href="#marketplace/profile">
|
||||||
|
${comment_when(hub.settings.last_sync_datetime)}</a>.
|
||||||
|
<a href="#marketplace/my-products">
|
||||||
|
See your Published Products</a>.`);
|
||||||
|
},
|
||||||
|
|
||||||
clear_last_sync_message() {
|
clear_last_sync_message() {
|
||||||
this.last_sync_message = '';
|
this.last_sync_message = '';
|
||||||
|
},
|
||||||
|
|
||||||
|
remove_item_from_selection(item_code) {
|
||||||
|
this.selected_items = this.selected_items
|
||||||
|
.filter(item => item.item_code !== item_code);
|
||||||
|
},
|
||||||
|
|
||||||
|
make_publishing_dialog() {
|
||||||
|
this.item_publish_dialog = ItemPublishDialog(
|
||||||
|
{
|
||||||
|
fn: (values) => {
|
||||||
|
this.add_item_to_publish(values);
|
||||||
|
this.item_publish_dialog.hide();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fn: () => {
|
||||||
|
const values = this.item_publish_dialog.get_values(true);
|
||||||
|
this.update_items_data_to_publish(values);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
add_item_to_publish(values) {
|
||||||
|
this.update_items_data_to_publish(values);
|
||||||
|
|
||||||
|
const item_code = values.item_code;
|
||||||
|
let item_doc = this.items_dict[item_code];
|
||||||
|
|
||||||
|
const item_to_publish = Object.assign({}, item_doc, values);
|
||||||
|
this.selected_items.push(item_to_publish);
|
||||||
|
},
|
||||||
|
|
||||||
|
update_items_data_to_publish(values) {
|
||||||
|
this.items_data_to_publish[values.item_code] = values;
|
||||||
|
},
|
||||||
|
|
||||||
|
show_publishing_dialog_for_item(item_code) {
|
||||||
|
let item_data = this.items_data_to_publish[item_code];
|
||||||
|
if(!item_data) { item_data = { item_code }; };
|
||||||
|
|
||||||
|
this.item_publish_dialog.clear();
|
||||||
|
|
||||||
|
const item_doc = this.items_dict[item_code];
|
||||||
|
if(item_doc) {
|
||||||
|
this.item_publish_dialog.fields_dict.image_list.set_data(
|
||||||
|
item_doc.attachments.map(attachment => attachment.file_url)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.item_publish_dialog.set_values(item_data);
|
||||||
|
this.item_publish_dialog.show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,54 +1,42 @@
|
|||||||
function ItemPublishDialog(primary_action, secondary_action) {
|
function ItemPublishDialog(primary_action, secondary_action) {
|
||||||
let dialog = new frappe.ui.Dialog({
|
let dialog = new frappe.ui.Dialog({
|
||||||
title: __('Edit Publishing Details'),
|
title: __('Edit Publishing Details'),
|
||||||
fields: [
|
fields: [
|
||||||
{
|
{
|
||||||
"label": "Item Code",
|
"label": "Item Code",
|
||||||
"fieldname": "item_code",
|
"fieldname": "item_code",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": "Hub Category",
|
"label": "Hub Category",
|
||||||
"fieldname": "hub_category",
|
"fieldname": "hub_category",
|
||||||
"fieldtype": "Autocomplete",
|
"fieldtype": "Autocomplete",
|
||||||
"options": [],
|
"options": [],
|
||||||
"reqd": 1
|
"reqd": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": "Images",
|
"label": "Images",
|
||||||
"fieldname": "image_list",
|
"fieldname": "image_list",
|
||||||
"fieldtype": "MultiSelect",
|
"fieldtype": "MultiSelect",
|
||||||
"options": [],
|
"options": [],
|
||||||
"reqd": 1
|
"reqd": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
primary_action_label: primary_action.label || __('Set Details'),
|
primary_action_label: primary_action.label || __('Set Details'),
|
||||||
primary_action: primary_action.fn,
|
primary_action: primary_action.fn,
|
||||||
secondary_action: secondary_action.fn
|
secondary_action: secondary_action.fn
|
||||||
});
|
});
|
||||||
|
|
||||||
|
hub.call('get_categories')
|
||||||
|
.then(categories => {
|
||||||
|
categories = categories.map(d => d.name);
|
||||||
|
dialog.fields_dict.hub_category.set_data(categories);
|
||||||
|
});
|
||||||
|
|
||||||
function set_hub_category_options(data) {
|
return dialog;
|
||||||
dialog.fields_dict.hub_category.set_data(
|
|
||||||
data.map(d => d.name)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const hub_call_key = 'get_categories{}';
|
|
||||||
const categories_cache = erpnext.hub.cache[hub_call_key];
|
|
||||||
|
|
||||||
if(categories_cache) {
|
|
||||||
set_hub_category_options(categories_cache);
|
|
||||||
}
|
|
||||||
|
|
||||||
erpnext.hub.on(`response:${hub_call_key}`, (data) => {
|
|
||||||
set_hub_category_options(data.response);
|
|
||||||
});
|
|
||||||
|
|
||||||
return dialog;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
ItemPublishDialog
|
ItemPublishDialog
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,37 +0,0 @@
|
|||||||
function get_publishing_header() {
|
|
||||||
const title_html = `<h5>${__('Select Products to Publish')}</h5>`;
|
|
||||||
|
|
||||||
const subtitle_html = `<p class="text-muted">
|
|
||||||
${__(`Only products with an image, description and category can be published.
|
|
||||||
Please update them if an item in your inventory does not appear.`)}
|
|
||||||
</p>`;
|
|
||||||
|
|
||||||
const publish_button_html = `<button class="btn btn-primary btn-sm publish-items" disabled>
|
|
||||||
<i class="visible-xs octicon octicon-check"></i>
|
|
||||||
<span class="hidden-xs">${__('Publish')}</span>
|
|
||||||
</button>`;
|
|
||||||
|
|
||||||
return $(`
|
|
||||||
<div class="publish-area empty">
|
|
||||||
<div class="publish-area-head">
|
|
||||||
${title_html}
|
|
||||||
${publish_button_html}
|
|
||||||
</div>
|
|
||||||
<div id="vue-area"></div>
|
|
||||||
<div class="empty-items-container flex align-center flex-column justify-center">
|
|
||||||
<p class="text-muted">${__('No Items Selected')}</p>
|
|
||||||
</div>
|
|
||||||
<div class="row hub-items-container selected-items"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class='subpage-title flex'>
|
|
||||||
<div>
|
|
||||||
${subtitle_html}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`);
|
|
||||||
}
|
|
||||||
|
|
||||||
export {
|
|
||||||
get_publishing_header
|
|
||||||
}
|
|
||||||
@ -1,11 +1,3 @@
|
|||||||
import SubPage from './subpage';
|
|
||||||
import { get_item_card_container_html } from '../components/items_container';
|
|
||||||
import { get_local_item_card_html } from '../components/item_card';
|
|
||||||
import { make_search_bar } from '../components/search_bar';
|
|
||||||
import { get_publishing_header } from '../components/publishing_area';
|
|
||||||
import { ItemPublishDialog } from '../components/item_publish_dialog';
|
|
||||||
|
|
||||||
|
|
||||||
import PublishPage from '../components/PublishPage.vue';
|
import PublishPage from '../components/PublishPage.vue';
|
||||||
|
|
||||||
erpnext.hub.Publish = class Publish {
|
erpnext.hub.Publish = class Publish {
|
||||||
@ -25,259 +17,4 @@ erpnext.hub.Publish = class Publish {
|
|||||||
$('[data-page-name="publish"]').hide();
|
$('[data-page-name="publish"]').hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// this.items_data_to_publish = {};
|
|
||||||
// this.unpublished_items = [];
|
|
||||||
// this.fetched_items = [];
|
|
||||||
// this.fetched_items_dict = {};
|
|
||||||
|
|
||||||
|
|
||||||
show_message(message) {
|
|
||||||
this.$wrapper.prepend(NotificationMessage(message));
|
|
||||||
}
|
|
||||||
|
|
||||||
refresh() {
|
|
||||||
if(!hub.settings.sync_in_progress) {
|
|
||||||
this.make_publish_ready_state();
|
|
||||||
} else {
|
|
||||||
this.make_publish_in_progress_state();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
make_publish_ready_state() {
|
|
||||||
this.$wrapper.empty();
|
|
||||||
this.$wrapper.append(get_publishing_header());
|
|
||||||
|
|
||||||
make_search_bar({
|
|
||||||
wrapper: this.$wrapper,
|
|
||||||
on_search: keyword => {
|
|
||||||
this.search_value = keyword;
|
|
||||||
this.get_items_and_render();
|
|
||||||
},
|
|
||||||
placeholder: __('Search Items')
|
|
||||||
});
|
|
||||||
|
|
||||||
this.setup_publishing_events();
|
|
||||||
this.show_last_sync_message();
|
|
||||||
this.get_items_and_render();
|
|
||||||
}
|
|
||||||
|
|
||||||
// show_last_sync_message() {
|
|
||||||
// if(hub.settings.last_sync_datetime) {
|
|
||||||
// this.show_message(`Last sync was <a href="#marketplace/profile">${comment_when(hub.settings.last_sync_datetime)}</a>.
|
|
||||||
// <a href="#marketplace/my-products">See your Published Products</a>.`);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
setup_publishing_events() {
|
|
||||||
this.$wrapper.find('.publish-items').on('click', () => {
|
|
||||||
this.publish_selected_items()
|
|
||||||
.then(this.refresh.bind(this))
|
|
||||||
});
|
|
||||||
|
|
||||||
this.selected_items_container = this.$wrapper.find('.selected-items');
|
|
||||||
|
|
||||||
this.$current_selected_card = null;
|
|
||||||
|
|
||||||
this.make_publishing_dialog();
|
|
||||||
|
|
||||||
this.$wrapper.on('click', '[data-route]', (e) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
const $target = $(e.currentTarget);
|
|
||||||
const route = $target.data().route;
|
|
||||||
frappe.set_route(route);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.$wrapper.on('click', '.hub-card', (e) => {
|
|
||||||
const $target = $(e.currentTarget);
|
|
||||||
const item_code = $target.attr('data-id');
|
|
||||||
this.show_publishing_dialog_for_item(item_code);
|
|
||||||
this.$current_selected_card = $target.parent();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
make_publishing_dialog() {
|
|
||||||
this.item_publish_dialog = ItemPublishDialog(
|
|
||||||
{
|
|
||||||
fn: (values) => {
|
|
||||||
this.add_item_to_publish(values);
|
|
||||||
this.item_publish_dialog.hide();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
fn: () => {
|
|
||||||
const values = this.item_publish_dialog.get_values(true);
|
|
||||||
this.update_items_data_to_publish(values);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
add_item_to_publish(values) {
|
|
||||||
this.update_items_data_to_publish(values);
|
|
||||||
this.select_current_card()
|
|
||||||
}
|
|
||||||
|
|
||||||
update_items_data_to_publish(values) {
|
|
||||||
// Add item additional data
|
|
||||||
this.items_data_to_publish[values.item_code] = values;
|
|
||||||
}
|
|
||||||
|
|
||||||
select_current_card() {
|
|
||||||
this.$current_selected_card.appendTo(this.selected_items_container);
|
|
||||||
this.$current_selected_card.find('.hub-card').toggleClass('active');
|
|
||||||
|
|
||||||
this.update_selected_items_count();
|
|
||||||
}
|
|
||||||
|
|
||||||
show_publishing_dialog_for_item(item_code) {
|
|
||||||
let item_data = this.items_data_to_publish[item_code];
|
|
||||||
|
|
||||||
if(!item_data) { item_data = { item_code }; };
|
|
||||||
|
|
||||||
this.item_publish_dialog.clear();
|
|
||||||
|
|
||||||
const item_doc = this.fetched_items_dict[item_code];
|
|
||||||
if(item_doc) {
|
|
||||||
this.item_publish_dialog.fields_dict.image_list.set_data(
|
|
||||||
item_doc.attachments.map(attachment => attachment.file_url)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.item_publish_dialog.set_values(item_data);
|
|
||||||
this.item_publish_dialog.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
update_selected_items_count() {
|
|
||||||
const total_items = this.$wrapper.find('.hub-card.active').length;
|
|
||||||
|
|
||||||
const is_empty = total_items === 0;
|
|
||||||
|
|
||||||
let button_label;
|
|
||||||
if (total_items > 0) {
|
|
||||||
const more_than_one = total_items > 1;
|
|
||||||
button_label = __('Publish {0} item{1}', [total_items, more_than_one ? 's' : '']);
|
|
||||||
} else {
|
|
||||||
button_label = __('Publish');
|
|
||||||
}
|
|
||||||
|
|
||||||
this.$wrapper.find('.publish-items')
|
|
||||||
.text(button_label)
|
|
||||||
.prop('disabled', is_empty);
|
|
||||||
|
|
||||||
this.$wrapper.find('.publish-area').toggleClass('empty', is_empty);
|
|
||||||
this.$wrapper.find('.publish-area').toggleClass('filled', !is_empty);
|
|
||||||
}
|
|
||||||
|
|
||||||
make_publish_in_progress_state() {
|
|
||||||
this.$wrapper.empty();
|
|
||||||
|
|
||||||
this.$wrapper.append(this.show_publish_progress());
|
|
||||||
|
|
||||||
const subtitle_html = `<p class="text-muted">
|
|
||||||
${__(`Only products with an image, description and category can be published.
|
|
||||||
Please update them if an item in your inventory does not appear.`)}
|
|
||||||
</p>`;
|
|
||||||
|
|
||||||
this.$wrapper.append(subtitle_html);
|
|
||||||
|
|
||||||
// Show search list with only description, and don't set any events
|
|
||||||
make_search_bar({
|
|
||||||
wrapper: this.$wrapper,
|
|
||||||
on_search: keyword => {
|
|
||||||
this.search_value = keyword;
|
|
||||||
this.get_items_and_render();
|
|
||||||
},
|
|
||||||
placeholder: __('Search Items')
|
|
||||||
});
|
|
||||||
|
|
||||||
this.get_items_and_render();
|
|
||||||
}
|
|
||||||
|
|
||||||
show_publish_progress() {
|
|
||||||
const items_to_publish = this.items_data_to_publish.length
|
|
||||||
? this.items_data_to_publish
|
|
||||||
: JSON.parse(hub.settings.custom_data || '[]');
|
|
||||||
|
|
||||||
const $publish_progress = $(`<div class="sync-progress">
|
|
||||||
<p><b>${__(`Syncing ${items_to_publish.length} Products`)}</b></p>
|
|
||||||
<div class="progress">
|
|
||||||
<div class="progress-bar" style="width: 1%"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>`);
|
|
||||||
|
|
||||||
const items_to_publish_container = $(get_item_card_container_html(
|
|
||||||
items_to_publish, '', get_local_item_card_html));
|
|
||||||
|
|
||||||
items_to_publish_container.find('.hub-card').addClass('active');
|
|
||||||
|
|
||||||
$publish_progress.append(items_to_publish_container);
|
|
||||||
|
|
||||||
return $publish_progress;
|
|
||||||
}
|
|
||||||
|
|
||||||
get_items_and_render(wrapper = this.$wrapper) {
|
|
||||||
wrapper.find('.results').remove();
|
|
||||||
const items = this.get_valid_items();
|
|
||||||
|
|
||||||
if(!items.then) {
|
|
||||||
this.render(items, wrapper);
|
|
||||||
} else {
|
|
||||||
items.then(r => {
|
|
||||||
this.fetched_items = r.message;
|
|
||||||
this.render(r.message, wrapper);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render(items, wrapper) {
|
|
||||||
const items_container = $(get_item_card_container_html(items, '', get_local_item_card_html));
|
|
||||||
items_container.addClass('results');
|
|
||||||
wrapper.append(items_container);
|
|
||||||
|
|
||||||
items.map(item => {
|
|
||||||
this.fetched_items_dict[item.item_code] = item;
|
|
||||||
});
|
|
||||||
|
|
||||||
// remove the items which doesn't have a valid image
|
|
||||||
setTimeout(() => {
|
|
||||||
items_container.find('.no-image').each(function() {
|
|
||||||
$(this).closest('.hub-card-container').remove();
|
|
||||||
});
|
|
||||||
}, 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
get_valid_items() {
|
|
||||||
if(this.unpublished_items.length) {
|
|
||||||
return this.unpublished_items;
|
|
||||||
}
|
|
||||||
return frappe.call(
|
|
||||||
'erpnext.hub_node.api.get_valid_items',
|
|
||||||
{
|
|
||||||
search_value: this.search_value
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
publish_selected_items() {
|
|
||||||
const item_codes_to_publish = [];
|
|
||||||
this.$wrapper.find('.hub-card.active').map(function () {
|
|
||||||
item_codes_to_publish.push($(this).attr("data-id"));
|
|
||||||
});
|
|
||||||
|
|
||||||
// Retrieve item data
|
|
||||||
const items_data_to_publish = item_codes_to_publish.map(item_code => this.items_data_to_publish[item_code])
|
|
||||||
|
|
||||||
return frappe.call(
|
|
||||||
'erpnext.hub_node.api.publish_selected_items',
|
|
||||||
{
|
|
||||||
items_to_publish: items_data_to_publish
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -25,16 +25,6 @@ body[data-route^="marketplace/"] {
|
|||||||
font-size: @text-medium;
|
font-size: @text-medium;
|
||||||
}
|
}
|
||||||
|
|
||||||
// .btn-primary {
|
|
||||||
// background-color: #89da28;
|
|
||||||
// border-color: #61ca23;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// .btn-primary:hover {
|
|
||||||
// background-color: #61ca23;
|
|
||||||
// border-color: #59b81c;
|
|
||||||
// }
|
|
||||||
|
|
||||||
.progress-bar {
|
.progress-bar {
|
||||||
background-color: #89da28;
|
background-color: #89da28;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user