Merge branch 'hub-redesign' of https://github.com/frappe/erpnext into hub-redesign
diff --git a/erpnext/hub_node/api.py b/erpnext/hub_node/api.py
index 559e22a..0c9af1a 100644
--- a/erpnext/hub_node/api.py
+++ b/erpnext/hub_node/api.py
@@ -2,6 +2,7 @@
 import frappe, requests, json
 from frappe.utils import now
 from frappe.frappeclient import FrappeClient
+from frappe.desk.form.load import get_attachments
 
 @frappe.whitelist()
 def call_hub_method(method, params=None):
@@ -31,22 +32,32 @@
 
 	valid_items = filter(lambda x: x.image and x.description, items)
 
-	def attach_source_type(item):
+	def prepare_item(item):
 		item.source_type = "local"
+		item.attachments = get_attachments('Item', item.item_code)
 		return item
 
-	valid_items = map(lambda x: attach_source_type(x), valid_items)
+	valid_items = map(lambda x: prepare_item(x), valid_items)
+
 	return valid_items
 
 @frappe.whitelist()
 def publish_selected_items(items_to_publish):
 	items_to_publish = json.loads(items_to_publish)
 	if not len(items_to_publish):
-		return
+		frappe.throw('No items to publish')
 
-	for item_code in items_to_publish:
+	for item in items_to_publish:
+		item_code = item.get('item_code')
 		frappe.db.set_value('Item', item_code, 'publish_in_hub', 1)
 
+		frappe.get_doc({
+			'doctype': 'Hub Tracked Item',
+			'item_code': item_code,
+			'hub_category': item.get('hub_category'),
+			'image_list': item.get('image_list')
+		}).insert()
+
 	try:
 		hub_settings = frappe.get_doc('Hub Settings')
 		item_sync_preprocess()
diff --git a/erpnext/hub_node/data_migration_mapping/item_to_hub_item/__init__.py b/erpnext/hub_node/data_migration_mapping/item_to_hub_item/__init__.py
index 9445e3a..0b6b2bc 100644
--- a/erpnext/hub_node/data_migration_mapping/item_to_hub_item/__init__.py
+++ b/erpnext/hub_node/data_migration_mapping/item_to_hub_item/__init__.py
@@ -1,19 +1,25 @@
-import io, base64, urllib, os
+import frappe, io, base64, urllib, os
 
 def pre_process(doc):
 
-	file_path = doc.image
-	file_name = os.path.basename(file_path)
+	# file_path = doc.image
+	# file_name = os.path.basename(file_path)
 
-	if file_path.startswith('http'):
-		url = file_path
-		file_path = os.path.join('/tmp', file_name)
-		urllib.urlretrieve(url, file_path)
+	# if file_path.startswith('http'):
+	# 	url = file_path
+	# 	file_path = os.path.join('/tmp', file_name)
+	# 	urllib.urlretrieve(url, file_path)
 
-	with io.open(file_path, 'rb') as f:
-		doc.image = base64.b64encode(f.read())
+	# with io.open(file_path, 'rb') as f:
+	# 	doc.image = base64.b64encode(f.read())
 
-	doc.image_file_name = file_name
+	# doc.image_file_name = file_name
+
+	cached_details = frappe.get_doc('Hub Tracked Item', doc.item_code)
+
+	if cached_details:
+		doc.hub_category = cached_details.hub_category
+		doc.image_list = cached_details.image_list
 
 	return doc
 
diff --git a/erpnext/hub_node/data_migration_mapping/item_to_hub_item/item_to_hub_item.json b/erpnext/hub_node/data_migration_mapping/item_to_hub_item/item_to_hub_item.json
index 3ace088..bcece69 100644
--- a/erpnext/hub_node/data_migration_mapping/item_to_hub_item/item_to_hub_item.json
+++ b/erpnext/hub_node/data_migration_mapping/item_to_hub_item/item_to_hub_item.json
@@ -26,8 +26,18 @@
   }, 
   {
    "is_child_table": 0, 
+   "local_fieldname": "image_list", 
+   "remote_fieldname": "image_list"
+  }, 
+  {
+   "is_child_table": 0, 
    "local_fieldname": "item_group", 
    "remote_fieldname": "item_group"
+  }, 
+  {
+   "is_child_table": 0, 
+   "local_fieldname": "hub_category", 
+   "remote_fieldname": "hub_category"
   }
  ], 
  "idx": 1, 
