From f585197045428bcbed6af2174abaf432adf7c8dd Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Mon, 23 Jul 2018 11:55:07 +0530 Subject: [PATCH 001/238] Cleanup homepage --- erpnext/public/js/hub/hub_listing.js | 233 +++++++++++++++++++-------- erpnext/public/less/hub.less | 67 +++++++- 2 files changed, 232 insertions(+), 68 deletions(-) diff --git a/erpnext/public/js/hub/hub_listing.js b/erpnext/public/js/hub/hub_listing.js index 0ff79708d7..9ac1b848ea 100644 --- a/erpnext/public/js/hub/hub_listing.js +++ b/erpnext/public/js/hub/hub_listing.js @@ -22,7 +22,7 @@ erpnext.hub.HubListing = class HubListing extends frappe.views.BaseList { const title = this.page_title; let iconHtml = ``; let titleHtml = `${title}`; - this.page.set_title(iconHtml + titleHtml, '', false, title); + this.page.set_title(titleHtml, '', false, title); } setup_fields() { @@ -36,6 +36,8 @@ erpnext.hub.HubListing = class HubListing extends frappe.views.BaseList { }); } + setup_filter_area() { } + get_meta() { return new Promise(resolve => frappe.call('erpnext.hub_node.get_meta', {doctype: this.doctype}, resolve)); @@ -72,12 +74,12 @@ erpnext.hub.HubListing = class HubListing extends frappe.views.BaseList { } 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) - }); + // 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() { @@ -88,6 +90,21 @@ erpnext.hub.HubListing = class HubListing extends frappe.views.BaseList { 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() { @@ -146,7 +163,10 @@ erpnext.hub.HubListing = class HubListing extends frappe.views.BaseList { if (this.start === 0) { // ${this.getHeaderHtml()} this.$result.html(` -
+
+
+ Recently Published +
${html}
`); @@ -366,23 +386,23 @@ erpnext.hub.ItemListing = class ItemListing extends erpnext.hub.HubListing { } 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' - } - ]; + // 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() { @@ -414,6 +434,10 @@ erpnext.hub.ItemListing = class ItemListing extends erpnext.hub.HubListing { setup_side_bar() { super.setup_side_bar(); + this.setup_new_sidebar(); + + return; + let $pitch = $(`
+
  • + Browse +
  • +
  • + Favorites +
  • +
  • + Become a seller +
  • + + `); + + 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(` +
      + ${sidebar_items.join('')} +
    + `); + }); + } + 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; + const filter = {}; + + if (this.search_value) { + filter.item_name = ['like', `%${this.search_value}%`]; } - return filters; + + 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) { @@ -485,11 +558,14 @@ erpnext.hub.ItemListing = class ItemListing extends erpnext.hub.HubListing { }); } - item_html(item) { + 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 _class = !item[this.imageFieldName] ? 'no-image' : ''; + + 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']; @@ -507,41 +583,66 @@ erpnext.hub.ItemListing = class ItemListing extends erpnext.hub.HubListing { 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}) -
    - -
    -
    - -
    - - +
    +
    +
    + - +
    + ${ratingHtml} + (${reviewLength}) +
    + +
    +
    +
    + `; + item_html = ` +
    +
    +
    +
    ${title}
    +
    ${subtitle}
    +
    +
    + +
    +
    `; diff --git a/erpnext/public/less/hub.less b/erpnext/public/less/hub.less index bdca28f1e7..ef08fde270 100644 --- a/erpnext/public/less/hub.less +++ b/erpnext/public/less/hub.less @@ -6,8 +6,71 @@ body[data-route^="Hub/"] { height: 40px; } - .hub-page-title { - margin-left: 10px; + .layout-main-section { + border: none; + } + + .frappe-list { + padding-top: 25px; + font-size: @text-medium; + + @media (max-width: @screen-xs) { + padding-left: 20px; + padding-right: 20px; + } + } + + .hub-card { + border: 1px solid @border-color; + margin-bottom: 25px; + border-radius: 4px; + overflow: hidden; + cursor: pointer; + } + + .hub-card-header { + padding: 12px 15px; + height: 60px; + } + + .hub-card-body { + height: 200px; + } + + .hub-card-image { + min-width: 100%; + width: 100%; + } + + .hub-search-container { + margin-bottom: 20px; + + input { + font-size: @text-medium; + height: 32px; + } + } + + .hub-sidebar { + padding-top: 25px; + padding-right: 15px; + } + + .hub-sidebar-group { + margin-bottom: 10px; + } + + .hub-sidebar-item { + padding: 5px 8px; + margin-bottom: 3px; + border-radius: 4px; + border: 1px solid transparent; + + cursor: pointer; + + &.active, &:hover:not(.is-title) { + border-color: @border-color; + } } .img-wrapper { From ea2d1b0a191ac95d12845efeb4710508d2260d34 Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Mon, 23 Jul 2018 18:44:46 +0530 Subject: [PATCH 002/238] New page: marketplace - subpages: home, favourites, item, category --- erpnext/hub_node/__init__.py | 2 +- erpnext/public/js/hub/hub_factory.js | 32 +- erpnext/public/js/hub/hub_listing.js | 459 +++++++++++++++++++++++++-- erpnext/public/less/hub.less | 52 ++- 4 files changed, 503 insertions(+), 42 deletions(-) diff --git a/erpnext/hub_node/__init__.py b/erpnext/hub_node/__init__.py index 65b1386466..a5e4607122 100644 --- a/erpnext/hub_node/__init__.py +++ b/erpnext/hub_node/__init__.py @@ -21,7 +21,7 @@ def get_list(doctype, start=0, limit=20, fields=["*"], filters="{}", order_by=No response = connection.get_list(doctype, limit_start=start, limit_page_length=limit, - filters=filters, fields=fields) + filters=filters, fields=['name']) # Bad, need child tables in response listing = [] diff --git a/erpnext/public/js/hub/hub_factory.js b/erpnext/public/js/hub/hub_factory.js index 6451e1d6a9..d54787af36 100644 --- a/erpnext/public/js/hub/hub_factory.js +++ b/erpnext/public/js/hub/hub_factory.js @@ -1,6 +1,32 @@ frappe.provide('erpnext.hub.pages'); -frappe.views.HubFactory = frappe.views.Factory.extend({ +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(); + } else { + this.make('marketplace'); + } + } + + make(page_name) { + const assets = [ + '/assets/erpnext/js/hub/hub_listing.js' + ]; + + frappe.require(assets, () => { + erpnext.hub.marketplace = new erpnext.hub.Marketplace({ + parent: this.make_page(true, page_name) + }); + }); + } +} + +frappe.views.HubFactory = class HubFactory extends frappe.views.Factory { + make(route) { const page_name = frappe.get_route_str(); const page = route[1]; @@ -60,7 +86,7 @@ frappe.views.HubFactory = frappe.views.Factory.extend({ window.hub_page = erpnext.hub.pages[page_name]; } }); - }, + } render_offline_card() { let html = `
    @@ -77,4 +103,4 @@ frappe.views.HubFactory = frappe.views.Factory.extend({ return; } -}); +} diff --git a/erpnext/public/js/hub/hub_listing.js b/erpnext/public/js/hub/hub_listing.js index 9ac1b848ea..a6a9ac975b 100644 --- a/erpnext/public/js/hub/hub_listing.js +++ b/erpnext/public/js/hub/hub_listing.js @@ -1,5 +1,404 @@ 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'); + + this.$sidebar.append(` +
      +
    • + ${__('Browse')} +
    • +
    • + ${__('Favorites')} +
    • +
    • + ${__('Become a seller')} +
    • +
    + `); + + 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 = [ + `
  • + ${__('Category')} +
  • `, + `
  • + ${__('All')} +
  • `, + ...categories.map(category => ` +
  • + ${__(category)} +
  • + `) + ]; + + this.$sidebar.append(` +
      + ${sidebar_items.join('')} +
    + `); + + this.update_sidebar(); + }); + } + + make_body() { + this.$body = this.$parent.find('.layout-main-section'); + } + + update_sidebar() { + const route = frappe.get_route_str(); + const $sidebar_item = this.$sidebar.find(`[data-route="${route}"]`); + + const $siblings = this.$sidebar.find('[data-route]'); + $siblings.removeClass('active').addClass('text-muted'); + + $sidebar_item.addClass('active').removeClass('text-muted'); + } + + refresh() { + const route = frappe.get_route(); + this.subpages = this.subpages || {}; + + for (let page in this.subpages) { + this.subpages[page].hide(); + } + + if (route[1] === 'home' && !this.subpages.home) { + this.subpages.home = new erpnext.hub.Home(this.$body); + } + + if (route[1] === 'favourites' && !this.subpages.favourites) { + this.subpages.favourites = new erpnext.hub.Favourites(this.$body); + } + + if (route[1] === 'category' && route[2] && !this.subpages.category) { + this.subpages.category = new erpnext.hub.Category(this.$body); + } + + if (route[1] === 'item' && route[2] && !this.subpages.item) { + this.subpages.item = new erpnext.hub.Item(this.$body); + } + + if (!Object.keys(this.subpages).includes(route[1])) { + frappe.show_not_found(); + return; + } + + this.update_sidebar(); + frappe.utils.scroll_to(0); + this.subpages[route[1]].show(); + } +} + +class SubPage { + constructor(parent) { + this.$parent = $(parent); + this.make_wrapper(); + } + + make_wrapper() { + const page_name = frappe.get_route()[1]; + this.$wrapper = $(`
    `).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() { + const hub_item_code = frappe.get_route()[2]; + + this.get_item(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') + } +
    +
    +
    +
    +
    + Seller Information +
    +
    + +
    +
    + ${company} +

    + Contact Seller +

    +
    +
    +
    + `; + + this.$wrapper.html(html); + } +} + + + +function get_item_card_container_html(items, title) { + const html = (items || []).map(item => get_item_card_html(item)).join(''); + + return ` +
    +
    + ${title} +
    + ${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_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 = ` +
    +
    +
    +
    ${title}
    +
    ${subtitle}
    +
    +
    + +
    +
    +
    +
    + `; + + 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(); @@ -40,7 +439,7 @@ erpnext.hub.HubListing = class HubListing extends frappe.views.BaseList { get_meta() { return new Promise(resolve => - frappe.call('erpnext.hub_node.get_meta', {doctype: this.doctype}, resolve)); + frappe.call('erpnext.hub_node.get_meta', { doctype: this.doctype }, resolve)); } set_breadcrumbs() { } @@ -83,7 +482,7 @@ erpnext.hub.HubListing = class HubListing extends frappe.views.BaseList { } setup_view() { - if(frappe.route_options){ + if (frappe.route_options) { const filters = []; for (let field in frappe.route_options) { var value = frappe.route_options[field]; @@ -146,9 +545,9 @@ erpnext.hub.HubListing = class HubListing extends frappe.views.BaseList { {{ _("Payment Cancelled") }}
    -

    ${ __("Your payment is cancelled.") }

    +

    ${ __("Your payment is cancelled.")}

    + ${ __("Continue")}
    `; let page = this.page.wrapper.find('.layout-side-section') @@ -172,7 +571,7 @@ erpnext.hub.HubListing = class HubListing extends frappe.views.BaseList { `); } - if(this.data.length) { + if (this.data.length) { this.doc = this.data[0]; } @@ -226,11 +625,11 @@ erpnext.hub.HubListing = class HubListing extends frappe.views.BaseList { } get_image_html(encoded_name, src, alt_text) { - return `${ alt_text }`; + return `${alt_text}`; } get_image_placeholder(title) { - return `${ frappe.get_abbr(title) }`; + return `${frappe.get_abbr(title)}`; } loadImage(item) { @@ -241,7 +640,7 @@ erpnext.hub.HubListing = class HubListing extends frappe.views.BaseList { let placeholder = this.get_image_placeholder(title); let $container = this.$result.find(`.image-field[data-name="${encoded_name}"]`); - if(!item[this.imageFieldName]) { + if (!item[this.imageFieldName]) { $container.prepend(placeholder); $container.addClass('no-image'); } @@ -262,7 +661,7 @@ erpnext.hub.HubListing = class HubListing extends frappe.views.BaseList { } setup_quick_view() { - if(this.quick_view) return; + if (this.quick_view) return; this.quick_view = new frappe.ui.Dialog({ title: 'Quick View', @@ -312,10 +711,10 @@ erpnext.hub.HubListing = class HubListing extends frappe.views.BaseList { } setup_like() { - if(this.setup_like_done) return; + 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; + if ($(e.target).hasClass('changing')) return; $(e.target).addClass('changing'); e.preventDefault(); @@ -326,13 +725,13 @@ erpnext.hub.HubListing = class HubListing extends frappe.views.BaseList { let values = this.data_dict[name]; let heart = $(e.target); - if(heart.hasClass('like-button')) { + if (heart.hasClass('like-button')) { heart = $(e.target).find('.octicon'); } let remove = 1; - if(heart.hasClass('liked')) { + if (heart.hasClass('liked')) { // unlike heart.removeClass('liked'); } else { @@ -349,7 +748,7 @@ erpnext.hub.HubListing = class HubListing extends frappe.views.BaseList { }, callback: (r) => { let message = __("Added to Favourites"); - if(remove) { + if (remove) { message = __("Removed from Favourites"); } frappe.show_alert(message); @@ -454,7 +853,7 @@ erpnext.hub.ItemListing = class ItemListing extends erpnext.hub.HubListing { label: 'All Categories', expandable: true, - args: {parent: this.current_category}, + args: { parent: this.current_category }, method: 'erpnext.hub_node.get_categories', on_click: (node) => { this.update_category(node.label); @@ -501,8 +900,8 @@ erpnext.hub.ItemListing = class ItemListing extends erpnext.hub.HubListing { .then(r => { const categories = r.message.map(d => d.value).sort(); const sidebar_items = [ - `
  • - Category + `
  • + ${__('Category')}
  • `, `
  • All @@ -523,7 +922,7 @@ erpnext.hub.ItemListing = class ItemListing extends erpnext.hub.HubListing { } update_category(label) { - this.current_category = (label=='All Categories') ? undefined : label; + this.current_category = (label == 'All Categories') ? undefined : label; this.refresh(); } @@ -573,14 +972,14 @@ erpnext.hub.ItemListing = class ItemListing extends erpnext.hub.HubListing { const ratingAverage = reviewLength ? item.reviews .map(r => r.rating) - .reduce((a, b) => a + b, 0)/reviewLength + .reduce((a, b) => a + b, 0) / reviewLength : -1; let ratingHtml = ``; - for(var i = 0; i < 5; i++) { + for (var i = 0; i < 5; i++) { let starClass = 'fa-star'; - if(i >= ratingAverage) starClass = 'fa-star-o'; + if (i >= ratingAverage) starClass = 'fa-star-o'; ratingHtml += ``; } let dot_spacer = ''; @@ -608,7 +1007,7 @@ erpnext.hub.ItemListing = class ItemListing extends erpnext.hub.HubListing { (${reviewLength})
  • @@ -651,7 +1050,7 @@ erpnext.hub.ItemListing = class ItemListing extends erpnext.hub.HubListing { }; -erpnext.hub.Favourites = class Favourites extends erpnext.hub.ItemListing { +erpnext.hub.Favourites2 = class Favourites extends erpnext.hub.ItemListing { constructor(opts) { super(opts); this.show(); @@ -702,18 +1101,18 @@ erpnext.hub.Favourites = class Favourites extends erpnext.hub.ItemListing { } update_category(label) { - this.current_category = (label=='All Categories') ? undefined : label; + this.current_category = (label == 'All Categories') ? undefined : label; this.refresh(); } get_filters_for_args() { - if(!this.filter_area) return; + 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) { + if (this.current_category) { filters['hub_category'] = this.current_category; } return filters; @@ -772,10 +1171,10 @@ erpnext.hub.CompanyListing = class CompanyListing extends erpnext.hub.HubListing 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]]; - }); + // this.filter_area.get().forEach(f => { + // let field = f[1] !== 'name' ? f[1] : 'company_name'; + // filters[field] = [f[2], f[3]]; + // }); return filters; } diff --git a/erpnext/public/less/hub.less b/erpnext/public/less/hub.less index ef08fde270..48d52ca5aa 100644 --- a/erpnext/public/less/hub.less +++ b/erpnext/public/less/hub.less @@ -1,18 +1,15 @@ @import "../../../../frappe/frappe/public/less/variables.less"; -body[data-route^="Hub/"] { - .hub-icon { - width: 40px; - height: 40px; +body[data-route^="marketplace/"] { + .layout-side-section { + padding-top: 25px; + padding-right: 25px; } .layout-main-section { border: none; - } - - .frappe-list { - padding-top: 25px; font-size: @text-medium; + padding-top: 25px; @media (max-width: @screen-xs) { padding-left: 20px; @@ -26,17 +23,32 @@ body[data-route^="Hub/"] { border-radius: 4px; overflow: hidden; cursor: pointer; + + &:hover .hub-card-overlay { + display: block; + } } .hub-card-header { padding: 12px 15px; height: 60px; + border-bottom: 1px solid @border-color; } .hub-card-body { + position: relative; height: 200px; } + .hub-card-overlay { + display: none; + position: absolute; + top: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.1); + } + .hub-card-image { min-width: 100%; width: 100%; @@ -73,6 +85,30 @@ body[data-route^="Hub/"] { } } + .hub-item-image { + border: 1px solid @border-color; + border-radius: 4px; + overflow: hidden; + height: 200px; + width: 200px; + display: flex; + align-items: center; + } + + .hub-item-seller img { + width: 50px; + height: 50px; + border-radius: 4px; + border: 1px solid @border-color; + } +} + +body[data-route^="Hub/"] { + .hub-icon { + width: 40px; + height: 40px; + } + .img-wrapper { border: 1px solid #d1d8dd; border-radius: 3px; From 0fbaa725041c35e32127ba30c29c7dbb2e659118 Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Wed, 25 Jul 2018 00:49:11 +0530 Subject: [PATCH 003/238] Add review area --- .../doctype/hub_settings/hub_settings.py | 2 +- erpnext/public/js/hub/hub_listing.js | 130 +++++++++++++++++- 2 files changed, 126 insertions(+), 6 deletions(-) diff --git a/erpnext/hub_node/doctype/hub_settings/hub_settings.py b/erpnext/hub_node/doctype/hub_settings/hub_settings.py index 15ee4b7da3..58b946e1af 100644 --- a/erpnext/hub_node/doctype/hub_settings/hub_settings.py +++ b/erpnext/hub_node/doctype/hub_settings/hub_settings.py @@ -12,7 +12,7 @@ from six import string_types hub_url = "https://hubmarket.org" # hub_url = "http://159.89.175.122" -# hub_url = "http://erpnext.hub:8000" +# hub_url = "http://erpnext.hub:8001" class OAuth2Session(): def __init__(self, headers): diff --git a/erpnext/public/js/hub/hub_listing.js b/erpnext/public/js/hub/hub_listing.js index a6a9ac975b..f61272d3f8 100644 --- a/erpnext/public/js/hub/hub_listing.js +++ b/erpnext/public/js/hub/hub_listing.js @@ -25,7 +25,7 @@ erpnext.hub.Marketplace = class Marketplace { } make_sidebar() { - this.$sidebar = this.$parent.find('.layout-side-section'); + this.$sidebar = this.$parent.find('.layout-side-section').addClass('hidden-xs'); this.$sidebar.append(`
      @@ -237,9 +237,9 @@ erpnext.hub.Category = class Category extends SubPage { erpnext.hub.Item = class Item extends SubPage { refresh() { - const hub_item_code = frappe.get_route()[2]; + this.hub_item_code = frappe.get_route()[2]; - this.get_item(hub_item_code) + this.get_item(this.hub_item_code) .then(item => { this.render(item); }); @@ -285,6 +285,11 @@ erpnext.hub.Item = class Item extends SubPage { const html = `
      +
      +
      + +
      +
      @@ -321,15 +326,130 @@ erpnext.hub.Item = class Item extends SubPage {

      + +
      + +
      `; 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.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 `
      + + +
      +
      +
      ${edit_html}
      + +
      + + ${image_html} + + +
      + + + ${data.username} + + + + + +
      +
      +
      +
      +

      + ${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(''); From bc8caefc197cecc0ee69facd19820f27c5dcb52d Mon Sep 17 00:00:00 2001 From: Prateeksha Singh Date: Wed, 25 Jul 2018 10:58:56 +0530 Subject: [PATCH 004/238] [hub] add register as a seller page --- erpnext/hub_node/__init__.py | 7 + .../doctype/hub_settings/hub_settings.json | 319 +++--------------- .../doctype/hub_settings/hub_settings.py | 34 +- erpnext/public/js/hub/hub_listing.js | 197 ++++++++++- erpnext/public/less/hub.less | 27 +- 5 files changed, 301 insertions(+), 283 deletions(-) diff --git a/erpnext/hub_node/__init__.py b/erpnext/hub_node/__init__.py index a5e4607122..a94f6b1615 100644 --- a/erpnext/hub_node/__init__.py +++ b/erpnext/hub_node/__init__.py @@ -31,6 +31,13 @@ def get_list(doctype, start=0, limit=20, fields=["*"], filters="{}", order_by=No return listing + + +@frappe.whitelist() +def get_valid_items(): + items = frappe.get_list('Item') + return items + @frappe.whitelist() def get_item_favourites(start=0, limit=20, fields=["*"], order_by=None): doctype = 'Hub Item' diff --git a/erpnext/hub_node/doctype/hub_settings/hub_settings.json b/erpnext/hub_node/doctype/hub_settings/hub_settings.json index 7c7109c64c..c742c57b2b 100644 --- a/erpnext/hub_node/doctype/hub_settings/hub_settings.json +++ b/erpnext/hub_node/doctype/hub_settings/hub_settings.json @@ -14,243 +14,21 @@ "fields": [ { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, - "fieldname": "enabled", + "fieldname": "registered", "fieldtype": "Check", - "hidden": 1, + "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, - "label": "Enabled", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "suspended", - "fieldtype": "Check", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Suspended", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "enabled", - "fieldname": "hub_username", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Hub Username", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "user", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "User", - "length": 0, - "no_copy": 0, - "options": "User", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_0", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "enabled", - "fieldname": "hub_user_status", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Status", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "enabled", - "fieldname": "language", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Language", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "collapsible_depends_on": "eval:(!doc.enabled)", - "columns": 0, - "depends_on": "", - "fieldname": "seller_profile_section", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Company and Seller Profile", + "label": "Registered", "length": 0, "no_copy": 0, "permlevel": 0, @@ -268,6 +46,7 @@ }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -299,6 +78,7 @@ }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -331,6 +111,7 @@ }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -362,6 +143,7 @@ }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -394,11 +176,44 @@ }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, - "fieldname": "company_logo", + "fieldname": "currency", + "fieldtype": "Currency", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Currency", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "logo", "fieldtype": "Attach Image", "hidden": 0, "ignore_user_permissions": 0, @@ -425,11 +240,12 @@ }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, - "fieldname": "seller_description", + "fieldname": "company_description", "fieldtype": "Text Editor", "hidden": 0, "ignore_user_permissions": 0, @@ -456,6 +272,7 @@ }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -487,38 +304,7 @@ }, { "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "users", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Users", - "length": 0, - "no_copy": 0, - "options": "Hub Users", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -551,6 +337,7 @@ }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -582,6 +369,7 @@ }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -614,6 +402,7 @@ }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -647,6 +436,7 @@ }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -679,6 +469,7 @@ }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -711,6 +502,7 @@ }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -744,6 +536,7 @@ }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 1, @@ -777,6 +570,7 @@ }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -817,8 +611,8 @@ "issingle": 1, "istable": 0, "max_attachments": 0, - "modified": "2018-03-26 00:55:17.929140", - "modified_by": "test1@example.com", + "modified": "2018-07-24 18:53:09.442523", + "modified_by": "cave@aperture.com", "module": "Hub Node", "name": "Hub Settings", "name_case": "", @@ -826,7 +620,6 @@ "permissions": [ { "amend": 0, - "apply_user_permissions": 0, "cancel": 0, "create": 1, "delete": 0, diff --git a/erpnext/hub_node/doctype/hub_settings/hub_settings.py b/erpnext/hub_node/doctype/hub_settings/hub_settings.py index 58b946e1af..551423c8a2 100644 --- a/erpnext/hub_node/doctype/hub_settings/hub_settings.py +++ b/erpnext/hub_node/doctype/hub_settings/hub_settings.py @@ -10,9 +10,10 @@ from frappe import _ from erpnext.utilities.product import get_price, get_qty_in_stock from six import string_types -hub_url = "https://hubmarket.org" +# hub_url = "https://hubmarket.org" # hub_url = "http://159.89.175.122" # hub_url = "http://erpnext.hub:8001" +hub_url = "http://hub.market:8000" class OAuth2Session(): def __init__(self, headers): @@ -80,20 +81,20 @@ class HubSettings(Document): def register(self): """ Create a User on hub.erpnext.org and return username/password """ data = { - 'email': frappe.session.user + 'profile': self.as_json() } post_url = hub_url + '/api/method/hub.hub.api.register' - response = requests.post(post_url, data=data) + response = requests.post(post_url, data=data, headers = {'accept': 'application/json'}) + response.raise_for_status() - message = response.json().get('message') + + if response.ok: + message = response.json().get('message') + else: + frappe.throw(json.loads(response.text)) - if message and message.get('password'): - self.user = frappe.session.user - self.create_hub_connector(message) - self.company = frappe.defaults.get_user_default('company') - self.enabled = 1 - self.save() + return message.get('password') if message else None def unregister(self): """ Disable the User on hub.erpnext.org""" @@ -143,3 +144,16 @@ def reset_hub_settings(last_sync_datetime = ""): def sync(): hub_settings = frappe.get_doc('Hub Settings') hub_settings.sync() + +@frappe.whitelist() +def register_seller(**kwargs): + settings = frappe.get_doc('Hub Settings') + settings.update(kwargs) + password = settings.register() + + print(password) + + # if password: + # self.create_hub_connector(message) + # self.registered = 1 + # self.save() diff --git a/erpnext/public/js/hub/hub_listing.js b/erpnext/public/js/hub/hub_listing.js index f61272d3f8..218960bb17 100644 --- a/erpnext/public/js/hub/hub_listing.js +++ b/erpnext/public/js/hub/hub_listing.js @@ -5,11 +5,16 @@ erpnext.hub.Marketplace = class Marketplace { this.$parent = $(parent); this.page = parent.page; - this.setup_header(); - this.make_sidebar(); - this.make_body(); - this.setup_events(); - this.refresh(); + frappe.db.get_doc('Hub Settings') + .then(doc => { + this.hub_settings = doc; + + this.setup_header(); + this.make_sidebar(); + this.make_body(); + this.setup_events(); + this.refresh(); + }); } setup_header() { @@ -35,7 +40,7 @@ erpnext.hub.Marketplace = class Marketplace {
    • ${__('Favorites')}
    • -
    • +
    • ${__('Become a seller')}
    @@ -110,6 +115,11 @@ erpnext.hub.Marketplace = class Marketplace { this.subpages.item = new erpnext.hub.Item(this.$body); } + if (route[1] === 'register' && !this.subpages.register) { + this.subpages.register = new erpnext.hub.Register(this.$body); + } + + if (!Object.keys(this.subpages).includes(route[1])) { frappe.show_not_found(); return; @@ -189,7 +199,7 @@ erpnext.hub.Home = class Home extends SubPage { render(items) { const html = get_item_card_container_html(items, __('Recently Published')); - this.$wrapper.html(html) + this.$wrapper.append(html) } } @@ -412,7 +422,6 @@ erpnext.hub.Item = class Item extends SubPage { -
    ${edit_html}
    @@ -449,6 +458,176 @@ erpnext.hub.Item = class Item extends SubPage {
    `; } } +erpnext.hub.Register = class Register extends SubPage { + make_wrapper() { + super.make_wrapper(); + this.$register_container = $(`
    `) + .appendTo(this.$wrapper); + this.$form_container = $('
    ') + .appendTo(this.$wrapper); + + // const title = __('Become a Seller'); + // this.$register_container.append($(`
    ${title}
    `)); + } + + refresh() { + // company, company_email, logo, description + this.render(); + } + + // make_input() { + // // + // } + + 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(); + + 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', () => { + const form_values = this.field_group.get_values(); + frappe.call('erpnext.hub_node.doctype.hub_settings.hub_settings.register_seller', form_values) + .then(() => { + // Reload page and things ... but for now + frappe.msgprint('Registered successfully.'); + }); + }); + } + + make_form() { + const form_html = `
    +
    + Become a Seller +
    + +
    +
    +
    +
    + +
    +
    +
    + +
    +
    + +
    + +
    + +
    + +
    + +
    + +
    + +
    + +
    + +
    +
    +
    +
    `; + + + + this.$form_container.append(form_html); + + this.$form_container.on('submit', (e) => { + e.preventDefault(); + + const formValues = this.$form_container.find('form').serializeArray(); + + console.log(formValues); + }) + } +} + +erpnext.hub.PublishItems = class PublishItems extends SubPage { + refresh() { + this.get_favourites() + .then(r => { + this.render(r.message); + }); + } + + render(items) { + const html = get_item_card_container_html(items, __(this.category)); + this.$wrapper.html(html); + } + + get_valid_items() { + return frappe.call('erpnext.hub_node.get_valid_items'); + } +} function get_item_card_container_html(items, title) { const html = (items || []).map(item => get_item_card_html(item)).join(''); @@ -1335,4 +1514,4 @@ erpnext.hub.CompanyListing = class CompanyListing extends erpnext.hub.HubListing return item_html; } -}; \ No newline at end of file +}; diff --git a/erpnext/public/less/hub.less b/erpnext/public/less/hub.less index 48d52ca5aa..35e1f8de8f 100644 --- a/erpnext/public/less/hub.less +++ b/erpnext/public/less/hub.less @@ -17,6 +17,10 @@ body[data-route^="marketplace/"] { } } + input, textarea { + font-size: @text-medium; + } + .hub-card { border: 1px solid @border-color; margin-bottom: 25px; @@ -58,7 +62,6 @@ body[data-route^="marketplace/"] { margin-bottom: 20px; input { - font-size: @text-medium; height: 32px; } } @@ -101,6 +104,28 @@ body[data-route^="marketplace/"] { border-radius: 4px; border: 1px solid @border-color; } + + .register-title { + font-size: @text-regular; + } + + .register-form { + border: 1px solid @border-color; + border-radius: 4px; + padding: 15px 25px; + } + + .form-container { + .frappe-control { + max-width: 100% !important; + } + } + + .form-message { + padding-top: 0; + padding-bottom: 0; + border-bottom: none; + } } body[data-route^="Hub/"] { From cf2d2a5c754e92438572aa43fc6f4dede0dd0c66 Mon Sep 17 00:00:00 2001 From: Prateeksha Singh Date: Wed, 25 Jul 2018 20:02:12 +0530 Subject: [PATCH 005/238] [hub][init] add publish items page --- erpnext/hub_node/__init__.py | 14 ++- erpnext/public/js/hub/hub_listing.js | 180 +++++++++++++-------------- erpnext/public/less/hub.less | 40 +++++- 3 files changed, 133 insertions(+), 101 deletions(-) diff --git a/erpnext/hub_node/__init__.py b/erpnext/hub_node/__init__.py index a94f6b1615..138ff1e40e 100644 --- a/erpnext/hub_node/__init__.py +++ b/erpnext/hub_node/__init__.py @@ -31,12 +31,18 @@ def get_list(doctype, start=0, limit=20, fields=["*"], filters="{}", order_by=No return listing - - +#### LOCAL ITEMS @frappe.whitelist() def get_valid_items(): - items = frappe.get_list('Item') - return items + items = frappe.get_list('Item', fields=["*"]) + valid_items = filter(lambda x: x.image and x.description, items) + + def attach_source_type(item): + item.source_type = "local" + return item + + valid_items = map(lambda x: attach_source_type(x), valid_items) + return valid_items @frappe.whitelist() def get_item_favourites(start=0, limit=20, fields=["*"], order_by=None): diff --git a/erpnext/public/js/hub/hub_listing.js b/erpnext/public/js/hub/hub_listing.js index 218960bb17..856d538dd2 100644 --- a/erpnext/public/js/hub/hub_listing.js +++ b/erpnext/public/js/hub/hub_listing.js @@ -26,6 +26,8 @@ erpnext.hub.Marketplace = class Marketplace { const $target = $(e.currentTarget); const route = $target.data().route; frappe.set_route(route); + + e.stopPropagation(); }); } @@ -43,6 +45,12 @@ erpnext.hub.Marketplace = class Marketplace {
  • ${__('Become a seller')}
  • +
  • + ${__('Your Profile')} +
  • +
  • + ${__('Publish Products')} +
  • `); @@ -60,6 +68,9 @@ erpnext.hub.Marketplace = class Marketplace { `
  • ${__('All')}
  • `, + `
  • + ${__('Your Products')} +
  • `, ...categories.map(category => `
  • ${__(category)} @@ -119,6 +130,10 @@ erpnext.hub.Marketplace = class Marketplace { this.subpages.register = new erpnext.hub.Register(this.$body); } + if (route[1] === 'publish' && !this.subpages.publish) { + this.subpages.publish = new erpnext.hub.Publish(this.$body); + } + if (!Object.keys(this.subpages).includes(route[1])) { frappe.show_not_found(); @@ -217,7 +232,7 @@ erpnext.hub.Favourites = class Favourites extends SubPage { render(items) { const html = get_item_card_container_html(items, __('Favourites')); - this.$wrapper.html(html) + this.$wrapper.append(html) } } @@ -241,7 +256,7 @@ erpnext.hub.Category = class Category extends SubPage { render(items) { const html = get_item_card_container_html(items, __(this.category)); - this.$wrapper.html(html) + this.$wrapper.append(html) } } @@ -551,77 +566,42 @@ erpnext.hub.Register = class Register extends SubPage { }); }); } - - make_form() { - const form_html = `
    -
    - Become a Seller -
    - -
    -
    -
    -
    - -
    -
    -
    - -
    -
    - -
    - -
    - -
    - -
    - -
    - -
    - -
    - -
    - -
    -
    -
    -
    `; - - - - this.$form_container.append(form_html); - - this.$form_container.on('submit', (e) => { - e.preventDefault(); - - const formValues = this.$form_container.find('form').serializeArray(); - - console.log(formValues); - }) - } } -erpnext.hub.PublishItems = class PublishItems extends SubPage { +erpnext.hub.Publish = class Publish extends SubPage { + constructor(parent) { + super(parent); + // this.selected_items = []; + } + refresh() { - this.get_favourites() + this.get_valid_items() .then(r => { this.render(r.message); }); } render(items) { - const html = get_item_card_container_html(items, __(this.category)); - this.$wrapper.html(html); + const html = get_item_card_container_html( + items, + + __('Select Products to Publish'), + + __(`Only products with an image and description can be published. + Please update them if an item in your inventory does not appear.`), + + `
    + +
    ` + ); + + const container = $(html); + container.addClass('static').on('click', '.hub-card', (e) => { + const $target = $(e.currentTarget); + $target.toggleClass('active'); + }); + + this.$wrapper.append(container); } get_valid_items() { @@ -629,17 +609,23 @@ erpnext.hub.PublishItems = class PublishItems extends SubPage { } } -function get_item_card_container_html(items, title) { - const html = (items || []).map(item => get_item_card_html(item)).join(''); +function get_item_card_container_html(items, title, subtitle='', search_html='') { + const items_html = (items || []).map(item => get_item_card_html(item)).join(''); - return ` -
    -
    - ${title} -
    - ${html} + const html = `
    +
    + ${title} + +

    ${subtitle}

    + + ${search_html}
    - `; + + ${items_html} + +
    `; + + return html; } function get_item_card_html(item) { @@ -648,8 +634,8 @@ function get_item_card_html(item) { const img_url = item.image; const company_name = item.company_name; - const route = `marketplace/item/${item.hub_item_code}`; + // Subtitle let subtitle = [comment_when(item.creation)]; const rating = get_rating(item); if (rating > 0) { @@ -660,16 +646,36 @@ function get_item_card_html(item) { let dot_spacer = ''; subtitle = subtitle.join(dot_spacer); + // Decide item link + const isLocal = item.source_type === "local"; + const route = !isLocal + ? `marketplace/item/${item.hub_item_code}` + : `Form/Item/${item.item_name}`; + + const card_route = isLocal ? '' : `data-route='${route}'`; + + const show_local_item_button = isLocal + ? `
    + +
    ` + : ''; + const item_html = `
    -
    +
    -
    ${title}
    -
    ${subtitle}
    +
    +
    ${title}
    +
    ${subtitle}
    +
    +
    -
    +
    + ${show_local_item_button}
    @@ -904,23 +910,7 @@ erpnext.hub.HubListing = class HubListing extends frappe.views.BaseList { } renderHeader() { - return `
    -
    -
    - Riadco Group - Products by Blah blah -
    -
    -
    -
    - - -
    -
    -
    - ${''} -
    -
    `; + return ``; } get_image_html(encoded_name, src, alt_text) { diff --git a/erpnext/public/less/hub.less b/erpnext/public/less/hub.less index 35e1f8de8f..1243743032 100644 --- a/erpnext/public/less/hub.less +++ b/erpnext/public/less/hub.less @@ -21,6 +21,16 @@ body[data-route^="marketplace/"] { font-size: @text-medium; } + .btn-primary { + background-color: #89da28; + border-color: #61ca23; + } + + .btn-primary:hover { + background-color: #61ca23; + border-color: #59b81c; + } + .hub-card { border: 1px solid @border-color; margin-bottom: 25px; @@ -28,15 +38,33 @@ body[data-route^="marketplace/"] { overflow: hidden; cursor: pointer; - &:hover .hub-card-overlay { + &:hover .overlay { display: block; } + + &.active { + .hub-card-header { + background-color: #f4ffe5; + } + + .octicon-check { + display: inline; + } + } } .hub-card-header { padding: 12px 15px; height: 60px; border-bottom: 1px solid @border-color; + + display: flex; + justify-content: space-between; + + .octicon-check { + display: none; + font-size: 20px; + } } .hub-card-body { @@ -44,15 +72,23 @@ body[data-route^="marketplace/"] { height: 200px; } - .hub-card-overlay { + .overlay { display: none; position: absolute; + } + + .hub-card-overlay { top: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.1); } + .button-overlay { + top: 155px; + left: 15px; + } + .hub-card-image { min-width: 100%; width: 100%; From 9685d7c37a984a9d3eeb10e9484ddd7716a8c07e Mon Sep 17 00:00:00 2001 From: Prateeksha Singh Date: Thu, 26 Jul 2018 01:12:00 +0530 Subject: [PATCH 006/238] [hub] registered vs read-only views --- erpnext/public/js/hub/hub_listing.js | 47 ++++++++++++++++------------ 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/erpnext/public/js/hub/hub_listing.js b/erpnext/public/js/hub/hub_listing.js index 856d538dd2..9061ae62d3 100644 --- a/erpnext/public/js/hub/hub_listing.js +++ b/erpnext/public/js/hub/hub_listing.js @@ -8,7 +8,8 @@ erpnext.hub.Marketplace = class Marketplace { frappe.db.get_doc('Hub Settings') .then(doc => { this.hub_settings = doc; - + this.registered = doc.registered; + this.setup_header(); this.make_sidebar(); this.make_body(); @@ -26,7 +27,7 @@ erpnext.hub.Marketplace = class Marketplace { const $target = $(e.currentTarget); const route = $target.data().route; frappe.set_route(route); - + e.stopPropagation(); }); } @@ -34,6 +35,18 @@ erpnext.hub.Marketplace = class Marketplace { make_sidebar() { this.$sidebar = this.$parent.find('.layout-side-section').addClass('hidden-xs'); + const user_specific_items_html = this.registered + ? `
  • + ${__('Your Profile')} +
  • +
  • + ${__('Publish Products')} +
  • ` + + : `
  • + ${__('Become a seller')} +
  • `; + this.$sidebar.append(`
    • @@ -42,15 +55,7 @@ erpnext.hub.Marketplace = class Marketplace {
    • ${__('Favorites')}
    • -
    • - ${__('Become a seller')} -
    • -
    • - ${__('Your Profile')} -
    • -
    • - ${__('Publish Products')} -
    • + ${user_specific_items_html}
    `); @@ -68,9 +73,11 @@ erpnext.hub.Marketplace = class Marketplace { `
  • ${__('All')}
  • `, - `
  • - ${__('Your Products')} -
  • `, + ...(this.registered + ? [`
  • + ${__('Your Products')} +
  • `] + : []), ...categories.map(category => `
  • ${__(category)} @@ -480,7 +487,7 @@ erpnext.hub.Register = class Register extends SubPage { .appendTo(this.$wrapper); this.$form_container = $('
    ') .appendTo(this.$wrapper); - + // const title = __('Become a Seller'); // this.$register_container.append($(`
    ${title}
    `)); } @@ -493,7 +500,7 @@ erpnext.hub.Register = class Register extends SubPage { // make_input() { // // // } - + render() { this.make_field_group(); } @@ -583,11 +590,11 @@ erpnext.hub.Publish = class Publish extends SubPage { render(items) { const html = get_item_card_container_html( - items, + items, __('Select Products to Publish'), - __(`Only products with an image and description can be published. + __(`Only products with an image and description can be published. Please update them if an item in your inventory does not appear.`), `
    @@ -648,13 +655,13 @@ function get_item_card_html(item) { // Decide item link const isLocal = item.source_type === "local"; - const route = !isLocal + const route = !isLocal ? `marketplace/item/${item.hub_item_code}` : `Form/Item/${item.item_name}`; const card_route = isLocal ? '' : `data-route='${route}'`; - const show_local_item_button = isLocal + const show_local_item_button = isLocal ? `
    `; + + const select_all_button = ``; + const deselect_all_button = ``; + + const search_html = `
    + +
    `; + + const subpage_header = $(` +
    +
    + ${title_html} + ${subtitle_html} +
    + ${publish_button_html} +
    + + ${search_html} + + ${select_all_button} + ${deselect_all_button} + `); + + this.$wrapper.append(subpage_header); + + this.setup_events(); + } + + setup_events() { + this.$wrapper.find('.select-all').on('click', () => { + this.$wrapper.find('.hub-card').addClass('active'); + }); + + this.$wrapper.find('.deselect-all').on('click', () => { + this.$wrapper.find('.hub-card').removeClass('active'); + }); } refresh() { @@ -589,26 +625,13 @@ erpnext.hub.Publish = class Publish extends SubPage { } render(items) { - const html = get_item_card_container_html( - items, - - __('Select Products to Publish'), - - __(`Only products with an image and description can be published. - Please update them if an item in your inventory does not appear.`), - - `
    - -
    ` - ); - - const container = $(html); - container.addClass('static').on('click', '.hub-card', (e) => { + const items_container = $(get_item_card_container_html(items)); + items_container.addClass('static').on('click', '.hub-card', (e) => { const $target = $(e.currentTarget); $target.toggleClass('active'); }); - this.$wrapper.append(container); + this.$wrapper.append(items_container); } get_valid_items() { @@ -616,20 +639,14 @@ erpnext.hub.Publish = class Publish extends SubPage { } } -function get_item_card_container_html(items, title, subtitle='', search_html='') { +function get_item_card_container_html(items, title='') { const items_html = (items || []).map(item => get_item_card_html(item)).join(''); const html = `
    -
    +
    ${title} - -

    ${subtitle}

    - - ${search_html}
    - ${items_html} -
    `; return html; diff --git a/erpnext/public/less/hub.less b/erpnext/public/less/hub.less index 1243743032..8c7392ad49 100644 --- a/erpnext/public/less/hub.less +++ b/erpnext/public/less/hub.less @@ -31,6 +31,11 @@ body[data-route^="marketplace/"] { border-color: #59b81c; } + .subpage-title.flex { + align-items: flex-start; + justify-content: space-between; + } + .hub-card { border: 1px solid @border-color; margin-bottom: 25px; From 1d1696080e8366a8f5f9e8b8ef34407ec253a65f Mon Sep 17 00:00:00 2001 From: Prateeksha Singh Date: Thu, 26 Jul 2018 08:36:33 +0530 Subject: [PATCH 008/238] [hub][publish] add search, fix multiple append --- erpnext/hub_node/__init__.py | 9 +++++++-- erpnext/public/js/hub/hub_listing.js | 29 ++++++++++++++++++++++++++-- 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/erpnext/hub_node/__init__.py b/erpnext/hub_node/__init__.py index 138ff1e40e..993737e147 100644 --- a/erpnext/hub_node/__init__.py +++ b/erpnext/hub_node/__init__.py @@ -33,8 +33,13 @@ def get_list(doctype, start=0, limit=20, fields=["*"], filters="{}", order_by=No #### LOCAL ITEMS @frappe.whitelist() -def get_valid_items(): - items = frappe.get_list('Item', fields=["*"]) +def get_valid_items(search_value=''): + items = frappe.get_list('Item', fields=["*"], filters={ + 'item_name': ['like', '%' + search_value + '%'] + }) + + print([d.item_name for d in items]) + valid_items = filter(lambda x: x.image and x.description, items) def attach_source_type(item): diff --git a/erpnext/public/js/hub/hub_listing.js b/erpnext/public/js/hub/hub_listing.js index 3107c674be..33df23e0c5 100644 --- a/erpnext/public/js/hub/hub_listing.js +++ b/erpnext/public/js/hub/hub_listing.js @@ -186,6 +186,7 @@ erpnext.hub.Home = class Home extends SubPage { } get_items_and_render() { + this.$wrapper.find('.hub-card-container').empty(); this.get_items() .then(r => { erpnext.hub.hub_item_cache = r.message; @@ -238,6 +239,7 @@ erpnext.hub.Favourites = class Favourites extends SubPage { } render(items) { + this.$wrapper.find('.hub-card-container').empty(); const html = get_item_card_container_html(items, __('Favourites')); this.$wrapper.append(html) } @@ -253,6 +255,7 @@ erpnext.hub.Category = class Category extends SubPage { } get_items_for_category(category) { + this.$wrapper.find('.hub-card-container').empty(); return frappe.call('erpnext.hub_node.get_list', { doctype: 'Hub Item', filters: { @@ -490,6 +493,8 @@ erpnext.hub.Register = class Register extends SubPage { } refresh() { + this.$register_container.empty(); + this.$form_container.empty(); this.render(); } @@ -615,15 +620,30 @@ erpnext.hub.Publish = class Publish extends SubPage { this.$wrapper.find('.deselect-all').on('click', () => { this.$wrapper.find('.hub-card').removeClass('active'); }); + + const $search_input = this.$wrapper.find('.hub-search-container input'); + this.search_value = ''; + + $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)); } - refresh() { + get_items_and_render() { + this.$wrapper.find('.hub-card-container').empty(); this.get_valid_items() .then(r => { this.render(r.message); }); } + refresh() { + this.get_items_and_render(); + } + render(items) { const items_container = $(get_item_card_container_html(items)); items_container.addClass('static').on('click', '.hub-card', (e) => { @@ -635,7 +655,12 @@ erpnext.hub.Publish = class Publish extends SubPage { } get_valid_items() { - return frappe.call('erpnext.hub_node.get_valid_items'); + return frappe.call( + 'erpnext.hub_node.get_valid_items', + { + search_value: this.search_value + } + ); } } From f26177bdbc6491902478dc7793d660b2a07fecbb Mon Sep 17 00:00:00 2001 From: Prateeksha Singh Date: Thu, 26 Jul 2018 10:38:11 +0530 Subject: [PATCH 009/238] [hub] setup publish items flow --- erpnext/hub_node/__init__.py | 15 ++++++++-- erpnext/public/js/hub/hub_listing.js | 45 ++++++++++++++++++++++++---- erpnext/stock/doctype/item/item.py | 4 +-- 3 files changed, 55 insertions(+), 9 deletions(-) diff --git a/erpnext/hub_node/__init__.py b/erpnext/hub_node/__init__.py index 993737e147..c8e40eb8e8 100644 --- a/erpnext/hub_node/__init__.py +++ b/erpnext/hub_node/__init__.py @@ -38,8 +38,6 @@ def get_valid_items(search_value=''): 'item_name': ['like', '%' + search_value + '%'] }) - print([d.item_name for d in items]) - valid_items = filter(lambda x: x.image and x.description, items) def attach_source_type(item): @@ -49,6 +47,19 @@ def get_valid_items(search_value=''): valid_items = map(lambda x: attach_source_type(x), valid_items) return valid_items +@frappe.whitelist() +def publish_selected_items(items_to_publish, items_to_unpublish): + for item_code in json.loads(items_to_publish): + frappe.db.set_value('Item', item_code, 'publish_in_hub', 1) + + for item_code in json.loads(items_to_unpublish): + frappe.db.set_value('Item', item_code, 'publish_in_hub', 0) + + hub_settings = frappe.get_doc('Hub Settings') + hub_settings.sync() + + return + @frappe.whitelist() def get_item_favourites(start=0, limit=20, fields=["*"], order_by=None): doctype = 'Hub Item' diff --git a/erpnext/public/js/hub/hub_listing.js b/erpnext/public/js/hub/hub_listing.js index 33df23e0c5..eb55806784 100644 --- a/erpnext/public/js/hub/hub_listing.js +++ b/erpnext/public/js/hub/hub_listing.js @@ -576,11 +576,13 @@ erpnext.hub.Publish = class Publish extends SubPage { make_wrapper() { super.make_wrapper(); const title_html = `${__('Select Products to Publish')}`; - const subtitle_html = `

    - ${__(`Only products with an image and description can be published. + const info = `

    ${__("Status decided by the 'Publish in Hub' field in Item.")}

    `; + 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 = ``; @@ -621,6 +623,13 @@ erpnext.hub.Publish = class Publish extends SubPage { this.$wrapper.find('.hub-card').removeClass('active'); }); + this.$wrapper.find('.publish-items').on('click', () => { + this.publish_selected_items() + .then(r => { + frappe.msgprint('check'); + }); + }); + const $search_input = this.$wrapper.find('.hub-search-container input'); this.search_value = ''; @@ -662,6 +671,28 @@ erpnext.hub.Publish = class Publish extends SubPage { } ); } + + publish_selected_items() { + const items_to_publish = []; + const items_to_unpublish = []; + this.$wrapper.find('.hub-card').map(function () { + const active = $(this).hasClass('active'); + + if(active) { + items_to_publish.push($(this).attr("data-id")); + } else { + items_to_unpublish.push($(this).attr("data-id")); + } + }); + + return frappe.call( + 'erpnext.hub_node.publish_selected_items', + { + items_to_publish: items_to_publish, + items_to_unpublish: items_to_unpublish + } + ); + } } function get_item_card_container_html(items, title='') { @@ -680,10 +711,14 @@ function get_item_card_container_html(items, title='') { 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 active = item.publish_in_hub; + + const id = item.hub_item_code || item.item_code; + // Subtitle let subtitle = [comment_when(item.creation)]; const rating = get_rating(item); @@ -713,7 +748,7 @@ function get_item_card_html(item) { const item_html = `
    -
    +
    ${title}
    diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index f9d0e7121c..1829b25e8e 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -75,8 +75,8 @@ class Item(WebsiteGenerator): if not self.description: self.description = self.item_name - if self.is_sales_item and not self.get('is_item_from_hub'): - self.publish_in_hub = 1 + # if self.is_sales_item and not self.get('is_item_from_hub'): + # self.publish_in_hub = 1 def after_insert(self): '''set opening stock and item price''' From d1224ca662df31e8f694ddc7b365dfc0fe0d7149 Mon Sep 17 00:00:00 2001 From: Prateeksha Singh Date: Thu, 26 Jul 2018 13:39:06 +0530 Subject: [PATCH 010/238] [hub][init] add profile page --- erpnext/hub_node/__init__.py | 14 +++--- erpnext/public/js/hub/hub_listing.js | 65 +++++++++++++++++++++++----- 2 files changed, 63 insertions(+), 16 deletions(-) diff --git a/erpnext/hub_node/__init__.py b/erpnext/hub_node/__init__.py index c8e40eb8e8..5c86bb927c 100644 --- a/erpnext/hub_node/__init__.py +++ b/erpnext/hub_node/__init__.py @@ -34,9 +34,14 @@ def get_list(doctype, start=0, limit=20, fields=["*"], filters="{}", order_by=No #### LOCAL ITEMS @frappe.whitelist() def get_valid_items(search_value=''): - items = frappe.get_list('Item', fields=["*"], filters={ - 'item_name': ['like', '%' + search_value + '%'] - }) + items = frappe.get_list( + 'Item', fields=["*"], + filters={ + 'item_name': ['like', '%' + search_value + '%'], + 'publish_in_hub': 0 + }, + order_by="modified desc" + ) valid_items = filter(lambda x: x.image and x.description, items) @@ -52,9 +57,6 @@ def publish_selected_items(items_to_publish, items_to_unpublish): for item_code in json.loads(items_to_publish): frappe.db.set_value('Item', item_code, 'publish_in_hub', 1) - for item_code in json.loads(items_to_unpublish): - frappe.db.set_value('Item', item_code, 'publish_in_hub', 0) - hub_settings = frappe.get_doc('Hub Settings') hub_settings.sync() diff --git a/erpnext/public/js/hub/hub_listing.js b/erpnext/public/js/hub/hub_listing.js index eb55806784..6d0f3e96ac 100644 --- a/erpnext/public/js/hub/hub_listing.js +++ b/erpnext/public/js/hub/hub_listing.js @@ -137,11 +137,14 @@ erpnext.hub.Marketplace = class Marketplace { this.subpages.register = new erpnext.hub.Register(this.$body); } + if (route[1] === 'profile' && !this.subpages.profile) { + this.subpages.profile = new erpnext.hub.Profile(this.$body, this.hub_settings); + } + if (route[1] === 'publish' && !this.subpages.publish) { this.subpages.publish = new erpnext.hub.Publish(this.$body); } - if (!Object.keys(this.subpages).includes(route[1])) { frappe.show_not_found(); return; @@ -572,6 +575,51 @@ erpnext.hub.Register = class Register extends SubPage { } } +erpnext.hub.Profile = class Profile extends SubPage { + constructor(parent, profile_data) { + super(parent); + this.profile_data = profile_data; + } + + make_wrapper() { + super.make_wrapper(); + const profile_html = `
    +
    +
    + +
    +
    +
    +
    +
    + +
    +
    +
    +

    ${'title'}

    +
    +

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

    +

    ${'rating_html'}${'rating_count'}

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

    ${'description'}

    + ` : __('No description') + } +
    +
    +
    +
    `; + + this.$wrapper.html(profile_html); + } + + refresh() {} + + render() {} +} erpnext.hub.Publish = class Publish extends SubPage { make_wrapper() { super.make_wrapper(); @@ -604,9 +652,6 @@ erpnext.hub.Publish = class Publish extends SubPage {
    ${search_html} - - ${select_all_button} - ${deselect_all_button} `); this.$wrapper.append(subpage_header); @@ -615,13 +660,13 @@ erpnext.hub.Publish = class Publish extends SubPage { } setup_events() { - this.$wrapper.find('.select-all').on('click', () => { - this.$wrapper.find('.hub-card').addClass('active'); - }); + // this.$wrapper.find('.select-all').on('click', () => { + // this.$wrapper.find('.hub-card').addClass('active'); + // }); - this.$wrapper.find('.deselect-all').on('click', () => { - this.$wrapper.find('.hub-card').removeClass('active'); - }); + // this.$wrapper.find('.deselect-all').on('click', () => { + // this.$wrapper.find('.hub-card').removeClass('active'); + // }); this.$wrapper.find('.publish-items').on('click', () => { this.publish_selected_items() From b7504252883d6fb509139436bea0af311e7653ce Mon Sep 17 00:00:00 2001 From: Prateeksha Singh Date: Thu, 26 Jul 2018 16:25:22 +0530 Subject: [PATCH 011/238] [hub] sync in progress --- erpnext/hub_node/__init__.py | 17 ++- .../doctype/hub_settings/hub_settings.js | 131 +----------------- .../doctype/hub_settings/hub_settings.json | 34 ++++- erpnext/public/js/hub/hub_listing.js | 65 +++++---- 4 files changed, 86 insertions(+), 161 deletions(-) diff --git a/erpnext/hub_node/__init__.py b/erpnext/hub_node/__init__.py index 5c86bb927c..f9132397c0 100644 --- a/erpnext/hub_node/__init__.py +++ b/erpnext/hub_node/__init__.py @@ -2,7 +2,7 @@ # For license information, please see license.txt from __future__ import unicode_literals -import frappe, requests, json +import frappe, requests, json, time from frappe.utils import now, nowdate, cint from frappe.utils.nestedset import get_root_of from frappe.contacts.doctype.contact.contact import get_default_contact @@ -35,7 +35,8 @@ def get_list(doctype, start=0, limit=20, fields=["*"], filters="{}", order_by=No @frappe.whitelist() def get_valid_items(search_value=''): items = frappe.get_list( - 'Item', fields=["*"], + 'Item', + fields=["*"], filters={ 'item_name': ['like', '%' + search_value + '%'], 'publish_in_hub': 0 @@ -53,12 +54,14 @@ def get_valid_items(search_value=''): return valid_items @frappe.whitelist() -def publish_selected_items(items_to_publish, items_to_unpublish): - for item_code in json.loads(items_to_publish): - frappe.db.set_value('Item', item_code, 'publish_in_hub', 1) +def publish_selected_items(items_to_publish): + # for item_code in json.loads(items_to_publish): + # frappe.db.set_value('Item', item_code, 'publish_in_hub', 1) - hub_settings = frappe.get_doc('Hub Settings') - hub_settings.sync() + time.sleep(3) + + # hub_settings = frappe.get_doc('Hub Settings') + # hub_settings.sync() return diff --git a/erpnext/hub_node/doctype/hub_settings/hub_settings.js b/erpnext/hub_node/doctype/hub_settings/hub_settings.js index 29d870b371..4bd3333276 100644 --- a/erpnext/hub_node/doctype/hub_settings/hub_settings.js +++ b/erpnext/hub_node/doctype/hub_settings/hub_settings.js @@ -1,53 +1,23 @@ frappe.ui.form.on("Hub Settings", { refresh: function(frm) { + frm.disable_save(); frm.add_custom_button(__('Logs'), () => frappe.set_route('List', 'Data Migration Run', { data_migration_plan: 'Hub Sync' })); - frm.trigger("enabled"); - if (frm.doc.enabled) { frm.add_custom_button(__('Sync'), () => frm.call('sync')); } }, - onload: function(frm) { - let token = frappe.urllib.get_arg("access_token"); - if(token) { - let email = frm.get_field("user"); - console.log('token', frappe.urllib.get_arg("access_token")); - - get_user_details(frm, token, email); - let row = frappe.model.add_child(frm.doc, "Hub Users", "users"); - row.user = frappe.session.user; - } - - if(!frm.doc.country) { - frm.set_value("country", frappe.defaults.get_default("Country")); - } - if(!frm.doc.company) { - frm.set_value("company", frappe.defaults.get_default("Company")); - } - if(!frm.doc.user) { - frm.set_value("user", frappe.session.user); - } - }, + onload: function(frm) { }, onload_post_render: function(frm) { if(frm.get_field("unregister_from_hub").$input) frm.get_field("unregister_from_hub").$input.addClass("btn-danger"); }, on_update: function(frm) { }, - enabled: function(frm) { - if(!frm.doc.enabled) { - frm.trigger("set_enable_hub_primary_button"); - } else { - frm.page.set_primary_action(__("Save Settings"), () => { - frm.save(); - }); - } - }, hub_user_email: function(frm) { if(frm.doc.hub_user_email){ @@ -55,39 +25,6 @@ frappe.ui.form.on("Hub Settings", { } }, - set_enable_hub_primary_button: (frm) => { - frm.page.set_primary_action(__("Enable Hub"), () => { - if(frappe.session.user === "Administrator") { - frappe.msgprint(__("Please login as another user.")) - } else { - // frappe.verify_password(() => { - - // } ); - - frm.trigger("call_pre_reg"); - // frm.trigger("call_register"); - - } - }); - }, - - call_pre_reg: (frm) => { - this.frm.call({ - doc: this.frm.doc, - method: "pre_reg", - args: {}, - freeze: true, - callback: function(r) { - console.log(r.message); - authorize(frm, r.message.client_id, r.message.redirect_uri); - }, - onerror: function() { - frappe.msgprint(__("Wrong Password")); - frm.set_value("enabled", 0); - } - }); - }, - call_register: (frm) => { this.frm.call({ doc: this.frm.doc, @@ -111,67 +48,3 @@ frappe.ui.form.on("Hub Settings", { }); }, }); - -// let hub_url = 'https://hubmarket.org' -let hub_url = 'http://159.89.175.122' -// let hub_url = 'http://erpnext.hub:8000' - -function authorize(frm, client_id, redirect_uri) { - - // queryStringData is details of OAuth Client (Implicit Grant) on Custom App - var queryStringData = { - response_type : "token", - client_id : client_id, - redirect_uri : redirect_uri - } - - // Get current raw route and build url - const route = "/desk#" + frappe.get_raw_route_str(); - localStorage.removeItem("route"); // Clear previously set route if any - localStorage.setItem("route", route); - - // Go authorize! - let api_route = "/api/method/frappe.integrations.oauth2.authorize?"; - let url = hub_url + api_route + $.param(queryStringData); - window.location.replace(url, 'test'); -} - -function get_user_details(frm, token, email) { - console.log('user_details'); - var route = localStorage.getItem("route"); - if (token && route) { - // Clean up access token from route - frappe.set_route(frappe.get_route().join("/")) - - // query protected resource e.g. Hub Items with token - var call = { - "async": true, - "crossDomain": true, - "url": hub_url + "/api/resource/User", - "method": "GET", - "data": { - // "email": email, - "fields": '["name", "first_name", "language"]', - "limit_page_length": 1 - }, - "headers": { - "authorization": "Bearer " + token, - "content-type": "application/x-www-form-urlencoded" - } - } - $.ajax(call).done(function (response) { - // display openid profile - console.log('response', response); - - let data = response.data[0]; - frm.set_value("enabled", 1); - frm.set_value("hub_username", data.first_name); - frm.set_value("hub_user_status", "Starter"); - frm.set_value("language", data.language); - frm.save(); - - // clear route from localStorage - localStorage.removeItem("route"); - }); - } -} diff --git a/erpnext/hub_node/doctype/hub_settings/hub_settings.json b/erpnext/hub_node/doctype/hub_settings/hub_settings.json index c742c57b2b..61ef48ca54 100644 --- a/erpnext/hub_node/doctype/hub_settings/hub_settings.json +++ b/erpnext/hub_node/doctype/hub_settings/hub_settings.json @@ -44,6 +44,38 @@ "translatable": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "sync_in_progress", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Sync in Progress", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_in_quick_entry": 0, @@ -611,7 +643,7 @@ "issingle": 1, "istable": 0, "max_attachments": 0, - "modified": "2018-07-24 18:53:09.442523", + "modified": "2018-07-26 15:56:36.104768", "modified_by": "cave@aperture.com", "module": "Hub Node", "name": "Hub Settings", diff --git a/erpnext/public/js/hub/hub_listing.js b/erpnext/public/js/hub/hub_listing.js index 6d0f3e96ac..7a71c11ea4 100644 --- a/erpnext/public/js/hub/hub_listing.js +++ b/erpnext/public/js/hub/hub_listing.js @@ -138,11 +138,14 @@ erpnext.hub.Marketplace = class Marketplace { } if (route[1] === 'profile' && !this.subpages.profile) { - this.subpages.profile = new erpnext.hub.Profile(this.$body, this.hub_settings); + this.subpages.profile = new erpnext.hub.Profile(this.$body, {data: this.hub_settings}); } if (route[1] === 'publish' && !this.subpages.publish) { - this.subpages.publish = new erpnext.hub.Publish(this.$body); + this.subpages.publish = new erpnext.hub.Publish( + this.$body, + {sync_in_progress: this.hub_settings.sync_in_progress} + ); } if (!Object.keys(this.subpages).includes(route[1])) { @@ -157,9 +160,9 @@ erpnext.hub.Marketplace = class Marketplace { } class SubPage { - constructor(parent) { + constructor(parent, options) { this.$parent = $(parent); - this.make_wrapper(); + this.make_wrapper(options); } make_wrapper() { @@ -621,8 +624,14 @@ erpnext.hub.Profile = class Profile extends SubPage { render() {} } erpnext.hub.Publish = class Publish extends SubPage { - make_wrapper() { + make_wrapper(options) { super.make_wrapper(); + this.sync_in_progress = options.sync_in_progress; + + this.load_publish_page(); + } + + load_publish_page() { const title_html = `${__('Select Products to Publish')}`; const info = `

    ${__("Status decided by the 'Publish in Hub' field in Item.")}

    `; const subtitle_html = ` @@ -660,17 +669,10 @@ erpnext.hub.Publish = class Publish extends SubPage { } setup_events() { - // this.$wrapper.find('.select-all').on('click', () => { - // this.$wrapper.find('.hub-card').addClass('active'); - // }); - - // this.$wrapper.find('.deselect-all').on('click', () => { - // this.$wrapper.find('.hub-card').removeClass('active'); - // }); - this.$wrapper.find('.publish-items').on('click', () => { this.publish_selected_items() .then(r => { + this.load_publishing_state(); frappe.msgprint('check'); }); }); @@ -687,6 +689,11 @@ erpnext.hub.Publish = class Publish extends SubPage { } get_items_and_render() { + if(this.sync_in_progress) { + this.load_publishing_state(); + return; + } + this.$wrapper.find('.hub-card-container').empty(); this.get_valid_items() .then(r => { @@ -703,6 +710,18 @@ erpnext.hub.Publish = class Publish extends SubPage { items_container.addClass('static').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; + const more_than_one = total_items > 1; + this.$wrapper.find('.publish-items') + .html(__('Publish ' + total_items + ' item' + (more_than_one ? 's' : ''))); + + // if($target.hasClass('active')) { + // $target.addClass('active'); + // } else { + // $target.removeClass('active'); + // } }); this.$wrapper.append(items_container); @@ -717,24 +736,22 @@ erpnext.hub.Publish = class Publish extends SubPage { ); } + load_publishing_state() { + this.$wrapper.html(`
    + Publishing +
    `); + } + publish_selected_items() { const items_to_publish = []; - const items_to_unpublish = []; - this.$wrapper.find('.hub-card').map(function () { - const active = $(this).hasClass('active'); - - if(active) { - items_to_publish.push($(this).attr("data-id")); - } else { - items_to_unpublish.push($(this).attr("data-id")); - } + this.$wrapper.find('.hub-card.active').map(function () { + items_to_publish.push($(this).attr("data-id")); }); return frappe.call( 'erpnext.hub_node.publish_selected_items', { - items_to_publish: items_to_publish, - items_to_unpublish: items_to_unpublish + items_to_publish: items_to_publish } ); } From d450c0ac0819b74a265a93808d12dd304968827f Mon Sep 17 00:00:00 2001 From: Prateeksha Singh Date: Thu, 26 Jul 2018 17:24:33 +0530 Subject: [PATCH 012/238] [hub][publish] in_progress/empty state --- erpnext/hub_node/__init__.py | 12 ++++++----- .../doctype/hub_settings/hub_settings.py | 8 +++++--- erpnext/public/js/hub/hub_listing.js | 20 +++++++++---------- erpnext/public/less/hub.less | 5 +++++ 4 files changed, 27 insertions(+), 18 deletions(-) diff --git a/erpnext/hub_node/__init__.py b/erpnext/hub_node/__init__.py index f9132397c0..7d846d1b94 100644 --- a/erpnext/hub_node/__init__.py +++ b/erpnext/hub_node/__init__.py @@ -55,13 +55,15 @@ def get_valid_items(search_value=''): @frappe.whitelist() def publish_selected_items(items_to_publish): - # for item_code in json.loads(items_to_publish): - # frappe.db.set_value('Item', item_code, 'publish_in_hub', 1) + for item_code in json.loads(items_to_publish): + frappe.db.set_value('Item', item_code, 'publish_in_hub', 1) - time.sleep(3) + # frappe.db.set_value("Hub Settings", "Hub Settings", "sync_in_progress", 1) + # time.sleep(10) + # frappe.db.set_value("Hub Settings", "Hub Settings", "sync_in_progress", 0) - # hub_settings = frappe.get_doc('Hub Settings') - # hub_settings.sync() + hub_settings = frappe.get_doc('Hub Settings') + hub_settings.sync() return diff --git a/erpnext/hub_node/doctype/hub_settings/hub_settings.py b/erpnext/hub_node/doctype/hub_settings/hub_settings.py index 551423c8a2..52fdb8c522 100644 --- a/erpnext/hub_node/doctype/hub_settings/hub_settings.py +++ b/erpnext/hub_node/doctype/hub_settings/hub_settings.py @@ -46,10 +46,12 @@ class HubSettings(Document): doc = frappe.get_doc({ 'doctype': 'Data Migration Run', 'data_migration_plan': 'Hub Sync', - 'data_migration_connector': 'Hub Connector' + 'data_migration_connector': 'Hub Connector', }).insert() + self.sync_in_progress = 1 doc.run() + self.sync_in_progress = 0 def pre_reg(self): site_name = frappe.local.site + ':' + str(frappe.conf.webserver_port) @@ -86,9 +88,9 @@ class HubSettings(Document): post_url = hub_url + '/api/method/hub.hub.api.register' response = requests.post(post_url, data=data, headers = {'accept': 'application/json'}) - + response.raise_for_status() - + if response.ok: message = response.json().get('message') else: diff --git a/erpnext/public/js/hub/hub_listing.js b/erpnext/public/js/hub/hub_listing.js index 7a71c11ea4..bde1e8fe54 100644 --- a/erpnext/public/js/hub/hub_listing.js +++ b/erpnext/public/js/hub/hub_listing.js @@ -670,9 +670,9 @@ erpnext.hub.Publish = class Publish extends SubPage { setup_events() { this.$wrapper.find('.publish-items').on('click', () => { + this.load_publishing_state(); this.publish_selected_items() .then(r => { - this.load_publishing_state(); frappe.msgprint('check'); }); }); @@ -716,12 +716,6 @@ erpnext.hub.Publish = class Publish extends SubPage { const more_than_one = total_items > 1; this.$wrapper.find('.publish-items') .html(__('Publish ' + total_items + ' item' + (more_than_one ? 's' : ''))); - - // if($target.hasClass('active')) { - // $target.addClass('active'); - // } else { - // $target.removeClass('active'); - // } }); this.$wrapper.append(items_container); @@ -737,9 +731,9 @@ erpnext.hub.Publish = class Publish extends SubPage { } load_publishing_state() { - this.$wrapper.html(`
    - Publishing -
    `); + this.$wrapper.html(get_empty_state( + 'Publishing items ... You will be notified once published.' + )); } publish_selected_items() { @@ -757,6 +751,12 @@ erpnext.hub.Publish = class Publish extends SubPage { } } +function get_empty_state(message) { + return `
    +

    ${message}

    +
    ` +} + function get_item_card_container_html(items, title='') { const items_html = (items || []).map(item => get_item_card_html(item)).join(''); diff --git a/erpnext/public/less/hub.less b/erpnext/public/less/hub.less index 8c7392ad49..bd503fe1e3 100644 --- a/erpnext/public/less/hub.less +++ b/erpnext/public/less/hub.less @@ -156,6 +156,11 @@ body[data-route^="marketplace/"] { padding: 15px 25px; } + .empty-state { + justify-content: center; + padding-top: 159px; + } + .form-container { .frappe-control { max-width: 100% !important; From bcd7077342dd91f378f10f905ed0e25ffcd82229 Mon Sep 17 00:00:00 2001 From: Prateeksha Singh Date: Thu, 26 Jul 2018 18:20:58 +0530 Subject: [PATCH 013/238] [hub] remove sync_in_progress set --- erpnext/hub_node/doctype/hub_settings/hub_settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/hub_node/doctype/hub_settings/hub_settings.py b/erpnext/hub_node/doctype/hub_settings/hub_settings.py index 52fdb8c522..319c76325e 100644 --- a/erpnext/hub_node/doctype/hub_settings/hub_settings.py +++ b/erpnext/hub_node/doctype/hub_settings/hub_settings.py @@ -51,7 +51,7 @@ class HubSettings(Document): self.sync_in_progress = 1 doc.run() - self.sync_in_progress = 0 + # self.sync_in_progress = 0 def pre_reg(self): site_name = frappe.local.site + ':' + str(frappe.conf.webserver_port) From d885248309bb82f68da208a5cff8d0caf4a17b5a Mon Sep 17 00:00:00 2001 From: Prateeksha Singh Date: Thu, 26 Jul 2018 18:21:33 +0530 Subject: [PATCH 014/238] [hub] remove sync_in _progress set --- erpnext/hub_node/doctype/hub_settings/hub_settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/hub_node/doctype/hub_settings/hub_settings.py b/erpnext/hub_node/doctype/hub_settings/hub_settings.py index 319c76325e..813c881291 100644 --- a/erpnext/hub_node/doctype/hub_settings/hub_settings.py +++ b/erpnext/hub_node/doctype/hub_settings/hub_settings.py @@ -49,7 +49,7 @@ class HubSettings(Document): 'data_migration_connector': 'Hub Connector', }).insert() - self.sync_in_progress = 1 + # self.sync_in_progress = 1 doc.run() # self.sync_in_progress = 0 From 2ca5afa21499c95ead4749d47e399c4ffb5971e2 Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Thu, 26 Jul 2018 18:35:04 +0530 Subject: [PATCH 015/238] Move marketplace ui to marketplace.js --- erpnext/public/js/hub/hub_factory.js | 2 +- erpnext/public/js/hub/hub_listing.js | 789 --------------------------- 2 files changed, 1 insertion(+), 790 deletions(-) diff --git a/erpnext/public/js/hub/hub_factory.js b/erpnext/public/js/hub/hub_factory.js index d54787af36..c94edf4e4d 100644 --- a/erpnext/public/js/hub/hub_factory.js +++ b/erpnext/public/js/hub/hub_factory.js @@ -14,7 +14,7 @@ frappe.views.marketplaceFactory = class marketplaceFactory extends frappe.views. make(page_name) { const assets = [ - '/assets/erpnext/js/hub/hub_listing.js' + '/assets/erpnext/js/hub/marketplace.js' ]; frappe.require(assets, () => { diff --git a/erpnext/public/js/hub/hub_listing.js b/erpnext/public/js/hub/hub_listing.js index eb55806784..368c723e5b 100644 --- a/erpnext/public/js/hub/hub_listing.js +++ b/erpnext/public/js/hub/hub_listing.js @@ -1,792 +1,3 @@ -frappe.provide('erpnext.hub'); - -erpnext.hub.Marketplace = class Marketplace { - constructor({ parent }) { - this.$parent = $(parent); - this.page = parent.page; - - frappe.db.get_doc('Hub Settings') - .then(doc => { - this.hub_settings = doc; - this.registered = doc.registered; - - 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); - - e.stopPropagation(); - }); - } - - make_sidebar() { - this.$sidebar = this.$parent.find('.layout-side-section').addClass('hidden-xs'); - - const user_specific_items_html = this.registered - ? `
  • - ${__('Your Profile')} -
  • -
  • - ${__('Publish Products')} -
  • ` - - : `
  • - ${__('Become a seller')} -
  • `; - - this.$sidebar.append(` -
      -
    • - ${__('Browse')} -
    • -
    • - ${__('Favorites')} -
    • - ${user_specific_items_html} -
    - `); - - 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 = [ - `
  • - ${__('Category')} -
  • `, - `
  • - ${__('All')} -
  • `, - ...(this.registered - ? [`
  • - ${__('Your Products')} -
  • `] - : []), - ...categories.map(category => ` -
  • - ${__(category)} -
  • - `) - ]; - - this.$sidebar.append(` -
      - ${sidebar_items.join('')} -
    - `); - - this.update_sidebar(); - }); - } - - make_body() { - this.$body = this.$parent.find('.layout-main-section'); - } - - update_sidebar() { - const route = frappe.get_route_str(); - const $sidebar_item = this.$sidebar.find(`[data-route="${route}"]`); - - const $siblings = this.$sidebar.find('[data-route]'); - $siblings.removeClass('active').addClass('text-muted'); - - $sidebar_item.addClass('active').removeClass('text-muted'); - } - - refresh() { - const route = frappe.get_route(); - this.subpages = this.subpages || {}; - - for (let page in this.subpages) { - this.subpages[page].hide(); - } - - if (route[1] === 'home' && !this.subpages.home) { - this.subpages.home = new erpnext.hub.Home(this.$body); - } - - if (route[1] === 'favourites' && !this.subpages.favourites) { - this.subpages.favourites = new erpnext.hub.Favourites(this.$body); - } - - if (route[1] === 'category' && route[2] && !this.subpages.category) { - this.subpages.category = new erpnext.hub.Category(this.$body); - } - - if (route[1] === 'item' && route[2] && !this.subpages.item) { - this.subpages.item = new erpnext.hub.Item(this.$body); - } - - if (route[1] === 'register' && !this.subpages.register) { - this.subpages.register = new erpnext.hub.Register(this.$body); - } - - if (route[1] === 'publish' && !this.subpages.publish) { - this.subpages.publish = new erpnext.hub.Publish(this.$body); - } - - - if (!Object.keys(this.subpages).includes(route[1])) { - frappe.show_not_found(); - return; - } - - this.update_sidebar(); - frappe.utils.scroll_to(0); - this.subpages[route[1]].show(); - } -} - -class SubPage { - constructor(parent) { - this.$parent = $(parent); - this.make_wrapper(); - } - - make_wrapper() { - const page_name = frappe.get_route()[1]; - this.$wrapper = $(`
    `).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.$wrapper.find('.hub-card-container').empty(); - 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.append(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) { - 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.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') - } -
    -
    -
    -
    -
    - Seller Information -
    -
    - -
    -
    - ${company} -

    - Contact Seller -

    -
    -
    - -
    - -
    -
    - `; - - 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.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 `
    - -
    -
    -
    ${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(); - - 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', () => { - const form_values = this.field_group.get_values(); - frappe.call('erpnext.hub_node.doctype.hub_settings.hub_settings.register_seller', form_values) - .then(() => { - // Reload page and things ... but for now - frappe.msgprint('Registered successfully.'); - }); - }); - } -} - -erpnext.hub.Publish = class Publish extends SubPage { - make_wrapper() { - super.make_wrapper(); - const title_html = `${__('Select Products to Publish')}`; - const info = `

    ${__("Status decided by the 'Publish in Hub' field in Item.")}

    `; - 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 = ``; - - const select_all_button = ``; - const deselect_all_button = ``; - - const search_html = `
    - -
    `; - - const subpage_header = $(` -
    -
    - ${title_html} - ${subtitle_html} -
    - ${publish_button_html} -
    - - ${search_html} - - ${select_all_button} - ${deselect_all_button} - `); - - this.$wrapper.append(subpage_header); - - this.setup_events(); - } - - setup_events() { - this.$wrapper.find('.select-all').on('click', () => { - this.$wrapper.find('.hub-card').addClass('active'); - }); - - this.$wrapper.find('.deselect-all').on('click', () => { - this.$wrapper.find('.hub-card').removeClass('active'); - }); - - this.$wrapper.find('.publish-items').on('click', () => { - this.publish_selected_items() - .then(r => { - frappe.msgprint('check'); - }); - }); - - const $search_input = this.$wrapper.find('.hub-search-container input'); - this.search_value = ''; - - $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)); - } - - get_items_and_render() { - this.$wrapper.find('.hub-card-container').empty(); - this.get_valid_items() - .then(r => { - this.render(r.message); - }); - } - - refresh() { - this.get_items_and_render(); - } - - render(items) { - const items_container = $(get_item_card_container_html(items)); - items_container.addClass('static').on('click', '.hub-card', (e) => { - const $target = $(e.currentTarget); - $target.toggleClass('active'); - }); - - this.$wrapper.append(items_container); - } - - get_valid_items() { - return frappe.call( - 'erpnext.hub_node.get_valid_items', - { - search_value: this.search_value - } - ); - } - - publish_selected_items() { - const items_to_publish = []; - const items_to_unpublish = []; - this.$wrapper.find('.hub-card').map(function () { - const active = $(this).hasClass('active'); - - if(active) { - items_to_publish.push($(this).attr("data-id")); - } else { - items_to_unpublish.push($(this).attr("data-id")); - } - }); - - return frappe.call( - 'erpnext.hub_node.publish_selected_items', - { - items_to_publish: items_to_publish, - items_to_unpublish: items_to_unpublish - } - ); - } -} - -function get_item_card_container_html(items, title='') { - const items_html = (items || []).map(item => get_item_card_html(item)).join(''); - - const html = `
    -
    - ${title} -
    - ${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_name; - - const active = item.publish_in_hub; - - const id = item.hub_item_code || item.item_code; - - // Subtitle - 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); - - // Decide item link - const isLocal = item.source_type === "local"; - const route = !isLocal - ? `marketplace/item/${item.hub_item_code}` - : `Form/Item/${item.item_name}`; - - const card_route = isLocal ? '' : `data-route='${route}'`; - - const show_local_item_button = isLocal - ? `
    - -
    ` - : ''; - - const item_html = ` -
    -
    -
    -
    -
    ${title}
    -
    ${subtitle}
    -
    - -
    -
    - -
    - ${show_local_item_button} -
    -
    -
    - `; - - 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() { From f4884f810885ada0359b7a069a2318590c91604b Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Thu, 26 Jul 2018 18:37:01 +0530 Subject: [PATCH 016/238] Marketplace - Cache hub calls - call_hub_method using FrappeClient connection - Become a seller UX fixes --- erpnext/hub_node/__init__.py | 13 + .../doctype/hub_settings/hub_settings.py | 20 +- erpnext/public/js/hub/marketplace.js | 844 ++++++++++++++++++ 3 files changed, 867 insertions(+), 10 deletions(-) create mode 100644 erpnext/public/js/hub/marketplace.js diff --git a/erpnext/hub_node/__init__.py b/erpnext/hub_node/__init__.py index c8e40eb8e8..d15785f279 100644 --- a/erpnext/hub_node/__init__.py +++ b/erpnext/hub_node/__init__.py @@ -14,6 +14,19 @@ def enable_hub(): frappe.db.commit() return hub_settings +@frappe.whitelist() +def call_hub_method(method, params=None): + connection = get_client_connection() + + params = json.loads(params) + params.update({ + 'cmd': 'hub.hub.api.' + method + }) + + response = connection.post_api('hub.hub.api.' + method, params) + response = connection.post_request(params) + return response + @frappe.whitelist() def get_list(doctype, start=0, limit=20, fields=["*"], filters="{}", order_by=None): connection = get_client_connection() diff --git a/erpnext/hub_node/doctype/hub_settings/hub_settings.py b/erpnext/hub_node/doctype/hub_settings/hub_settings.py index 551423c8a2..561f1f0faa 100644 --- a/erpnext/hub_node/doctype/hub_settings/hub_settings.py +++ b/erpnext/hub_node/doctype/hub_settings/hub_settings.py @@ -86,15 +86,20 @@ class HubSettings(Document): post_url = hub_url + '/api/method/hub.hub.api.register' response = requests.post(post_url, data=data, headers = {'accept': 'application/json'}) - + response.raise_for_status() - + if response.ok: message = response.json().get('message') else: frappe.throw(json.loads(response.text)) - return message.get('password') if message else None + if message.get('email'): + self.create_hub_connector(message) + self.registered = 1 + self.save() + + return message or None def unregister(self): """ Disable the User on hub.erpnext.org""" @@ -149,11 +154,6 @@ def sync(): def register_seller(**kwargs): settings = frappe.get_doc('Hub Settings') settings.update(kwargs) - password = settings.register() + message = settings.register() - print(password) - - # if password: - # self.create_hub_connector(message) - # self.registered = 1 - # self.save() + return message.get('email') diff --git a/erpnext/public/js/hub/marketplace.js b/erpnext/public/js/hub/marketplace.js new file mode 100644 index 0000000000..2526b95eaf --- /dev/null +++ b/erpnext/public/js/hub/marketplace.js @@ -0,0 +1,844 @@ +frappe.provide('hub'); +frappe.provide('erpnext.hub'); + +erpnext.hub.Marketplace = class Marketplace { + constructor({ parent }) { + this.$parent = $(parent); + this.page = parent.page; + + frappe.db.get_doc('Hub Settings') + .then(doc => { + this.hub_settings = doc; + this.registered = doc.registered; + + 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); + + e.stopPropagation(); + }); + } + + make_sidebar() { + this.$sidebar = this.$parent.find('.layout-side-section').addClass('hidden-xs'); + + this.make_sidebar_nav_buttons(); + this.make_sidebar_categories(); + } + + make_sidebar_nav_buttons() { + let $nav_group = this.$sidebar.find('[data-nav-buttons]'); + if (!$nav_group.length) { + $nav_group = $('