frappe.provide('erpnext.hub');
erpnext.hub.Marketplace = class Marketplace {
constructor({ parent }) {
this.$parent = $(parent);
this.page = parent.page;
this.setup_header();
this.make_sidebar();
this.make_body();
this.setup_events();
this.refresh();
}
setup_header() {
this.page.set_title(__('Marketplace'));
}
setup_events() {
this.$parent.on('click', '[data-route]', (e) => {
const $target = $(e.currentTarget);
const route = $target.data().route;
frappe.set_route(route);
});
}
make_sidebar() {
this.$sidebar = this.$parent.find('.layout-side-section').addClass('hidden-xs');
this.$sidebar.append(`
`);
this.make_sidebar_categories();
}
make_sidebar_categories() {
frappe.call('erpnext.hub_node.get_categories')
.then(r => {
const categories = r.message.map(d => d.value).sort();
const sidebar_items = [
``,
``,
...categories.map(category => `
`)
];
this.$sidebar.append(`
`).appendTo(this.$parent);
this.hide();
}
show() {
this.refresh();
this.$wrapper.show();
}
hide() {
this.$wrapper.hide();
}
}
erpnext.hub.Home = class Home extends SubPage {
make_wrapper() {
super.make_wrapper();
this.make_search_bar();
}
refresh() {
this.get_items_and_render();
}
get_items_and_render() {
this.get_items()
.then(r => {
erpnext.hub.hub_item_cache = r.message;
this.render(r.message);
});
}
get_items() {
return frappe.call('erpnext.hub_node.get_list', {
doctype: 'Hub Item',
filters: {
image: ['like', 'http%']
}
});
}
make_search_bar() {
const $search = $(`
`
);
this.$wrapper.append($search);
const $search_input = $search.find('input');
$search_input.on('keydown', frappe.utils.debounce((e) => {
if (e.which === frappe.ui.keyCode.ENTER) {
this.search_value = $search_input.val();
this.get_items_and_render();
}
}, 300));
}
render(items) {
const html = get_item_card_container_html(items, __('Recently Published'));
this.$wrapper.html(html)
}
}
erpnext.hub.Favourites = class Favourites extends SubPage {
refresh() {
this.get_favourites()
.then(r => {
this.render(r.message);
});
}
get_favourites() {
return frappe.call('erpnext.hub_node.get_item_favourites');
}
render(items) {
const html = get_item_card_container_html(items, __('Favourites'));
this.$wrapper.html(html)
}
}
erpnext.hub.Category = class Category extends SubPage {
refresh() {
this.category = frappe.get_route()[2];
this.get_items_for_category(this.category)
.then(r => {
this.render(r.message);
});
}
get_items_for_category(category) {
return frappe.call('erpnext.hub_node.get_list', {
doctype: 'Hub Item',
filters: {
hub_category: category
}
});
}
render(items) {
const html = get_item_card_container_html(items, __(this.category));
this.$wrapper.html(html)
}
}
erpnext.hub.Item = class Item extends SubPage {
refresh() {
this.hub_item_code = frappe.get_route()[2];
this.get_item(this.hub_item_code)
.then(item => {
this.render(item);
});
}
get_item(hub_item_code) {
return new Promise(resolve => {
const item = (erpnext.hub.hub_item_cache || []).find(item => item.name === hub_item_code)
if (item) {
resolve(item);
} else {
frappe.call('erpnext.hub_node.get_list', {
doctype: 'Hub Item',
filters: {
name: hub_item_code
}
})
.then(r => {
resolve(r.message[0]);
});
}
});
}
render(item) {
const title = item.item_name || item.name;
const company = item.company_name;
const who = __('Posted By {0}', [company]);
const when = comment_when(item.creation);
const city = item.seller_city ? item.seller_city + ', ' : '';
const country = item.country ? item.country : '';
const where = `${city}${country}`;
const dot_spacer = '
· ';
const description = item.description || '';
const rating_html = get_rating_html(item);
const rating_count = item.reviews.length > 0 ? `(${item.reviews.length} reviews)` : '';
const html = `
${title}
${where}${dot_spacer}${when}
${rating_html}${rating_count}
${description ?
`
${__('Description')}
${description}
` : __('No description')
}
`;
this.$wrapper.html(html);
this.make_review_area();
this.render_reviews(item);
}
make_review_area() {
this.comment_area = new frappe.ui.ReviewArea({
parent: this.$wrapper.find('.timeline-head').empty(),
mentions: [],
on_submit: (val) => {
val.user = frappe.session.user;
val.username = frappe.session.user_fullname;
frappe.call({
method: 'erpnext.hub_node.send_review',
args: {
hub_item_code: this.hub_item_code,
review: val
},
callback: (r) => {
console.log(r);
this.render_reviews(r.message);
this.comment_area.reset();
},
freeze: true
});
}
});
}
render_reviews(item) {
this.$wrapper.find('.timeline-items').empty();
item.reviews.forEach(review => this.render_review(review, item));
}
render_review(review, item) {
let username = review.username || review.user || __("Anonymous");
let image_html = review.user_image
? `
`
: `
${frappe.get_abbr(username)}
`
let edit_html = review.own
? `
${'data.delete'}
${'data.edit'}
`
: '';
let rating_html = get_rating_html(item);
const $timeline_items = this.$wrapper.find('.timeline-items');
$(this.get_timeline_item(review, image_html, edit_html, rating_html))
.appendTo($timeline_items);
}
get_timeline_item(data, image_html, edit_html, rating_html) {
return `
${image_html}
${edit_html}
${rating_html}
${data.subject}
${data.content}
`;
}
}
function get_item_card_container_html(items, title) {
const html = (items || []).map(item => get_item_card_html(item)).join('');
return `
`;
}
function get_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_name;
const route = `marketplace/item/${item.hub_item_code}`;
let subtitle = [comment_when(item.creation)];
const rating = get_rating(item);
if (rating > 0) {
subtitle.push(rating + `
`)
}
subtitle.push(company_name);
let dot_spacer = '
· ';
subtitle = subtitle.join(dot_spacer);
const item_html = `
`;
return item_html;
}
function get_rating(item) {
const review_length = (item.reviews || []).length;
return review_length
? item.reviews
.map(r => r.rating)
.reduce((a, b) => a + b, 0) / review_length
: 0;
}
function get_rating_html(item) {
const rating = get_rating(item);
let rating_html = ``;
for (var i = 0; i < 5; i++) {
let star_class = 'fa-star';
if (i >= rating) star_class = 'fa-star-o';
rating_html += `
`;
}
return rating_html;
}
erpnext.hub.HubListing = class HubListing extends frappe.views.BaseList {
setup_defaults() {
super.setup_defaults();
this.page_title = __('');
this.method = 'erpnext.hub_node.get_list';
this.cache = {};
const route = frappe.get_route();
this.page_name = route[1];
this.menu_items = this.menu_items.concat(this.get_menu_items());
this.imageFieldName = 'image';
this.show_filters = 0;
}
set_title() {
const title = this.page_title;
let iconHtml = `
`;
let titleHtml = `
${title}`;
this.page.set_title(titleHtml, '', false, title);
}
setup_fields() {
return this.get_meta()
.then(r => {
this.meta = r.message.meta || this.meta;
frappe.model.sync(this.meta);
this.bootstrap_data(r.message);
this.prepareFormFields();
});
}
setup_filter_area() { }
get_meta() {
return new Promise(resolve =>
frappe.call('erpnext.hub_node.get_meta', { doctype: this.doctype }, resolve));
}
set_breadcrumbs() { }
prepareFormFields() { }
bootstrap_data() { }
get_menu_items() {
const items = [
{
label: __('Hub Settings'),
action: () => frappe.set_route('Form', 'Hub Settings'),
standard: true
},
{
label: __('Favourites'),
action: () => frappe.set_route('Hub', 'Favourites'),
standard: true
}
];
return items;
}
setup_side_bar() {
this.sidebar = new frappe.ui.Sidebar({
wrapper: this.page.wrapper.find('.layout-side-section'),
css_class: 'hub-sidebar'
});
}
setup_sort_selector() {
// this.sort_selector = new frappe.ui.SortSelector({
// parent: this.filter_area.$filter_list_wrapper,
// doctype: this.doctype,
// args: this.order_by,
// onchange: () => this.refresh(true)
// });
}
setup_view() {
if (frappe.route_options) {
const filters = [];
for (let field in frappe.route_options) {
var value = frappe.route_options[field];
this.page.fields_dict[field].set_value(value);
}
}
const $hub_search = $(`
`
);
this.$frappe_list.prepend($hub_search);
const $search_input = $hub_search.find('input');
$search_input.on('keydown', frappe.utils.debounce((e) => {
if (e.which === frappe.ui.keyCode.ENTER) {
this.search_value = $search_input.val();
this.refresh();
}
}, 300));
}
get_args() {
return {
doctype: this.doctype,
start: this.start,
limit: this.page_length,
order_by: this.order_by,
// fields: this.fields,
filters: this.get_filters_for_args()
};
}
update_data(r) {
const data = r.message;
if (this.start === 0) {
this.data = data;
} else {
this.data = this.data.concat(data);
}
this.data_dict = {};
}
freeze(toggle) { }
render() {
this.data_dict = {};
this.render_image_view();
this.setup_quick_view();
this.setup_like();
}
render_offline_card() {
let html = `
{{ _("Payment Cancelled") }}
${ __("Your payment is cancelled.")}
`;
let page = this.page.wrapper.find('.layout-side-section')
page.append(html);
return;
}
render_image_view() {
var html = this.data.map(this.item_html.bind(this)).join("");
if (this.start === 0) {
// ${this.getHeaderHtml()}
this.$result.html(`
Recently Published
${html}
`);
}
if (this.data.length) {
this.doc = this.data[0];
}
this.data.map(this.loadImage.bind(this));
this.data_dict = {};
this.data.map(d => {
this.data_dict[d.hub_item_code] = d;
});
}
getHeaderHtml(title, image, content) {
// let company_html =
return `
`;
}
renderHeader() {
return `
`;
}
get_image_html(encoded_name, src, alt_text) {
return `
`;
}
get_image_placeholder(title) {
return `
${frappe.get_abbr(title)}`;
}
loadImage(item) {
item._name = encodeURI(item.name);
const encoded_name = item._name;
const title = strip_html(item[this.meta.title_field || 'name']);
let placeholder = this.get_image_placeholder(title);
let $container = this.$result.find(`.image-field[data-name="${encoded_name}"]`);
if (!item[this.imageFieldName]) {
$container.prepend(placeholder);
$container.addClass('no-image');
}
frappe.load_image(item[this.imageFieldName],
(imageObj) => {
$container.prepend(imageObj)
},
() => {
$container.prepend(placeholder);
$container.addClass('no-image');
},
(imageObj) => {
imageObj.title = encoded_name;
imageObj.alt = title;
}
)
}
setup_quick_view() {
if (this.quick_view) return;
this.quick_view = new frappe.ui.Dialog({
title: 'Quick View',
fields: this.formFields
});
this.quick_view.set_primary_action(__('Request a Quote'), () => {
this.show_rfq_modal()
.then(values => {
item.item_code = values.item_code;
delete values.item_code;
const supplier = values;
return [item, supplier];
})
.then(([item, supplier]) => {
return this.make_rfq(item, supplier, this.page.btn_primary);
})
.then(r => {
console.log(r);
if (r.message && r.message.rfq) {
this.page.btn_primary.addClass('disabled').html(`
${__('Quote Requested')}`);
} else {
throw r;
}
})
.catch((e) => {
console.log(e); //eslint-disable-line
});
}, 'octicon octicon-plus');
this.$result.on('click', '.btn.zoom-view', (e) => {
e.preventDefault();
e.stopPropagation();
var name = $(e.target).attr('data-name');
name = decodeURIComponent(name);
this.quick_view.set_title(name);
let values = this.data_dict[name];
this.quick_view.set_values(values);
let fields = [];
this.quick_view.show();
return false;
});
}
setup_like() {
if (this.setup_like_done) return;
this.setup_like_done = 1;
this.$result.on('click', '.btn.like-button', (e) => {
if ($(e.target).hasClass('changing')) return;
$(e.target).addClass('changing');
e.preventDefault();
e.stopPropagation();
var name = $(e.target).attr('data-name');
name = decodeURIComponent(name);
let values = this.data_dict[name];
let heart = $(e.target);
if (heart.hasClass('like-button')) {
heart = $(e.target).find('.octicon');
}
let remove = 1;
if (heart.hasClass('liked')) {
// unlike
heart.removeClass('liked');
} else {
// like
remove = 0;
heart.addClass('liked');
}
frappe.call({
method: 'erpnext.hub_node.update_wishlist_item',
args: {
item_name: values.hub_item_code,
remove: remove
},
callback: (r) => {
let message = __("Added to Favourites");
if (remove) {
message = __("Removed from Favourites");
}
frappe.show_alert(message);
},
freeze: true
});
$(e.target).removeClass('changing');
return false;
});
}
}
erpnext.hub.ItemListing = class ItemListing extends erpnext.hub.HubListing {
constructor(opts) {
super(opts);
this.show();
}
setup_defaults() {
super.setup_defaults();
this.doctype = 'Hub Item';
this.page_title = __('Marketplace');
this.fields = ['name', 'hub_item_code', 'image', 'item_name', 'item_code', 'company_name', 'description', 'country'];
this.filters = [];
}
render() {
this.data_dict = {};
this.render_image_view();
this.setup_quick_view();
this.setup_like();
}
bootstrap_data(response) {
// let companies = response.companies.map(d => d.name);
// this.custom_filter_configs = [
// {
// fieldtype: 'Autocomplete',
// label: __('Select Company'),
// condition: 'like',
// fieldname: 'company_name',
// options: companies
// },
// {
// fieldtype: 'Link',
// label: __('Select Country'),
// options: 'Country',
// condition: 'like',
// fieldname: 'country'
// }
// ];
}
prepareFormFields() {
let fieldnames = ['item_name', 'description', 'company_name', 'country'];
this.formFields = this.meta.fields
.filter(field => fieldnames.includes(field.fieldname))
.map(field => {
let {
label,
fieldname,
fieldtype,
} = field;
let read_only = 1;
return {
label,
fieldname,
fieldtype,
read_only,
};
});
this.formFields.unshift({
label: 'image',
fieldname: 'image',
fieldtype: 'Attach Image'
});
}
setup_side_bar() {
super.setup_side_bar();
this.setup_new_sidebar();
return;
let $pitch = $(`
Sell on HubMarket
Over 2000 products listed. Register your company to start selling.
`);
this.sidebar.$sidebar.append($pitch);
this.category_tree = new frappe.ui.Tree({
parent: this.sidebar.$sidebar,
label: 'All Categories',
expandable: true,
args: { parent: this.current_category },
method: 'erpnext.hub_node.get_categories',
on_click: (node) => {
this.update_category(node.label);
}
});
this.sidebar.add_item({
label: __('Companies'),
on_click: () => frappe.set_route('Hub', 'Company')
}, undefined, true);
this.sidebar.add_item({
label: this.hub_settings.company,
on_click: () => frappe.set_route('Form', 'Company', this.hub_settings.company)
}, __("Account"));
this.sidebar.add_item({
label: __("Favourites"),
on_click: () => frappe.set_route('Hub', 'Favourites')
}, __("Account"));
this.sidebar.add_item({
label: __("Settings"),
on_click: () => frappe.set_route('Form', 'Hub Settings')
}, __("Account"));
}
setup_new_sidebar() {
this.sidebar.$sidebar.append(`
`);
frappe.call('erpnext.hub_node.get_categories')
.then(r => {
const categories = r.message.map(d => d.value).sort();
const sidebar_items = [
``,
``,
...categories.map(category => `
`)
];
this.sidebar.$sidebar.append(`
${sidebar_items.join('')}
`);
});
}
update_category(label) {
this.current_category = (label == 'All Categories') ? undefined : label;
this.refresh();
}
get_filters_for_args() {
const filter = {};
if (this.search_value) {
filter.item_name = ['like', `%${this.search_value}%`];
}
filter.image = ['like', 'http%'];
return filter;
// if(!this.filter_area) return;
// let filters = {};
// this.filter_area.get().forEach(f => {
// let field = f[1] !== 'name' ? f[1] : 'item_name';
// filters[field] = [f[2], f[3]];
// });
// if(this.current_category) {
// filters['hub_category'] = this.current_category;
// }
// return filters;
}
update_data(r) {
super.update_data(r);
this.data_dict = {};
this.data.map(d => {
this.data_dict[d.hub_item_code] = d;
});
}
item_html(item, index) {
item._name = encodeURI(item.name);
const encoded_name = item._name;
const title = strip_html(item[this.meta.title_field || 'name']);
const img_url = item[this.imageFieldName];
const no_image = !img_url;
const _class = no_image ? 'no-image' : '';
const route = `#Hub/Item/${item.hub_item_code}`;
const company_name = item['company_name'];
const reviewLength = (item.reviews || []).length;
const ratingAverage = reviewLength
? item.reviews
.map(r => r.rating)
.reduce((a, b) => a + b, 0) / reviewLength
: -1;
let ratingHtml = ``;
for (var i = 0; i < 5; i++) {
let starClass = 'fa-star';
if (i >= ratingAverage) starClass = 'fa-star-o';
ratingHtml += `
`;
}
let dot_spacer = '
· ';
let subtitle = '';
subtitle += comment_when(item.creation);
subtitle += dot_spacer;
if (ratingAverage > 0) {
subtitle += ratingAverage + `
`;
subtitle += dot_spacer;
}
subtitle += company_name;
let item_html = `
`;
item_html = `
`;
return item_html;
}
};
erpnext.hub.Favourites2 = class Favourites extends erpnext.hub.ItemListing {
constructor(opts) {
super(opts);
this.show();
}
setup_defaults() {
super.setup_defaults();
this.doctype = 'Hub Item';
this.page_title = __('Favourites');
this.fields = ['name', 'hub_item_code', 'image', 'item_name', 'item_code', 'company_name', 'description', 'country'];
this.filters = [];
this.method = 'erpnext.hub_node.get_item_favourites';
}
setup_filter_area() { }
setup_sort_selector() { }
// setupHe
getHeaderHtml() {
return '';
}
get_args() {
return {
start: this.start,
limit: this.page_length,
order_by: this.order_by,
fields: this.fields
};
}
bootstrap_data(response) { }
prepareFormFields() { }
setup_side_bar() {
this.sidebar = new frappe.ui.Sidebar({
wrapper: this.page.wrapper.find('.layout-side-section'),
css_class: 'hub-sidebar'
});
this.sidebar.add_item({
label: __('Back to Products'),
on_click: () => frappe.set_route('Hub', 'Item')
});
}
update_category(label) {
this.current_category = (label == 'All Categories') ? undefined : label;
this.refresh();
}
get_filters_for_args() {
if (!this.filter_area) return;
let filters = {};
this.filter_area.get().forEach(f => {
let field = f[1] !== 'name' ? f[1] : 'item_name';
filters[field] = [f[2], f[3]];
});
if (this.current_category) {
filters['hub_category'] = this.current_category;
}
return filters;
}
update_data(r) {
super.update_data(r);
this.data_dict = {};
this.data.map(d => {
this.data_dict[d.hub_item_code] = d;
});
}
};
erpnext.hub.CompanyListing = class CompanyListing extends erpnext.hub.HubListing {
constructor(opts) {
super(opts);
this.show();
}
render() {
this.data_dict = {};
this.render_image_view();
}
setup_defaults() {
super.setup_defaults();
this.doctype = 'Hub Company';
this.page_title = __('Companies');
this.fields = ['company_logo', 'name', 'site_name', 'seller_city', 'seller_description', 'seller', 'country', 'company_name'];
this.filters = [];
this.custom_filter_configs = [
{
fieldtype: 'Link',
label: 'Country',
options: 'Country',
condition: 'like',
fieldname: 'country'
}
];
this.imageFieldName = 'company_logo';
}
setup_side_bar() {
this.sidebar = new frappe.ui.Sidebar({
wrapper: this.page.wrapper.find('.layout-side-section'),
css_class: 'hub-sidebar'
});
this.sidebar.add_item({
label: __('Back to Products'),
on_click: () => frappe.set_route('Hub', 'Item')
});
}
get_filters_for_args() {
let filters = {};
// this.filter_area.get().forEach(f => {
// let field = f[1] !== 'name' ? f[1] : 'company_name';
// filters[field] = [f[2], f[3]];
// });
return filters;
}
item_html(company) {
company._name = encodeURI(company.company_name);
const encoded_name = company._name;
const title = strip_html(company.company_name);
const _class = !company[this.imageFieldName] ? 'no-image' : '';
const company_name = company['company_name'];
const route = `#Hub/Company/${company_name}`;
let image_html = company.company_logo ?
`
` :
`
${frappe.get_abbr(company.company_name)}
`;
let item_html = `
`;
return item_html;
}
};