@@ -35,7 +45,7 @@
  "mapping_name": "Item to Hub Item", 
  "mapping_type": "Push", 
  "migration_id_field": "hub_sync_id", 
- "modified": "2018-08-01 16:37:09.170546", 
+ "modified": "2018-08-19 22:20:25.727581", 
  "modified_by": "Administrator", 
  "name": "Item to Hub Item", 
  "owner": "Administrator", 
diff --git a/erpnext/hub_node/data_migration_plan/hub_sync/hub_sync.json b/erpnext/hub_node/data_migration_plan/hub_sync/hub_sync.json
index 1f772b6..e90b1dd 100644
--- a/erpnext/hub_node/data_migration_plan/hub_sync/hub_sync.json
+++ b/erpnext/hub_node/data_migration_plan/hub_sync/hub_sync.json
@@ -9,7 +9,7 @@
    "mapping": "Item to Hub Item"
   }
  ], 
- "modified": "2018-08-01 16:37:09.027512", 
+ "modified": "2018-08-19 22:20:25.644602", 
  "modified_by": "Administrator", 
  "module": "Hub Node", 
  "name": "Hub Sync", 
diff --git a/erpnext/hub_node/doctype/hub_tracked_item/hub_tracked_item.json b/erpnext/hub_node/doctype/hub_tracked_item/hub_tracked_item.json
index 063c878..9384adb 100644
--- a/erpnext/hub_node/doctype/hub_tracked_item/hub_tracked_item.json
+++ b/erpnext/hub_node/doctype/hub_tracked_item/hub_tracked_item.json
@@ -3,6 +3,7 @@
  "allow_guest_to_view": 0, 
  "allow_import": 0, 
  "allow_rename": 0, 
+ "autoname": "field:item_code", 
  "beta": 0, 
  "creation": "2018-03-18 09:33:50.267762", 
  "custom": 0, 
@@ -14,6 +15,7 @@
  "fields": [
   {
    "allow_bulk_edit": 0, 
+   "allow_in_quick_entry": 0, 
    "allow_on_submit": 0, 
    "bold": 0, 
    "collapsible": 0, 
@@ -41,6 +43,70 @@
    "search_index": 0, 
    "set_only_once": 0, 
    "translatable": 0, 
+   "unique": 1
+  }, 
+  {
+   "allow_bulk_edit": 0, 
+   "allow_in_quick_entry": 0, 
+   "allow_on_submit": 0, 
+   "bold": 0, 
+   "collapsible": 0, 
+   "columns": 0, 
+   "fieldname": "hub_category", 
+   "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 Category", 
+   "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": "image_list", 
+   "fieldtype": "Long Text", 
+   "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": "Image List", 
+   "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
   }
  ], 
@@ -49,12 +115,12 @@
  "hide_toolbar": 0, 
  "idx": 0, 
  "image_view": 0, 
- "in_create": 1, 
+ "in_create": 0, 
  "is_submittable": 0, 
  "issingle": 0, 
  "istable": 0, 
  "max_attachments": 0, 
- "modified": "2018-03-18 09:34:01.757713", 
+ "modified": "2018-08-19 22:24:06.207307", 
  "modified_by": "Administrator", 
  "module": "Hub Node", 
  "name": "Hub Tracked Item", 
