From 709a4a3f4e2fc059691d89306946c8df350d0daa Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Wed, 1 Aug 2018 14:09:07 +0530 Subject: [PATCH] Break marketplace.js into multiple files --- erpnext/public/build.json | 3 + erpnext/public/js/hub/helpers.js | 143 +++ erpnext/public/js/hub/hub_call.js | 44 + erpnext/public/js/hub/hub_factory.js | 86 +- erpnext/public/js/hub/hub_form.js | 493 ------- erpnext/public/js/hub/hub_listing.js | 802 ------------ erpnext/public/js/hub/marketplace.js | 1132 +---------------- erpnext/public/js/hub/pages/base_page.js | 35 + erpnext/public/js/hub/pages/category.js | 27 + erpnext/public/js/hub/pages/favourites.js | 21 + erpnext/public/js/hub/pages/home.js | 41 + erpnext/public/js/hub/pages/item.js | 327 +++++ erpnext/public/js/hub/pages/not_found.js | 10 + erpnext/public/js/hub/pages/profile.js | 98 ++ erpnext/public/js/hub/pages/publish.js | 228 ++++ .../public/js/hub/pages/published_products.js | 23 + erpnext/public/js/hub/pages/register.js | 110 ++ erpnext/public/js/hub/pages/search.js | 34 + 18 files changed, 1162 insertions(+), 2495 deletions(-) create mode 100644 erpnext/public/js/hub/helpers.js create mode 100644 erpnext/public/js/hub/hub_call.js delete mode 100644 erpnext/public/js/hub/hub_form.js delete mode 100644 erpnext/public/js/hub/hub_listing.js create mode 100644 erpnext/public/js/hub/pages/base_page.js create mode 100644 erpnext/public/js/hub/pages/category.js create mode 100644 erpnext/public/js/hub/pages/favourites.js create mode 100644 erpnext/public/js/hub/pages/home.js create mode 100644 erpnext/public/js/hub/pages/item.js create mode 100644 erpnext/public/js/hub/pages/not_found.js create mode 100644 erpnext/public/js/hub/pages/profile.js create mode 100644 erpnext/public/js/hub/pages/publish.js create mode 100644 erpnext/public/js/hub/pages/published_products.js create mode 100644 erpnext/public/js/hub/pages/register.js create mode 100644 erpnext/public/js/hub/pages/search.js diff --git a/erpnext/public/build.json b/erpnext/public/build.json index ed4ebababe..7bcf99bd2f 100644 --- a/erpnext/public/build.json +++ b/erpnext/public/build.json @@ -7,6 +7,9 @@ "public/js/website_utils.js", "public/js/shopping_cart.js" ], + "js/marketplace.min.js": [ + "public/js/hub/marketplace.js" + ], "js/erpnext.min.js": [ "public/js/conf.js", "public/js/utils.js", diff --git a/erpnext/public/js/hub/helpers.js b/erpnext/public/js/hub/helpers.js new file mode 100644 index 0000000000..22e35c3d89 --- /dev/null +++ b/erpnext/public/js/hub/helpers.js @@ -0,0 +1,143 @@ +function get_empty_state(message, action) { + return `
+

${message}

+ ${action ? `

${action}

`: ''} +
`; +} + +function get_item_card_container_html(items, title='', get_item_html = get_item_card_html) { + const items_html = (items || []).map(item => get_item_html(item)).join(''); + const title_html = title + ? `
+ ${title} +
` + : ''; + + const html = `
+ ${title_html} + ${items_html} +
`; + + return html; +} + +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; + + // Subtitle + let subtitle = [comment_when(item.creation)]; + const rating = item.average_rating; + if (rating > 0) { + subtitle.push(rating + ``) + } + subtitle.push(company_name); + + let dot_spacer = ''; + subtitle = subtitle.join(dot_spacer); + + const item_html = ` +
+
+
+
${title}
+
${subtitle}
+
+
+ +
+
+
+
+ `; + + 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 + ``) + } + subtitle.push(company_name); + + let dot_spacer = ''; + subtitle = subtitle.join(dot_spacer); + + const edit_item_button = `
+ +
`; + + const item_html = ` +
+
+
+
${title}
+
${subtitle}
+ +
+
+ +
+
+ ${edit_item_button} +
+
+
+
+
+ `; + + return item_html; +} + + +function get_rating_html(rating) { + 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; +} + +function make_search_bar({wrapper, on_search, placeholder = __('Search for anything')}) { + const $search = $(` +
+ +
` + ); + wrapper.append($search); + const $search_input = $search.find('input'); + + $search_input.on('keydown', frappe.utils.debounce((e) => { + if (e.which === frappe.ui.keyCode.ENTER) { + const search_value = $search_input.val(); + on_search(search_value); + } + }, 300)); +} + +export { + get_empty_state, + get_item_card_container_html, + get_item_card_html, + get_local_item_card_html, + get_rating_html, + make_search_bar, +} \ No newline at end of file diff --git a/erpnext/public/js/hub/hub_call.js b/erpnext/public/js/hub/hub_call.js new file mode 100644 index 0000000000..e0fead3e50 --- /dev/null +++ b/erpnext/public/js/hub/hub_call.js @@ -0,0 +1,44 @@ +frappe.provide('hub'); +frappe.provide('erpnext.hub'); + +erpnext.hub.cache = {}; +hub.call = function call_hub_method(method, args={}) { + return new Promise((resolve, reject) => { + + // cache + const key = method + JSON.stringify(args); + if (erpnext.hub.cache[key]) { + resolve(erpnext.hub.cache[key]); + } + + // cache invalidation after 5 minutes + const timeout = 5 * 60 * 1000; + + setTimeout(() => { + delete erpnext.hub.cache[key]; + }, timeout); + + frappe.call({ + method: 'erpnext.hub_node.call_hub_method', + args: { + method, + params: args + } + }) + .then(r => { + if (r.message) { + if (r.message.error) { + frappe.throw({ + title: __('Marketplace Error'), + message: r.message.error + }); + } + + erpnext.hub.cache[key] = r.message; + resolve(r.message) + } + reject(r) + }) + .fail(reject) + }); +} diff --git a/erpnext/public/js/hub/hub_factory.js b/erpnext/public/js/hub/hub_factory.js index c94edf4e4d..b073720430 100644 --- a/erpnext/public/js/hub/hub_factory.js +++ b/erpnext/public/js/hub/hub_factory.js @@ -1,9 +1,7 @@ -frappe.provide('erpnext.hub.pages'); +frappe.provide('erpnext.hub'); frappe.views.marketplaceFactory = class marketplaceFactory extends frappe.views.Factory { show() { - const page_name = frappe.get_route_str(); - if (frappe.pages.marketplace) { frappe.container.change_to('marketplace'); erpnext.hub.marketplace.refresh(); @@ -14,7 +12,7 @@ frappe.views.marketplaceFactory = class marketplaceFactory extends frappe.views. make(page_name) { const assets = [ - '/assets/erpnext/js/hub/marketplace.js' + '/assets/js/marketplace.min.js' ]; frappe.require(assets, () => { @@ -24,83 +22,3 @@ frappe.views.marketplaceFactory = class marketplaceFactory extends frappe.views. }); } } - -frappe.views.HubFactory = class HubFactory extends frappe.views.Factory { - - make(route) { - const page_name = frappe.get_route_str(); - const page = route[1]; - - const assets = { - 'List': [ - '/assets/erpnext/js/hub/hub_listing.js', - ], - 'Form': [ - '/assets/erpnext/js/hub/hub_form.js' - ] - }; - frappe.model.with_doc('Hub Settings', 'Hub Settings', () => { - this.hub_settings = frappe.get_doc('Hub Settings'); - - if (!erpnext.hub.pages[page_name]) { - if(!frappe.is_online()) { - this.render_offline_card(); - return; - } - if (!route[2]) { - frappe.require(assets['List'], () => { - if(page === 'Favourites') { - erpnext.hub.pages[page_name] = new erpnext.hub['Favourites']({ - parent: this.make_page(true, page_name), - hub_settings: this.hub_settings - }); - } else { - erpnext.hub.pages[page_name] = new erpnext.hub[page+'Listing']({ - parent: this.make_page(true, page_name), - hub_settings: this.hub_settings - }); - } - }); - } else if (!route[3]){ - frappe.require(assets['Form'], () => { - erpnext.hub.pages[page_name] = new erpnext.hub[page+'Page']({ - unique_id: route[2], - doctype: route[2], - parent: this.make_page(true, page_name), - hub_settings: this.hub_settings - }); - }); - } else { - frappe.require(assets['List'], () => { - frappe.route_options = {}; - frappe.route_options["company_name"] = route[2] - erpnext.hub.pages[page_name] = new erpnext.hub['ItemListing']({ - parent: this.make_page(true, page_name), - hub_settings: this.hub_settings - }); - }); - } - window.hub_page = erpnext.hub.pages[page_name]; - } else { - frappe.container.change_to(page_name); - window.hub_page = erpnext.hub.pages[page_name]; - } - }); - } - - render_offline_card() { - let html = `
-
- ${'Failed to connect'} -
-

${ __("Please check your network connection.") }

-
- ${ __("Reload") }
-
`; - - let page = $('#body_div'); - page.append(html); - - return; - } -} diff --git a/erpnext/public/js/hub/hub_form.js b/erpnext/public/js/hub/hub_form.js deleted file mode 100644 index 9287e6d54f..0000000000 --- a/erpnext/public/js/hub/hub_form.js +++ /dev/null @@ -1,493 +0,0 @@ -frappe.provide('erpnext.hub'); - -erpnext.hub.HubDetailsPage = class HubDetailsPage extends frappe.views.BaseList { - setup_defaults() { - super.setup_defaults(); - this.method = 'erpnext.hub_node.get_details'; - const route = frappe.get_route(); - // this.page_name = route[2]; - } - - setup_fields() { - return this.get_meta() - .then(r => { - this.meta = r.message.meta || this.meta; - this.categories = r.message.categories || []; - this.bootstrap_data(r.message); - - this.getFormFields(); - }); - } - - bootstrap_data() { } - - get_meta() { - return new Promise(resolve => - frappe.call('erpnext.hub_node.get_meta', {doctype: 'Hub ' + this.doctype}, resolve)); - } - - - set_breadcrumbs() { - frappe.breadcrumbs.add({ - label: __('Hub'), - route: '#Hub/' + this.doctype, - type: 'Custom' - }); - } - - setup_side_bar() { - this.sidebar = new frappe.ui.Sidebar({ - wrapper: this.$page.find('.layout-side-section'), - css_class: 'hub-form-sidebar' - }); - } - - setup_filter_area() { } - - setup_sort_selector() { } - - // let category = this.quick_view.get_values().hub_category; - // return new Promise((resolve, reject) => { - // frappe.call({ - // method: 'erpnext.hub_node.update_category', - // args: { - // hub_item_code: values.hub_item_code, - // category: category, - // }, - // callback: (r) => { - // resolve(); - // }, - // freeze: true - // }).fail(reject); - // }); - - get_timeline() { - return `
-
-
-
- -
-
-
`; - } - - get_footer() { - return ``; - } - - get_args() { - return { - hub_sync_id: this.unique_id, - doctype: 'Hub ' + this.doctype - }; - } - - prepare_data(r) { - this.data = r.message; - } - - update_data(r) { - this.data = r.message; - } - - render() { - const image_html = this.data[this.image_field_name] ? - ` - ` : - `
${frappe.get_abbr(this.page_title)}
`; - - this.sidebar.remove_item('image'); - this.sidebar.add_item({ - name: 'image', - label: image_html - }); - - if(!this.form) { - let fields = this.formFields; - this.form = new frappe.ui.FieldGroup({ - parent: this.$result, - fields - }); - this.form.make(); - } - - if(this.data.hub_category) { - this.form.fields_dict.set_category.hide(); - } - - this.form.set_values(this.data); - this.$result.show(); - - this.$timelineList && this.$timelineList.empty(); - if(this.data.reviews && this.data.reviews.length) { - this.data.reviews.map(review => { - this.addReviewToTimeline(review); - }) - } - - this.postRender() - } - - postRender() {} - - attachFooter() { - let footerHtml = ``; - - let parent = $('
').appendTo(this.page.main.parent()); - this.$footer = $(footerHtml).appendTo(parent); - } - - attachTimeline() { - let timelineHtml = `
-
-
-
- -
-
-
`; - - let parent = this.$footer.find(".form-comments"); - this.$timeline = $(timelineHtml).appendTo(parent); - - this.$timelineList = this.$timeline.find(".timeline-items"); - } - - attachReviewArea() { - this.comment_area = new frappe.ui.ReviewArea({ - parent: this.$footer.find('.timeline-head'), - 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.data.hub_item_code, - review: val - }, - callback: (r) => { - this.refresh(); - this.comment_area.reset(); - }, - freeze: true - }); - } - }); - } - - addReviewToTimeline(data) { - let username = data.username || data.user || __("Anonymous") - let imageHtml = data.user_image - ? `
` - : `
${frappe.get_abbr(username)}
` - - let editHtml = data.own - ? ` -
- - ${'data.edit'} - -
` - : ''; - - let ratingHtml = ''; - - for(var i = 0; i < 5; i++) { - let starIcon = 'fa-star-o' - if(i < data.rating) { - starIcon = 'fa-star'; - } - ratingHtml += ``; - } - - $(this.getTimelineItem(data, imageHtml, editHtml, ratingHtml)) - .appendTo(this.$timelineList); - } - - getTimelineItem(data, imageHtml, editHtml, ratingHtml) { - return `
- - -
-
-
${editHtml}
- -
- - ${imageHtml} - - -
- - - ${data.username} - - - - - - - - - ${''} - -
-
-
-
-

- ${data.subject} -

- -
- -

- ${ratingHtml} -

- -
-

- ${data.content} -

-
-
-
-
-
`; - } - - prepareFormFields(fields, fieldnames) { - return fields - .filter(field => fieldnames.includes(field.fieldname)) - .map(field => { - let { - label, - fieldname, - fieldtype, - } = field; - let read_only = 1; - return { - label, - fieldname, - fieldtype, - read_only, - }; - }); - } -}; - -erpnext.hub.ItemPage = class ItemPage extends erpnext.hub.HubDetailsPage { - constructor(opts) { - super(opts); - - this.show(); - } - - setup_defaults() { - super.setup_defaults(); - this.doctype = 'Item'; - this.image_field_name = 'image'; - } - - setup_page_head() { - super.setup_page_head(); - this.set_primary_action(); - } - - setup_side_bar() { - super.setup_side_bar(); - this.attachFooter(); - this.attachTimeline(); - this.attachReviewArea(); - } - - set_primary_action() { - let item = this.data; - this.page.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'); - } - - prepare_data(r) { - super.prepare_data(r); - this.page.set_title(this.data["item_name"]); - } - - make_rfq(item, supplier, btn) { - console.log(supplier); - return new Promise((resolve, reject) => { - frappe.call({ - method: 'erpnext.hub_node.make_rfq_and_send_opportunity', - args: { item, supplier }, - callback: resolve, - btn, - }).fail(reject); - }); - } - - postRender() { - this.categoryDialog = new frappe.ui.Dialog({ - title: __('Suggest Category'), - fields: [ - { - label: __('Category'), - fieldname: 'category', - fieldtype: 'Autocomplete', - options: this.categories, - reqd: 1 - } - ], - primary_action_label: __("Send"), - primary_action: () => { - let values = this.categoryDialog.get_values(); - frappe.call({ - method: 'erpnext.hub_node.update_category', - args: { - hub_item_code: this.data.hub_item_code, - category: values.category - }, - callback: () => { - this.categoryDialog.hide(); - this.refresh(); - }, - freeze: true - }).fail(() => {}); - } - }); - } - - getFormFields() { - let colOneFieldnames = ['item_name', 'item_code', 'description']; - let colTwoFieldnames = ['seller', 'company_name', 'country']; - let colOneFields = this.prepareFormFields(this.meta.fields, colOneFieldnames); - let colTwoFields = this.prepareFormFields(this.meta.fields, colTwoFieldnames); - - let miscFields = [ - { - label: __('Category'), - fieldname: 'hub_category', - fieldtype: 'Data', - read_only: 1 - }, - - { - label: __('Suggest Category?'), - fieldname: 'set_category', - fieldtype: 'Button', - click: () => { - this.categoryDialog.show(); - } - }, - - { - fieldname: 'cb1', - fieldtype: 'Column Break' - } - ]; - this.formFields = colOneFields.concat(miscFields, colTwoFields); - } - - show_rfq_modal() { - let item = this.data; - return new Promise(res => { - let fields = [ - { label: __('Item Code'), fieldtype: 'Data', fieldname: 'item_code', default: item.item_code }, - { fieldtype: 'Column Break' }, - { label: __('Item Group'), fieldtype: 'Link', fieldname: 'item_group', default: item.item_group }, - { label: __('Supplier Details'), fieldtype: 'Section Break' }, - { label: __('Supplier Name'), fieldtype: 'Data', fieldname: 'supplier_name', default: item.company_name }, - { label: __('Supplier Email'), fieldtype: 'Data', fieldname: 'supplier_email', default: item.seller }, - { fieldtype: 'Column Break' }, - { label: __('Supplier Group'), fieldname: 'supplier_group', - fieldtype: 'Link', options: 'Supplier Group' } - ]; - fields = fields.map(f => { f.reqd = 1; return f; }); - - const d = new frappe.ui.Dialog({ - title: __('Request for Quotation'), - fields: fields, - primary_action_label: __('Send'), - primary_action: (values) => { - res(values); - d.hide(); - } - }); - - d.show(); - }); - } -} - -erpnext.hub.CompanyPage = class CompanyPage extends erpnext.hub.HubDetailsPage { - constructor(opts) { - super(opts); - this.show(); - } - - setup_defaults() { - super.setup_defaults(); - this.doctype = 'Company'; - this.image_field_name = 'company_logo'; - } - - prepare_data(r) { - super.prepare_data(r); - this.page.set_title(this.data["company_name"]); - } - - getFormFields() { - let fieldnames = ['company_name', 'description', 'route', 'country', 'seller', 'site_name'];; - this.formFields = this.prepareFormFields(this.meta.fields, fieldnames); - } -} diff --git a/erpnext/public/js/hub/hub_listing.js b/erpnext/public/js/hub/hub_listing.js deleted file mode 100644 index 368c723e5b..0000000000 --- a/erpnext/public/js/hub/hub_listing.js +++ /dev/null @@ -1,802 +0,0 @@ - -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.")}

-
- ${ __("Continue")}
-
`; - - 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 ` -
-
-
- ${title} -
-
-
- ${title} -
- - ${content} - -
-
-
- `; - } - - renderHeader() { - return ``; - } - - get_image_html(encoded_name, src, alt_text) { - return `${alt_text}`; - } - - 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 = [ - `
  • - ${__('Category')} -
  • `, - `
  • - All -
  • `, - ...categories.map(category => ` -
  • - ${category} -
  • - `) - ]; - - this.sidebar.$sidebar.append(` - - `); - }); - } - - 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 = ` -
    -
    -
    -
    - - ${title} - -
    -
    - ${ratingHtml} - (${reviewLength}) -
    - -
    - -
    -
    - `; - - item_html = ` -
    -
    -
    -
    ${title}
    -
    ${subtitle}
    -
    -
    - -
    -
    -
    - `; - - 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 = ` -
    -
    -
    - - ${title} - -
    -
    - - -
    - `; - - return item_html; - } - -}; diff --git a/erpnext/public/js/hub/marketplace.js b/erpnext/public/js/hub/marketplace.js index 31cf5daf48..ab0d4f7e71 100644 --- a/erpnext/public/js/hub/marketplace.js +++ b/erpnext/public/js/hub/marketplace.js @@ -1,3 +1,19 @@ +// pages +import './pages/home'; +import './pages/favourites'; +import './pages/search'; +import './pages/category'; +import './pages/item'; +import './pages/register'; +import './pages/profile'; +import './pages/publish'; +import './pages/published_products'; +import './pages/not_found'; + +// helpers +import './helpers'; +import './hub_call'; + frappe.provide('hub'); frappe.provide('erpnext.hub'); @@ -180,1119 +196,3 @@ erpnext.hub.Marketplace = class Marketplace { this.subpages[route[1]].show(); } } - -class SubPage { - constructor(parent, options) { - this.$parent = $(parent); - this.make_wrapper(options); - - // handle broken images after every render - if (this.render) { - this._render = this.render.bind(this); - - this.render = (...args) => { - this._render(...args); - frappe.dom.handle_broken_images(this.$wrapper); - } - } - } - - make_wrapper() { - const page_name = frappe.get_route()[1]; - this.$wrapper = $(`
    `).appendTo(this.$parent); - this.hide(); - } - - empty() { - this.$wrapper.empty(); - } - - show() { - this.refresh(); - this.$wrapper.show(); - } - - hide() { - this.$wrapper.hide(); - } -} - -erpnext.hub.Home = class Home extends SubPage { - make_wrapper() { - super.make_wrapper(); - - make_search_bar({ - wrapper: this.$wrapper, - on_search: keyword => { - frappe.set_route('marketplace', 'search', keyword); - } - }); - } - - refresh() { - this.get_items_and_render(); - } - - get_items_and_render() { - this.$wrapper.find('.hub-card-container').empty(); - this.get_data() - .then(data => { - this.render(data); - }); - } - - get_data() { - return hub.call('get_data_for_homepage', { country: frappe.defaults.get_user_default('country') }); - } - - render(data) { - let html = get_item_card_container_html(data.random_items, __('Explore')); - this.$wrapper.append(html); - - if (data.items_by_country.length) { - html = get_item_card_container_html(data.items_by_country, __('Near you')); - this.$wrapper.append(html); - } - } -} - -erpnext.hub.Favourites = class Favourites extends SubPage { - refresh() { - this.get_favourites() - .then(items => { - this.render(items); - }); - } - - get_favourites() { - return hub.call('get_item_favourites'); - } - - render(items) { - this.$wrapper.find('.hub-card-container').empty(); - const html = get_item_card_container_html(items, __('Favourites')); - this.$wrapper.append(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) { - this.$wrapper.find('.hub-card-container').empty(); - 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.append(html) - } -} - -erpnext.hub.SearchPage = class SearchPage extends SubPage { - make_wrapper() { - super.make_wrapper(); - - make_search_bar({ - wrapper: this.$wrapper, - on_search: keyword => { - frappe.set_route('marketplace', 'search', keyword); - } - }); - } - - refresh() { - this.keyword = frappe.get_route()[2] || ''; - this.$wrapper.find('input').val(this.keyword); - - this.get_items_by_keyword(this.keyword) - .then(items => this.render(items)); - } - - get_items_by_keyword(keyword) { - return hub.call('get_items_by_keyword', { keyword }); - } - - render(items) { - this.$wrapper.find('.hub-card-container').remove(); - const title = this.keyword ? __('Search results for "{0}"', [this.keyword]) : ''; - const html = get_item_card_container_html(items, title); - this.$wrapper.append(html); - } -} - -erpnext.hub.Item = class Item extends SubPage { - make_wrapper() { - super.make_wrapper(); - this.setup_events(); - } - - refresh() { - this.show_skeleton(); - this.hub_item_code = frappe.get_route()[2]; - - this.own_item = false; - - this.get_item(this.hub_item_code) - .then(item => { - this.own_item = item.hub_seller === hub.settings.company_email; - this.item = item; - this.render(item); - }); - } - - show_skeleton() { - const skeleton = `
    -
    -
    -
    -
    -
    -

    Name

    -
    -

    Details

    -

    Ratings

    -
    -
    -
    -

    Desc

    -

    Desc

    -
    -
    -
    -
    `; - - this.$wrapper.html(skeleton); - } - - setup_events() { - this.$wrapper.on('click', '.btn-contact-seller', () => { - const d = new frappe.ui.Dialog({ - title: __('Send a message'), - fields: [ - { - fieldname: 'to', - fieldtype: 'Read Only', - label: __('To'), - default: this.item.company - }, - { - fieldtype: 'Text', - fieldname: 'message', - label: __('Message') - } - ] - }); - - d.show(); - }); - } - - get_item(hub_item_code) { - return hub.call('get_item_details', { hub_item_code }); - } - - render(item) { - const title = item.item_name || item.name; - const seller = item.company; - - const who = __('Posted By {0}', [seller]); - const when = comment_when(item.creation); - - const city = item.city ? item.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.average_rating); - const rating_count = item.no_of_ratings > 0 ? `${item.no_of_ratings} reviews` : __('No reviews yet'); - - let edit_buttons_html = ''; - - if(this.own_item) { - edit_buttons_html = `
    - - -
    `; - } - - const html = ` -
    -
    -
    - -
    -
    -
    -
    -
    - -
    -
    -
    -

    ${title}

    -
    -

    ${where}${dot_spacer}${when}

    -

    ${rating_html} (${rating_count})

    -
    -
    -
    - ${description ? - `${__('Description')} -

    ${description}

    - ` : `

    ${__('No description')}

    ` - } -

    - ${edit_buttons_html} -
    -
    - -
    -
    -
    -
    - Seller Information -
    -
    - -
    -
    - - -
    -
    - -
    - -
    -
    - `; - - this.$wrapper.html(html); - - if(this.own_item) { - this.bind_edit_buttons(); - } - - this.make_review_area(); - - this.get_reviews() - .then(reviews => { - this.reviews = reviews; - this.render_reviews(reviews); - }); - } - - bind_edit_buttons() { - this.edit_dialog = new frappe.ui.Dialog({ - title: "Edit Your Product", - fields: [] - }); - - this.$wrapper.find('.edit-item').on('click', this.on_edit.bind(this)); - this.$wrapper.find('.unpublish').on('click', this.on_unpublish.bind(this)); - } - - on_edit() { - this.edit_dialog.show(); - } - - on_unpublish() { - if(!this.unpublish_dialog) { - this.unpublish_dialog = new frappe.ui.Dialog({ - title: "Edit Your Product", - fields: [] - }); - } - - this.unpublish_dialog.show(); - } - - make_review_area() { - this.comment_area = new frappe.ui.ReviewArea({ - parent: this.$wrapper.find('.timeline-head').empty(), - mentions: [], - on_submit: (values) => { - values.user = frappe.session.user; - values.username = frappe.session.user_fullname; - - hub.call('add_item_review', { - hub_item_code: this.hub_item_code, - review: JSON.stringify(values) - }) - .then(review => { - this.reviews = this.reviews || []; - this.reviews.push(review); - this.render_reviews(this.reviews); - - this.comment_area.reset(); - }); - } - }); - } - - get_reviews() { - return hub.call('get_item_reviews', { hub_item_code: this.hub_item_code }).catch(() => {}); - } - - render_reviews(reviews=[]) { - this.$wrapper.find('.timeline-items').empty(); - - reviews.sort((a, b) => { - if (a.modified > b.modified) { - return -1; - } - - if (a.modified < b.modified) { - return 1; - } - - return 0; - }); - - reviews.forEach(review => this.render_review(review)); - } - - render_review(review) { - let username = review.username || review.user || __("Anonymous"); - - let image_html = review.user_image - ? `
    ` - : `
    ${frappe.get_abbr(username)}
    ` - - let edit_html = review.own - ? ` -
    - - ${'data.edit'} - -
    ` - : ''; - - let rating_html = get_rating_html(review.rating); - - 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 `
    - -
    -
    -
    ${edit_html}
    - -
    - - ${image_html} - - -
    - - - ${data.username} - - - - - -
    -
    -
    -
    -

    - ${rating_html} -

    -
    ${data.subject}
    -

    - ${data.content} -

    -
    -
    -
    -
    -
    `; - } -} -erpnext.hub.Register = class Register extends SubPage { - make_wrapper() { - super.make_wrapper(); - this.$register_container = $(`
    `) - .appendTo(this.$wrapper); - this.$form_container = $('
    ') - .appendTo(this.$wrapper); - } - - refresh() { - this.$register_container.empty(); - this.$form_container.empty(); - this.render(); - } - - render() { - this.make_field_group(); - } - - make_field_group() { - const fields = [ - { - fieldtype: 'Link', - fieldname: 'company', - label: __('Company'), - options: 'Company', - onchange: () => { - const value = this.field_group.get_value('company'); - - if (value) { - frappe.db.get_doc('Company', value) - .then(company => { - this.field_group.set_values({ - country: company.country, - company_email: company.email, - currency: company.default_currency - }); - }); - } - } - }, - { - fieldname: 'company_email', - label: __('Email'), - fieldtype: 'Data' - }, - { - fieldname: 'country', - label: __('Country'), - fieldtype: 'Read Only' - }, - { - fieldname: 'currency', - label: __('Currency'), - fieldtype: 'Read Only' - }, - { - fieldtype: 'Text', - label: __('About your Company'), - fieldname: 'company_description' - } - ]; - - this.field_group = new frappe.ui.FieldGroup({ - parent: this.$form_container, - fields - }); - - this.field_group.make(); - - const default_company = frappe.defaults.get_default('company'); - this.field_group.set_value('company', default_company); - - this.$form_container.find('.form-column').append(` -
    - -
    - `); - - this.$form_container.find('.form-message').removeClass('hidden small').addClass('h4').text(__('Become a Seller')) - - this.$form_container.on('click', '.btn-register', (e) => { - const form_values = this.field_group.get_values(); - - let values_filled = true; - const mandatory_fields = ['company', 'company_email', 'company_description']; - mandatory_fields.forEach(field => { - const value = form_values[field]; - if (!value) { - this.field_group.set_df_property(field, 'reqd', 1); - values_filled = false; - } - }); - if (!values_filled) return; - - frappe.call({ - method: 'erpnext.hub_node.doctype.hub_settings.hub_settings.register_seller', - args: form_values, - btn: $(e.currentTarget) - }).then(() => { - frappe.set_route('marketplace', 'publish'); - - // custom jquery event - this.$wrapper.trigger('seller-registered'); - }); - }); - } -} - -erpnext.hub.Profile = class Profile extends SubPage { - make_wrapper() { - super.make_wrapper(); - } - - refresh() { - this.get_hub_seller_profile(this.keyword) - .then(profile => this.render(profile)); - } - - get_hub_seller_profile() { - return hub.call('get_hub_seller_profile', { hub_seller: hub.settings.company_email }); - } - - render(profile) { - const p = profile; - const content_by_log_type = this.get_content_by_log_type(); - - let activity_logs = (p.hub_seller_activity || []).sort((a, b) => { - return new Date(b.creation) - new Date(a.creation); - }); - - const timeline_items_html = activity_logs - .map(log => { - const stats = JSON.parse(log.stats); - const no_of_items = stats && stats.push_update || ''; - - const content = content_by_log_type[log.type]; - const message = content.get_message(no_of_items); - const icon = content.icon; - return this.get_timeline_log_item(log.pretty_date, message, icon); - }) - .join(''); - - const profile_html = `
    -
    -
    - -
    -
    -
    -
    -
    - -
    -
    -
    -

    ${p.company}

    -
    -

    ${p.country}

    -

    ${p.site_name}

    -
    -
    -
    - ${'description' - ? `

    ${p.company_description}

    ` - : `

    __('No description') -

    -
    - -
    -
    - ${timeline_items_html} -
    -
    - -
    `; - - this.$wrapper.html(profile_html); - } - - get_timeline_log_item(pretty_date, message, icon) { - return `
    -
    - - ${pretty_date} ${message} -
    -
    `; - } - - get_content_by_log_type() { - return { - "Created": { - icon: 'octicon-heart', - get_message: () => 'Joined Marketplace' - }, - "Items Publish": { - icon: 'octicon-bookmark', - get_message: (no_of_items) => - `Published ${no_of_items} product${no_of_items > 1 ? 's' : ''} to Marketplace` - } - } - } -} - -erpnext.hub.Publish = class Publish extends SubPage { - make_wrapper() { - super.make_wrapper(); - this.items_to_publish = []; - this.unpublished_items = []; - this.fetched_items = []; - - frappe.realtime.on("items-sync", (data) => { - this.$wrapper.find('.progress-bar').css('width', data.progress_percent+'%'); - - if(data.progress_percent === 100 || data.progress_percent === '100') { - setTimeout(() => { - hub.settings.sync_in_progress = 0; - frappe.db.get_doc('Hub Settings') - .then(doc => { - hub.settings = doc; - this.refresh(); - }); - }, 500); - } - }); - } - - 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(this.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(); - - if(hub.settings.last_sync_datetime) { - this.show_message(`Last sync was ${comment_when(hub.settings.last_sync_datetime)}. - See your Published Products.`); - } - - this.get_items_and_render(); - } - - get_publishing_header() { - const title_html = `${__('Select Products to Publish')}`; - - const subtitle_html = `

    - ${__(`Only products with an image, description and category can be published. - Please update them if an item in your inventory does not appear.`)} -

    `; - - const publish_button_html = ``; - - return $(` -
    -
    - ${title_html} - ${subtitle_html} -
    - ${publish_button_html} -
    - `); - } - - setup_publishing_events() { - this.$wrapper.find('.publish-items').on('click', () => { - this.publish_selected_items() - .then(this.refresh.bind(this)) - }); - - this.$wrapper.on('click', '.hub-card', (e) => { - const $target = $(e.currentTarget); - $target.toggleClass('active'); - - // Get total items - const total_items = this.$wrapper.find('.hub-card.active').length; - - 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', total_items === 0); - }); - } - - show_message(message) { - const $message = $(`
    -

    - - ${message} - - -

    -
    `); - - $message.find('.octicon-x').on('click', () => { - $message.remove(); - }); - - this.$wrapper.prepend($message); - } - - make_publish_in_progress_state() { - this.$wrapper.empty(); - - this.$wrapper.append(this.show_publish_progress()); - - const subtitle_html = `

    - ${__(`Only products with an image, description and category can be published. - Please update them if an item in your inventory does not appear.`)} -

    `; - - this.$wrapper.append(subtitle_html); - - // Show search list with only desctiption, 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_to_publish.length - ? this.items_to_publish - : JSON.parse(hub.settings.custom_data); - - const $publish_progress = $(`
    -

    ${__(`Syncing ${items_to_publish.length} Products`)}

    -
    -
    -
    - -
    `); - - 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); - } - - get_valid_items() { - if(this.unpublished_items.length) { - return this.unpublished_items; - } - return frappe.call( - 'erpnext.hub_node.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")); - }); - - this.unpublished_items = this.fetched_items.filter(item => { - return !item_codes_to_publish.includes(item.item_code); - }); - - const items_to_publish = this.fetched_items.filter(item => { - return item_codes_to_publish.includes(item.item_code); - }); - this.items_to_publish = items_to_publish; - - return frappe.call( - 'erpnext.hub_node.publish_selected_items', - { - items_to_publish: item_codes_to_publish - } - ) - } -} - -erpnext.hub.PublishedProducts = class PublishedProducts extends SubPage { - get_items_and_render() { - this.$wrapper.find('.hub-card-container').empty(); - this.get_published_products() - .then(items => this.render(items)); - } - - refresh() { - this.get_items_and_render(); - } - - render(items) { - const items_container = $(get_item_card_container_html(items, __('Your Published Products'))); - this.$wrapper.append(items_container); - } - - get_published_products() { - return hub.call('get_items_by_seller', { hub_seller: hub.settings.company_email }); - } -} - -erpnext.hub.NotFound = class NotFound extends SubPage { - refresh() { - this.$wrapper.html(get_empty_state( - __('Sorry! I could not find what you were looking for.'), - `` - )); - } -} - -function get_empty_state(message, action) { - return `
    -

    ${message}

    - ${action ? `

    ${action}

    `: ''} -
    `; -} - -function get_item_card_container_html(items, title='', get_item_html = get_item_card_html) { - const items_html = (items || []).map(item => get_item_html(item)).join(''); - const title_html = title - ? `
    - ${title} -
    ` - : ''; - - const html = `
    - ${title_html} - ${items_html} -
    `; - - return html; -} - -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; - - // Subtitle - let subtitle = [comment_when(item.creation)]; - const rating = item.average_rating; - if (rating > 0) { - subtitle.push(rating + ``) - } - subtitle.push(company_name); - - let dot_spacer = ''; - subtitle = subtitle.join(dot_spacer); - - const item_html = ` -
    -
    -
    -
    ${title}
    -
    ${subtitle}
    -
    -
    - -
    -
    -
    -
    - `; - - 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 + ``) - } - subtitle.push(company_name); - - let dot_spacer = ''; - subtitle = subtitle.join(dot_spacer); - - const edit_item_button = `
    - -
    `; - - const item_html = ` -
    -
    -
    -
    ${title}
    -
    ${subtitle}
    - -
    -
    - -
    -
    - ${edit_item_button} -
    -
    -
    -
    -
    - `; - - return item_html; -} - - -function get_rating_html(rating) { - 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; -} - -function make_search_bar({wrapper, on_search, placeholder = __('Search for anything')}) { - const $search = $(` -
    - -
    ` - ); - wrapper.append($search); - const $search_input = $search.find('input'); - - $search_input.on('keydown', frappe.utils.debounce((e) => { - if (e.which === frappe.ui.keyCode.ENTER) { - const search_value = $search_input.val(); - on_search(search_value); - } - }, 300)); -} - -// caching - -erpnext.hub.cache = {}; -hub.call = function call_hub_method(method, args={}) { - return new Promise((resolve, reject) => { - - // cache - const key = method + JSON.stringify(args); - if (erpnext.hub.cache[key]) { - resolve(erpnext.hub.cache[key]); - } - - // cache invalidation after 5 minutes - const timeout = 5 * 60 * 1000; - - setTimeout(() => { - delete erpnext.hub.cache[key]; - }, timeout); - - frappe.call({ - method: 'erpnext.hub_node.call_hub_method', - args: { - method, - params: args - } - }) - .then(r => { - if (r.message) { - if (r.message.error) { - frappe.throw({ - title: __('Marketplace Error'), - message: r.message.error - }); - } - - erpnext.hub.cache[key] = r.message; - resolve(r.message) - } - reject(r) - }) - .fail(reject) - }); -} diff --git a/erpnext/public/js/hub/pages/base_page.js b/erpnext/public/js/hub/pages/base_page.js new file mode 100644 index 0000000000..70248da366 --- /dev/null +++ b/erpnext/public/js/hub/pages/base_page.js @@ -0,0 +1,35 @@ +export default class SubPage { + constructor(parent, options) { + this.$parent = $(parent); + this.make_wrapper(options); + + // handle broken images after every render + if (this.render) { + this._render = this.render.bind(this); + + this.render = (...args) => { + this._render(...args); + frappe.dom.handle_broken_images(this.$wrapper); + } + } + } + + make_wrapper() { + const page_name = frappe.get_route()[1]; + this.$wrapper = $(`
    `).appendTo(this.$parent); + this.hide(); + } + + empty() { + this.$wrapper.empty(); + } + + show() { + this.refresh(); + this.$wrapper.show(); + } + + hide() { + this.$wrapper.hide(); + } +} \ No newline at end of file diff --git a/erpnext/public/js/hub/pages/category.js b/erpnext/public/js/hub/pages/category.js new file mode 100644 index 0000000000..21dcb328fd --- /dev/null +++ b/erpnext/public/js/hub/pages/category.js @@ -0,0 +1,27 @@ +import SubPage from './base_page'; +import { get_item_card_container_html } from '../helpers'; + +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) { + this.$wrapper.find('.hub-card-container').empty(); + 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.append(html) + } +} \ No newline at end of file diff --git a/erpnext/public/js/hub/pages/favourites.js b/erpnext/public/js/hub/pages/favourites.js new file mode 100644 index 0000000000..9605eb19b0 --- /dev/null +++ b/erpnext/public/js/hub/pages/favourites.js @@ -0,0 +1,21 @@ +import SubPage from './base_page'; +import { get_item_card_container_html } from '../helpers'; + +erpnext.hub.Favourites = class Favourites extends SubPage { + refresh() { + this.get_favourites() + .then(items => { + this.render(items); + }); + } + + get_favourites() { + return hub.call('get_item_favourites'); + } + + render(items) { + this.$wrapper.find('.hub-card-container').empty(); + const html = get_item_card_container_html(items, __('Favourites')); + this.$wrapper.append(html) + } +} \ No newline at end of file diff --git a/erpnext/public/js/hub/pages/home.js b/erpnext/public/js/hub/pages/home.js new file mode 100644 index 0000000000..ff37e812c3 --- /dev/null +++ b/erpnext/public/js/hub/pages/home.js @@ -0,0 +1,41 @@ +import SubPage from './base_page'; +import { make_search_bar, get_item_card_container_html } from '../helpers'; + +erpnext.hub.Home = class Home extends SubPage { + make_wrapper() { + super.make_wrapper(); + + make_search_bar({ + wrapper: this.$wrapper, + on_search: keyword => { + frappe.set_route('marketplace', 'search', keyword); + } + }); + } + + refresh() { + this.get_items_and_render(); + } + + get_items_and_render() { + this.$wrapper.find('.hub-card-container').empty(); + this.get_data() + .then(data => { + this.render(data); + }); + } + + get_data() { + return hub.call('get_data_for_homepage', { country: frappe.defaults.get_user_default('country') }); + } + + render(data) { + let html = get_item_card_container_html(data.random_items, __('Explore')); + this.$wrapper.append(html); + + if (data.items_by_country.length) { + html = get_item_card_container_html(data.items_by_country, __('Near you')); + this.$wrapper.append(html); + } + } +} \ No newline at end of file diff --git a/erpnext/public/js/hub/pages/item.js b/erpnext/public/js/hub/pages/item.js new file mode 100644 index 0000000000..dabddeaab7 --- /dev/null +++ b/erpnext/public/js/hub/pages/item.js @@ -0,0 +1,327 @@ +import SubPage from './base_page'; +import { get_rating_html } from '../helpers'; + +erpnext.hub.Item = class Item extends SubPage { + make_wrapper() { + super.make_wrapper(); + this.setup_events(); + } + + refresh() { + this.show_skeleton(); + this.hub_item_code = frappe.get_route()[2]; + + this.own_item = false; + + this.get_item(this.hub_item_code) + .then(item => { + this.own_item = item.hub_seller === hub.settings.company_email; + this.item = item; + this.render(item); + }); + } + + show_skeleton() { + const skeleton = `
    +
    +
    +
    +
    +
    +

    Name

    +
    +

    Details

    +

    Ratings

    +
    +
    +
    +

    Desc

    +

    Desc

    +
    +
    +
    +
    `; + + this.$wrapper.html(skeleton); + } + + setup_events() { + this.$wrapper.on('click', '.btn-contact-seller', () => { + const d = new frappe.ui.Dialog({ + title: __('Send a message'), + fields: [ + { + fieldname: 'to', + fieldtype: 'Read Only', + label: __('To'), + default: this.item.company + }, + { + fieldtype: 'Text', + fieldname: 'message', + label: __('Message') + } + ] + }); + + d.show(); + }); + } + + get_item(hub_item_code) { + return hub.call('get_item_details', { hub_item_code }); + } + + render(item) { + const title = item.item_name || item.name; + const seller = item.company; + + const who = __('Posted By {0}', [seller]); + const when = comment_when(item.creation); + + const city = item.city ? item.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.average_rating); + const rating_count = item.no_of_ratings > 0 ? `${item.no_of_ratings} reviews` : __('No reviews yet'); + + let edit_buttons_html = ''; + + if(this.own_item) { + edit_buttons_html = `
    + + +
    `; + } + + const html = ` +
    +
    +
    + +
    +
    +
    +
    +
    + +
    +
    +
    +

    ${title}

    +
    +

    ${where}${dot_spacer}${when}

    +

    ${rating_html} (${rating_count})

    +
    +
    +
    + ${description ? + `${__('Description')} +

    ${description}

    + ` : `

    ${__('No description')}

    ` + } +

    + ${edit_buttons_html} +
    +
    + +
    +
    +
    +
    + Seller Information +
    +
    + +
    +
    + + +
    +
    + +
    + +
    +
    + `; + + this.$wrapper.html(html); + + if(this.own_item) { + this.bind_edit_buttons(); + } + + this.make_review_area(); + + this.get_reviews() + .then(reviews => { + this.reviews = reviews; + this.render_reviews(reviews); + }); + } + + bind_edit_buttons() { + this.edit_dialog = new frappe.ui.Dialog({ + title: "Edit Your Product", + fields: [] + }); + + this.$wrapper.find('.edit-item').on('click', this.on_edit.bind(this)); + this.$wrapper.find('.unpublish').on('click', this.on_unpublish.bind(this)); + } + + on_edit() { + this.edit_dialog.show(); + } + + on_unpublish() { + if(!this.unpublish_dialog) { + this.unpublish_dialog = new frappe.ui.Dialog({ + title: "Edit Your Product", + fields: [] + }); + } + + this.unpublish_dialog.show(); + } + + make_review_area() { + this.comment_area = new frappe.ui.ReviewArea({ + parent: this.$wrapper.find('.timeline-head').empty(), + mentions: [], + on_submit: (values) => { + values.user = frappe.session.user; + values.username = frappe.session.user_fullname; + + hub.call('add_item_review', { + hub_item_code: this.hub_item_code, + review: JSON.stringify(values) + }) + .then(review => { + this.reviews = this.reviews || []; + this.reviews.push(review); + this.render_reviews(this.reviews); + + this.comment_area.reset(); + }); + } + }); + } + + get_reviews() { + return hub.call('get_item_reviews', { hub_item_code: this.hub_item_code }).catch(() => {}); + } + + render_reviews(reviews=[]) { + this.$wrapper.find('.timeline-items').empty(); + + reviews.sort((a, b) => { + if (a.modified > b.modified) { + return -1; + } + + if (a.modified < b.modified) { + return 1; + } + + return 0; + }); + + reviews.forEach(review => this.render_review(review)); + } + + render_review(review) { + let username = review.username || review.user || __("Anonymous"); + + let image_html = review.user_image + ? `
    ` + : `
    ${frappe.get_abbr(username)}
    ` + + let edit_html = review.own + ? ` +
    + + ${'data.edit'} + +
    ` + : ''; + + let rating_html = get_rating_html(review.rating); + + 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 `
    + +
    +
    +
    ${edit_html}
    + +
    + + ${image_html} + + +
    + + + ${data.username} + + + + + +
    +
    +
    +
    +

    + ${rating_html} +

    +
    ${data.subject}
    +

    + ${data.content} +

    +
    +
    +
    +
    +
    `; + } +} \ No newline at end of file diff --git a/erpnext/public/js/hub/pages/not_found.js b/erpnext/public/js/hub/pages/not_found.js new file mode 100644 index 0000000000..a83d8817de --- /dev/null +++ b/erpnext/public/js/hub/pages/not_found.js @@ -0,0 +1,10 @@ +import SubPage from './base_page'; + +erpnext.hub.NotFound = class NotFound extends SubPage { + refresh() { + this.$wrapper.html(get_empty_state( + __('Sorry! I could not find what you were looking for.'), + `` + )); + } +} diff --git a/erpnext/public/js/hub/pages/profile.js b/erpnext/public/js/hub/pages/profile.js new file mode 100644 index 0000000000..6dd1f8716a --- /dev/null +++ b/erpnext/public/js/hub/pages/profile.js @@ -0,0 +1,98 @@ +import SubPage from './base_page'; + +erpnext.hub.Profile = class Profile extends SubPage { + make_wrapper() { + super.make_wrapper(); + } + + refresh() { + this.get_hub_seller_profile(this.keyword) + .then(profile => this.render(profile)); + } + + get_hub_seller_profile() { + return hub.call('get_hub_seller_profile', { hub_seller: hub.settings.company_email }); + } + + render(profile) { + const p = profile; + const content_by_log_type = this.get_content_by_log_type(); + + let activity_logs = (p.hub_seller_activity || []).sort((a, b) => { + return new Date(b.creation) - new Date(a.creation); + }); + + const timeline_items_html = activity_logs + .map(log => { + const stats = JSON.parse(log.stats); + const no_of_items = stats && stats.push_update || ''; + + const content = content_by_log_type[log.type]; + const message = content.get_message(no_of_items); + const icon = content.icon; + return this.get_timeline_log_item(log.pretty_date, message, icon); + }) + .join(''); + + const profile_html = `
    +
    +
    + +
    +
    +
    +
    +
    + +
    +
    +
    +

    ${p.company}

    +
    +

    ${p.country}

    +

    ${p.site_name}

    +
    +
    +
    + ${'description' + ? `

    ${p.company_description}

    ` + : `

    __('No description') +

    +
    + +
    +
    + ${timeline_items_html} +
    +
    + +
    `; + + this.$wrapper.html(profile_html); + } + + get_timeline_log_item(pretty_date, message, icon) { + return `
    +
    + + ${pretty_date} ${message} +
    +
    `; + } + + get_content_by_log_type() { + return { + "Created": { + icon: 'octicon-heart', + get_message: () => 'Joined Marketplace' + }, + "Items Publish": { + icon: 'octicon-bookmark', + get_message: (no_of_items) => + `Published ${no_of_items} product${no_of_items > 1 ? 's' : ''} to Marketplace` + } + } + } +} \ No newline at end of file diff --git a/erpnext/public/js/hub/pages/publish.js b/erpnext/public/js/hub/pages/publish.js new file mode 100644 index 0000000000..6e8caabb4c --- /dev/null +++ b/erpnext/public/js/hub/pages/publish.js @@ -0,0 +1,228 @@ +import SubPage from './base_page'; +import { make_search_bar, get_item_card_container_html, get_local_item_card_html } from '../helpers'; + +erpnext.hub.Publish = class Publish extends SubPage { + make_wrapper() { + super.make_wrapper(); + this.items_to_publish = []; + this.unpublished_items = []; + this.fetched_items = []; + + frappe.realtime.on("items-sync", (data) => { + this.$wrapper.find('.progress-bar').css('width', data.progress_percent+'%'); + + if(data.progress_percent === 100 || data.progress_percent === '100') { + setTimeout(() => { + hub.settings.sync_in_progress = 0; + frappe.db.get_doc('Hub Settings') + .then(doc => { + hub.settings = doc; + this.refresh(); + }); + }, 500); + } + }); + } + + 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(this.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(); + + if(hub.settings.last_sync_datetime) { + this.show_message(`Last sync was ${comment_when(hub.settings.last_sync_datetime)}. + See your Published Products.`); + } + + this.get_items_and_render(); + } + + get_publishing_header() { + const title_html = `${__('Select Products to Publish')}`; + + const subtitle_html = `

    + ${__(`Only products with an image, description and category can be published. + Please update them if an item in your inventory does not appear.`)} +

    `; + + const publish_button_html = ``; + + return $(` +
    +
    + ${title_html} + ${subtitle_html} +
    + ${publish_button_html} +
    + `); + } + + setup_publishing_events() { + this.$wrapper.find('.publish-items').on('click', () => { + this.publish_selected_items() + .then(this.refresh.bind(this)) + }); + + this.$wrapper.on('click', '.hub-card', (e) => { + const $target = $(e.currentTarget); + $target.toggleClass('active'); + + // Get total items + const total_items = this.$wrapper.find('.hub-card.active').length; + + 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', total_items === 0); + }); + } + + show_message(message) { + const $message = $(`
    +

    + + ${message} + + +

    +
    `); + + $message.find('.octicon-x').on('click', () => { + $message.remove(); + }); + + this.$wrapper.prepend($message); + } + + make_publish_in_progress_state() { + this.$wrapper.empty(); + + this.$wrapper.append(this.show_publish_progress()); + + const subtitle_html = `

    + ${__(`Only products with an image, description and category can be published. + Please update them if an item in your inventory does not appear.`)} +

    `; + + this.$wrapper.append(subtitle_html); + + // Show search list with only desctiption, 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_to_publish.length + ? this.items_to_publish + : JSON.parse(hub.settings.custom_data); + + const $publish_progress = $(`
    +

    ${__(`Syncing ${items_to_publish.length} Products`)}

    +
    +
    +
    + +
    `); + + 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); + } + + get_valid_items() { + if(this.unpublished_items.length) { + return this.unpublished_items; + } + return frappe.call( + 'erpnext.hub_node.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")); + }); + + this.unpublished_items = this.fetched_items.filter(item => { + return !item_codes_to_publish.includes(item.item_code); + }); + + const items_to_publish = this.fetched_items.filter(item => { + return item_codes_to_publish.includes(item.item_code); + }); + this.items_to_publish = items_to_publish; + + return frappe.call( + 'erpnext.hub_node.publish_selected_items', + { + items_to_publish: item_codes_to_publish + } + ) + } +} \ No newline at end of file diff --git a/erpnext/public/js/hub/pages/published_products.js b/erpnext/public/js/hub/pages/published_products.js new file mode 100644 index 0000000000..f21c6fa7ff --- /dev/null +++ b/erpnext/public/js/hub/pages/published_products.js @@ -0,0 +1,23 @@ +import SubPage from './base_page'; +import { get_item_card_container_html } from '../helpers'; + +erpnext.hub.PublishedProducts = class PublishedProducts extends SubPage { + get_items_and_render() { + this.$wrapper.find('.hub-card-container').empty(); + this.get_published_products() + .then(items => this.render(items)); + } + + refresh() { + this.get_items_and_render(); + } + + render(items) { + const items_container = $(get_item_card_container_html(items, __('Your Published Products'))); + this.$wrapper.append(items_container); + } + + get_published_products() { + return hub.call('get_items_by_seller', { hub_seller: hub.settings.company_email }); + } +} \ No newline at end of file diff --git a/erpnext/public/js/hub/pages/register.js b/erpnext/public/js/hub/pages/register.js new file mode 100644 index 0000000000..d8966f12a1 --- /dev/null +++ b/erpnext/public/js/hub/pages/register.js @@ -0,0 +1,110 @@ +import SubPage from './base_page'; + +erpnext.hub.Register = class Register extends SubPage { + make_wrapper() { + super.make_wrapper(); + this.$register_container = $(`
    `) + .appendTo(this.$wrapper); + this.$form_container = $('
    ') + .appendTo(this.$wrapper); + } + + refresh() { + this.$register_container.empty(); + this.$form_container.empty(); + this.render(); + } + + render() { + this.make_field_group(); + } + + make_field_group() { + const fields = [ + { + fieldtype: 'Link', + fieldname: 'company', + label: __('Company'), + options: 'Company', + onchange: () => { + const value = this.field_group.get_value('company'); + + if (value) { + frappe.db.get_doc('Company', value) + .then(company => { + this.field_group.set_values({ + country: company.country, + company_email: company.email, + currency: company.default_currency + }); + }); + } + } + }, + { + fieldname: 'company_email', + label: __('Email'), + fieldtype: 'Data' + }, + { + fieldname: 'country', + label: __('Country'), + fieldtype: 'Read Only' + }, + { + fieldname: 'currency', + label: __('Currency'), + fieldtype: 'Read Only' + }, + { + fieldtype: 'Text', + label: __('About your Company'), + fieldname: 'company_description' + } + ]; + + this.field_group = new frappe.ui.FieldGroup({ + parent: this.$form_container, + fields + }); + + this.field_group.make(); + + const default_company = frappe.defaults.get_default('company'); + this.field_group.set_value('company', default_company); + + this.$form_container.find('.form-column').append(` +
    + +
    + `); + + this.$form_container.find('.form-message').removeClass('hidden small').addClass('h4').text(__('Become a Seller')) + + this.$form_container.on('click', '.btn-register', (e) => { + const form_values = this.field_group.get_values(); + + let values_filled = true; + const mandatory_fields = ['company', 'company_email', 'company_description']; + mandatory_fields.forEach(field => { + const value = form_values[field]; + if (!value) { + this.field_group.set_df_property(field, 'reqd', 1); + values_filled = false; + } + }); + if (!values_filled) return; + + frappe.call({ + method: 'erpnext.hub_node.doctype.hub_settings.hub_settings.register_seller', + args: form_values, + btn: $(e.currentTarget) + }).then(() => { + frappe.set_route('marketplace', 'publish'); + + // custom jquery event + this.$wrapper.trigger('seller-registered'); + }); + }); + } +} diff --git a/erpnext/public/js/hub/pages/search.js b/erpnext/public/js/hub/pages/search.js new file mode 100644 index 0000000000..276c9bc668 --- /dev/null +++ b/erpnext/public/js/hub/pages/search.js @@ -0,0 +1,34 @@ +import SubPage from './base_page'; +import { make_search_bar, get_item_card_container_html } from '../helpers'; + +erpnext.hub.SearchPage = class SearchPage extends SubPage { + make_wrapper() { + super.make_wrapper(); + + make_search_bar({ + wrapper: this.$wrapper, + on_search: keyword => { + frappe.set_route('marketplace', 'search', keyword); + } + }); + } + + refresh() { + this.keyword = frappe.get_route()[2] || ''; + this.$wrapper.find('input').val(this.keyword); + + this.get_items_by_keyword(this.keyword) + .then(items => this.render(items)); + } + + get_items_by_keyword(keyword) { + return hub.call('get_items_by_keyword', { keyword }); + } + + render(items) { + this.$wrapper.find('.hub-card-container').remove(); + const title = this.keyword ? __('Search results for "{0}"', [this.keyword]) : ''; + const html = get_item_card_container_html(items, title); + this.$wrapper.append(html); + } +} \ No newline at end of file