@@ -63,7 +129,6 @@
  "permissions": [
   {
    "amend": 0, 
-   "apply_user_permissions": 0, 
    "cancel": 0, 
    "create": 1, 
    "delete": 1, 
@@ -89,5 +154,6 @@
  "sort_field": "modified", 
  "sort_order": "DESC", 
  "track_changes": 1, 
- "track_seen": 0
+ "track_seen": 0, 
+ "track_views": 0
 }
\ No newline at end of file
diff --git a/erpnext/public/js/hub/components/profile_dialog.js b/erpnext/public/js/hub/components/profile_dialog.js
new file mode 100644
index 0000000..800e8c6
--- /dev/null
+++ b/erpnext/public/js/hub/components/profile_dialog.js
@@ -0,0 +1,77 @@
+const ProfileDialog = (title = __('Edit Profile'), action={}, initial_values={}) => {
+    const fields = [
+        {
+            fieldtype: 'Link',
+            fieldname: 'company',
+            label: __('Company'),
+            options: 'Company',
+            onchange: () => {
+                const value = dialog.get_value('company');
+
+                if (value) {
+                    frappe.db.get_doc('Company', value)
+                        .then(company => {
+                            dialog.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'
+        }
+    ];
+
+    let dialog = new frappe.ui.Dialog({
+        title: title,
+        fields: fields,
+        primary_action_label: action.label || __('Update'),
+        primary_action: () => {
+            const form_values = dialog.get_values();
+            let values_filled = true;
+            const mandatory_fields = ['company', 'company_email', 'company_description'];
+            mandatory_fields.forEach(field => {
+                const value = form_values[field];
+                if (!value) {
+                    dialog.set_df_property(field, 'reqd', 1);
+                    values_filled = false;
+                }
+            });
+            if (!values_filled) return;
+
+            action.on_submit(form_values);
+        }
+    });
+
+    dialog.set_values(initial_values);
+
+    // Post create
+    const default_company = frappe.defaults.get_default('company');
+    dialog.set_value('company', default_company);
+
+    return dialog;
+}
+
+export {
+	ProfileDialog
+}
diff --git a/erpnext/public/js/hub/marketplace.js b/erpnext/public/js/hub/marketplace.js
index ee21d48..994aebc 100644
--- a/erpnext/public/js/hub/marketplace.js
+++ b/erpnext/public/js/hub/marketplace.js
@@ -12,6 +12,9 @@
 import './pages/messages';
 import './pages/not_found';
 
+// components
+import { ProfileDialog } from './components/profile_dialog';
+
 // helpers
 import './hub_call';
 import EventEmitter from './event_emitter';
@@ -49,6 +52,16 @@
 			const route = $target.data().route;
 			frappe.set_route(route);
 		});
+
+		// generic action handler
+		this.$parent.on('click', '[data-action]', e => {
+			const $target = $(e.currentTarget);
+			const action = $target.data().action;
+
+			if (action && this[action]) {
+				this[action].apply(this, $target);
+			}
+		})
 	}
 
 	make_sidebar() {
@@ -79,7 +92,7 @@
 					${__('Messages')}
 				</li>`
 
-			: `<li class="hub-sidebar-item text-muted" data-route="marketplace/register">
+			: `<li class="hub-sidebar-item text-muted" data-action="show_register_dialog">
 					${__('Become a seller')}
 				</li>`;
 
@@ -218,4 +231,30 @@
 		frappe.utils.scroll_to(0);
 		this.subpages[route[1]].show();
 	}
+
+	show_register_dialog() {
+		this.register_dialog = ProfileDialog(
+			__('Become a Seller'),
+			{
+				label: __('Register'),
+				on_submit: this.register_seller.bind(this)
+			}
+		);
+
+		this.register_dialog.show();
+	}
+
+	register_seller(form_values) {
+		frappe.call({
+		    method: 'erpnext.hub_node.doctype.hub_settings.hub_settings.register_seller',
+		    args: form_values,
+		    btn: $(e.currentTarget)
+		}).then(() => {
+			this.register_dialog.hide();
+			frappe.set_route('marketplace', 'publish');
+
+		    // custom jquery event
+		    this.$body.trigger('seller-registered');
+		});
+	}
 }
diff --git a/erpnext/public/js/hub/pages/profile.js b/erpnext/public/js/hub/pages/profile.js
index a38cde4..a57bc7c 100644
--- a/erpnext/public/js/hub/pages/profile.js
+++ b/erpnext/public/js/hub/pages/profile.js
@@ -1,19 +1,30 @@
 import SubPage from './subpage';
+import { get_detail_skeleton_html } from '../components/skeleton_state';
+import { ProfileDialog } from '../components/profile_dialog';
 
 erpnext.hub.Profile = class Profile extends SubPage {
 	make_wrapper() {
 		super.make_wrapper();
+		this.make_edit_profile_dialog();
 	}
 
 	refresh() {
+		this.show_skeleton();
 		this.get_hub_seller_profile(this.keyword)
-			.then(profile => this.render(profile));
+			.then(profile => {
+				this.edit_profile_dialog.set_values(profile);
+				this.render(profile);
+			});
 	}
 
 	get_hub_seller_profile() {
 		return hub.call('get_hub_seller_profile', { hub_seller: hub.settings.company_email });
 	}
 
+	show_skeleton() {
+		this.$wrapper.html(get_detail_skeleton_html());
+	}
+
 	render(profile) {
 		const p = profile;
 		const content_by_log_type = this.get_content_by_log_type();
@@ -46,7 +57,7 @@
 						<img src="${p.logo}">
 					</div>
 				</div>
-				<div class="col-md-6">
+				<div class="col-md-8">
 					<h2>${p.company}</h2>
 					<div class="text-muted">
 						<p>${p.country}</p>
@@ -60,6 +71,16 @@
 					}
 					</div>
 				</div>
+				<div class="col-md-1">
+					<div class="dropdown pull-right hub-item-dropdown">
+						<a class="dropdown-toggle btn btn-xs btn-default" data-toggle="dropdown">
+							<span class="caret"></span>
+						</a>
+						<ul class="dropdown-menu dropdown-right" role="menu">
+						<li><a data-action="edit_profile">${__('Edit Profile')}</a></li>
+						</ul>
+					</div>
+				</div>
 			</div>
 
 			<div class="timeline">
@@ -73,6 +94,33 @@
 		this.$wrapper.html(profile_html);
 	}
 
+	make_edit_profile_dialog() {
+		this.edit_profile_dialog = ProfileDialog(
+			__('Edit Profile'),
+			{
+				label: __('Update'),
+				on_submit: this.update_profile.bind(this)
+			}
+		);
+	}
+
+	edit_profile() {
+		this.edit_profile_dialog.set_values({
+			company_email: hub.settings.company_email
+		});
+		this.edit_profile_dialog.show();
+	}
+
+	update_profile(new_values) {
+		hub.call('update_profile', {
+			hub_seller: hub.settings.company_email,
+			updated_profile: new_values
+		}).then(new_profile => {
+				this.edit_profile_dialog.hide();
+				this.render(new_profile);
+			});
+	}
+
 	get_timeline_log_item(pretty_date, message, icon) {
 		return `<div class="media timeline-item  notification-content">
 			<div class="small">
@@ -95,4 +143,4 @@
 			}
 		}
 	}
-}
\ No newline at end of file
+}
diff --git a/erpnext/public/js/hub/pages/publish.js b/erpnext/public/js/hub/pages/publish.js
index a76f946..46e4545 100644
--- a/erpnext/public/js/hub/pages/publish.js
+++ b/erpnext/public/js/hub/pages/publish.js
@@ -3,12 +3,17 @@
 import { get_local_item_card_html } from '../components/item_card';
 import { make_search_bar } from '../components/search_bar';
 
+
 erpnext.hub.Publish = class Publish extends SubPage {
 	make_wrapper() {
 		super.make_wrapper();
-		this.items_to_publish = [];
+		this.items_to_publish = {};
 		this.unpublished_items = [];
 		this.fetched_items = [];
+		this.fetched_items_dict = {};
+
+		this.cache = erpnext.hub.cache.items_to_publish;
+		this.cache = [];
 
 		frappe.realtime.on("items-sync", (data) => {
 			this.$wrapper.find('.progress-bar').css('width', data.progress_percent+'%');
@@ -58,25 +63,34 @@
 	}
 
 	get_publishing_header() {
-		const title_html = `<b>${__('Select Products to Publish')}</b>`;
+		const title_html = `<h5>${__('Select Products to Publish')}</h5>`;
 
 		const subtitle_html = `<p class="text-muted">
 			${__(`Only products with an image, description and category can be published.
 			Please update them if an item in your inventory does not appear.`)}
 		</p>`;
 
-		const publish_button_html = `<button class="btn btn-primary btn-sm publish-items">
+		const publish_button_html = `<button class="btn btn-primary btn-sm publish-items" disabled>
 			<i class="visible-xs octicon octicon-check"></i>
 			<span class="hidden-xs">${__('Publish')}</span>
 		</button>`;
 
 		return $(`
+			<div class="publish-area empty">
+				<div class="flex justify-between align-flex-end">
+					${title_html}
+					${publish_button_html}
+				</div>
+				<div class="empty-items-container flex align-center flex-column justify-center">
+					<p class="text-muted">${__('No Items Selected')}</p>
+				</div>
+				<div class="row hub-items-container selected-items"></div>
+			</div>
+
 			<div class='subpage-title flex'>
 				<div>
-					${title_html}
 					${subtitle_html}
 				</div>
-				${publish_button_html}
 			</div>
 		`);
 	}
@@ -87,27 +101,117 @@
 				.then(this.refresh.bind(this))
 		});
 
+		this.selected_items_container = this.$wrapper.find('.selected-items');
+
+		this.$current_selected_card = null;
+
+		this.make_publishing_dialog();
+
 		this.$wrapper.on('click', '.hub-card', (e) => {
 			const $target = $(e.currentTarget);
-			$target.toggleClass('active');
+			const item_code = $target.attr('data-id');
+			this.show_publishing_dialog_for_item(item_code);
 
-			// Get total items
-			const total_items = this.$wrapper.find('.hub-card.active').length;
+			this.$current_selected_card = $target.parent();
 
-			let button_label;
-			if (total_items > 0) {
-				const more_than_one = total_items > 1;
-				button_label = __('Publish {0} item{1}', [total_items, more_than_one ? 's' : '']);
-			} else {
-				button_label = __('Publish');
-			}
-
-			this.$wrapper.find('.publish-items')
-				.text(button_label)
-				.prop('disabled', total_items === 0);
 		});
 	}
 
+	make_publishing_dialog() {
+		this.publishing_dialog = new frappe.ui.Dialog({
+			title: __('Edit Publishing Details'),
+			fields: [
+				{
+					"label": "Item Code",
+					"fieldname": "item_code",
+					"fieldtype": "Data",
+					"read_only": 1
+				},
+				{
+					"label": "Hub Category",
+					"fieldname": "hub_category",
+					"fieldtype": "Autocomplete",
+					"options": ["Agriculture", "Books", "Chemicals", "Clothing",
+						"Electrical", "Electronics", "Energy", "Fashion", "Food and Beverage",
+						"Health", "Home", "Industrial", "Machinery", "Packaging and Printing",
+						"Sports", "Transportation"
+					],
+					"reqd": 1
+				},
+				{
+					"label": "Images",
+					"fieldname": "image_list",
+					"fieldtype": "MultiSelect",
+					"options": [],
+					"reqd": 1
+				}
+			],
+			primary_action_label: __('Set Details'),
+			primary_action: () => {
+				const values = this.publishing_dialog.get_values(true);
+				this.items_to_publish[values.item_code] = values;
+
+				this.$current_selected_card.appendTo(this.selected_items_container);
+				this.$current_selected_card.find('.hub-card').toggleClass('active');
+
+				this.update_selected_items_count();
+
+				this.publishing_dialog.hide();
+			},
+			secondary_action: () => {
+				const values = this.publishing_dialog.get_values(true);
+				this.items_to_publish[values.item_code] = values;
+			}
+		});
+	}
+
+	show_publishing_dialog_for_item(item_code) {
+		let item_data = this.items_to_publish[item_code];
+
+		if(!item_data) { item_data = { item_code }; };
+
+		this.publishing_dialog.clear();
+
+		const item_doc = this.fetched_items_dict[item_code];
+		if(item_doc) {
+			this.publishing_dialog.fields_dict.image_list.set_data(
+				item_doc.attachments.map(attachment => attachment.file_url)
+			);
+		}
+
+		this.publishing_dialog.set_values(item_data);
+		this.publishing_dialog.show();
+	}
+
+	update_selected_items_count() {
+		const total_items = this.$wrapper.find('.hub-card.active').length;
+
+		const is_empty = total_items === 0;
+
+		let button_label;
+		if (total_items > 0) {
+			const more_than_one = total_items > 1;
+			button_label = __('Publish {0} item{1}', [total_items, more_than_one ? 's' : '']);
+		} else {
+			button_label = __('Publish');
+		}
+
+		this.$wrapper.find('.publish-items')
+			.text(button_label)
+			.prop('disabled', is_empty);
+
+		this.$wrapper.find('.publish-area').toggleClass('empty', is_empty);
+		this.$wrapper.find('.publish-area').toggleClass('filled', !is_empty);
+	}
+
+	add_item_to_publish() {
+		//
+	}
+
+	remove_item_from_publish() {
+		//
+	}
+
 	show_message(message) {
 		const $message = $(`<div class="subpage-message">
 			<p class="text-muted flex">
@@ -137,7 +241,7 @@
 
 		this.$wrapper.append(subtitle_html);
 
-		// Show search list with only desctiption, and don't set any events
+		// Show search list with only description, and don't set any events
 		make_search_bar({
 			wrapper: this.$wrapper,
 			on_search: keyword => {
@@ -191,6 +295,10 @@
 		const items_container = $(get_item_card_container_html(items, '', get_local_item_card_html));
 		items_container.addClass('results');
 		wrapper.append(items_container);
+
+		items.map(item => {
+			this.fetched_items_dict[item.item_code] = item;
+		})
 	}
 
 	get_valid_items() {
@@ -211,19 +319,22 @@
 			item_codes_to_publish.push($(this).attr("data-id"));
 		});
 
-		this.unpublished_items = this.fetched_items.filter(item => {
-			return !item_codes_to_publish.includes(item.item_code);
-		});
+		// this.unpublished_items = this.fetched_items.filter(item => {
+		// 	return !item_codes_to_publish.includes(item.item_code);
+		// });
 
-		const items_to_publish = this.fetched_items.filter(item => {
-			return item_codes_to_publish.includes(item.item_code);
-		});
-		this.items_to_publish = items_to_publish;
+		// const items_to_publish = this.fetched_items.filter(item => {
+		// 	return item_codes_to_publish.includes(item.item_code);
+		// });
+
+		// this.items_to_publish = items_to_publish;
+
+		const items_data_to_publish = item_codes_to_publish.map(item_code => this.items_to_publish[item_code])
 
 		return frappe.call(
 			'erpnext.hub_node.api.publish_selected_items',
 			{
-				items_to_publish: item_codes_to_publish
+				items_to_publish: items_data_to_publish
 			}
 		)
 	}
diff --git a/erpnext/public/less/hub.less b/erpnext/public/less/hub.less
index ac0aa42..2bfb109 100644
--- a/erpnext/public/less/hub.less
+++ b/erpnext/public/less/hub.less
@@ -6,7 +6,7 @@
 		padding-right: 25px;
 	}
 
-	[data-route] {
+	[data-route], [data-action] {
 		cursor: pointer;
 	}
 
@@ -211,6 +211,25 @@
 		height: 500px;
 	}
 
+	.empty-items-container {
+		height: 80px;
+		border-radius: 4px;
+		border: 1px solid @border-color;
+		margin: 15px 0px;
+	}
+
+	.publish-area.filled {
+		.empty-items-container {
+			display: none;
+		}
+	}
+
+	.publish-area.empty {
+		.hub-items-container {
+			display: none;
+		}
+	}
+
 	.form-container {
 		.frappe-control {
 			max-width: 100% !important;