[hub] resolve merge conflicts
diff --git a/erpnext/public/js/hub/marketplace.js b/erpnext/public/js/hub/marketplace.js
index 2214956..4c3e9ef 100644
--- a/erpnext/public/js/hub/marketplace.js
+++ b/erpnext/public/js/hub/marketplace.js
@@ -28,8 +28,6 @@
 			const $target = $(e.currentTarget);
 			const route = $target.data().route;
 			frappe.set_route(route);
-
-			e.stopPropagation();
 		});
 	}
 
@@ -222,8 +220,11 @@
 	make_wrapper() {
 		super.make_wrapper();
 
-		make_search_bar(this.$wrapper, keyword => {
-			frappe.set_route('marketplace', 'search', keyword);
+		make_search_bar({
+			wrapper: this.$wrapper,
+			on_search: keyword => {
+				frappe.set_route('marketplace', 'search', keyword);
+			}
 		});
 	}
 
@@ -302,8 +303,11 @@
 	make_wrapper() {
 		super.make_wrapper();
 
-		make_search_bar(this.$wrapper, keyword => {
-			frappe.set_route('marketplace', 'search', keyword);
+		make_search_bar({
+			wrapper: this.$wrapper,
+			on_search: keyword => {
+				frappe.set_route('marketplace', 'search', keyword);
+			}
 		});
 	}
 
@@ -840,15 +844,11 @@
 
 		const publish_button_html = `<button class="btn btn-primary btn-sm publish-items">
 			<i class="visible-xs octicon octicon-check"></i>
-			<span class="hidden-xs">Publish</span>
+			<span class="hidden-xs">${__('Publish')}</span>
 		</button>`;
 
-		const select_all_button = `<button class="btn btn-secondary btn-default btn-xs margin-right select-all">Select All</button>`;
-		const deselect_all_button = `<button class="btn btn-secondary btn-default btn-xs deselect-all">Deselect All</button>`;
-
-		const search_html = `<div class="hub-search-container">
-			<input type="text" class="form-control" placeholder="Search Items">
-		</div>`;
+		const select_all_button = `<button class="btn btn-secondary btn-default btn-xs margin-right select-all">${__('Select All')}</button>`;
+		const deselect_all_button = `<button class="btn btn-secondary btn-default btn-xs deselect-all">${__('Deselect All')}</button>`;
 
 		const subpage_header = $(`
 			<div class='subpage-title flex'>
@@ -858,12 +858,19 @@
 				</div>
 				${publish_button_html}
 			</div>
-
-			${search_html}
 		`);
 
 		this.$wrapper.append(subpage_header);
 
+		make_search_bar({
+			wrapper: this.$wrapper,
+			on_search: keyword => {
+				this.search_value = keyword;
+				this.get_items_and_render();
+			},
+			placeholder: __('Search Items')
+		});
+
 		this.setup_events();
 	}
 
@@ -871,19 +878,29 @@
 		this.$wrapper.find('.publish-items').on('click', () => {
 			this.publish_selected_items()
 				.then(r => {
-					frappe.msgprint('check');
+					console.log(`${r.message} items will be published`);
 				});
 		});
 
-		const $search_input = this.$wrapper.find('.hub-search-container input');
-		this.search_value = '';
+		this.$wrapper.on('click', '.hub-card', (e) => {
+			const $target = $(e.currentTarget);
+			$target.toggleClass('active');
 
-		$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();
+			// Get total items
+			const total_items = this.$wrapper.find('.hub-card.active').length;
+
+			let button_label;
+			if (total_items > 0) {
+				const more_than_one = total_items > 1;
+				button_label = __('Publish {0} item{1}', [total_items, more_than_one ? 's' : '']);
+			} else {
+				button_label = __('Publish');
 			}
-		}, 300));
+
+			this.$wrapper.find('.publish-items')
+				.text(button_label)
+				.prop('disabled', total_items === 0);
+		});
 	}
 
 	get_items_and_render() {
@@ -900,22 +917,15 @@
 	}
 
 	refresh() {
-		this.get_items_and_render();
+		if (hub.settings.sync_in_progress) {
+			this.load_publishing_state();
+		} else {
+			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');
-
-			// 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' : '')));
-		});
-
+		const items_container = $(get_item_card_container_html(items, '', get_local_item_card_html));
 		this.$wrapper.append(items_container);
 	}
 
@@ -988,8 +998,8 @@
 	</div>`;
 }
 
-function get_item_card_container_html(items, title='') {
-	const items_html = (items || []).map(item => get_item_card_html(item)).join('');
+function get_item_card_container_html(items, title='', get_item_html = get_item_card_html) {
+	const items_html = (items || []).map(item => get_item_html(item)).join('');
 	const title_html = title
 		? `<div class="col-sm-12 margin-bottom">
 				<b>${title}</b>
@@ -1008,11 +1018,44 @@
 	const item_name = item.item_name || item.name;
 	const title = strip_html(item_name);
 	const img_url = item.image;
-
 	const company_name = item.company;
 
-	const active = item.publish_in_hub;
+	// Subtitle
+	let subtitle = [comment_when(item.creation)];
+	const rating = item.average_rating;
+	if (rating > 0) {
+		subtitle.push(rating + `<i class='fa fa-fw fa-star-o'></i>`)
+	}
+	subtitle.push(company_name);
 
+	let dot_spacer = '<span aria-hidden="true"> · </span>';
+	subtitle = subtitle.join(dot_spacer);
+
+	const item_html = `
+		<div class="col-md-3 col-sm-4 col-xs-6">
+			<div class="hub-card" data-route="marketplace/item/${item.hub_item_code}">
+				<div class="hub-card-header">
+					<div class="hub-card-title ellipsis bold">${title}</div>
+					<div class="hub-card-subtitle ellipsis text-muted">${subtitle}</div>
+				</div>
+				<div class="hub-card-body">
+					<img class="hub-card-image" src="${img_url}" />
+					<div class="overlay hub-card-overlay"></div>
+				</div>
+			</div>
+		</div>
+	`;
+
+	return item_html;
+}
+
+function get_local_item_card_html(item) {
+	const item_name = item.item_name || item.name;
+	const title = strip_html(item_name);
+	const img_url = item.image;
+	const company_name = item.company;
+
+	const is_active = item.publish_in_hub;
 	const id = item.hub_item_code || item.item_code;
 
 	// Subtitle
@@ -1026,36 +1069,27 @@
 	let dot_spacer = '<span aria-hidden="true"> · </span>';
 	subtitle = subtitle.join(dot_spacer);
 
-	// Decide item link
-	const is_local = item.source_type === "local";
-	const route = !is_local
-		? `marketplace/item/${item.hub_item_code}`
-		: `Form/Item/${item.item_name}`;
-
-	const card_route = is_local ? '' : `data-route='${route}'`;
-
-	const show_local_item_button = is_local
-		? `<div class="overlay button-overlay" data-route='${route}' onclick="event.preventDefault();">
-				<button class="btn btn-default zoom-view">
-					<i class="octicon octicon-eye"></i>
-				</button>
-			</div>`
-		: '';
+	const edit_item_button = `<div class="hub-card-overlay-button" style="right: 15px; bottom: 15px;" data-route="Form/Item/${item.item_name}">
+		<button class="btn btn-default zoom-view">
+			<i class="octicon octicon-pencil text-muted"></i>
+		</button>
+	</div>`;
 
 	const item_html = `
 		<div class="col-md-3 col-sm-4 col-xs-6">
-			<div class="hub-card ${active ? 'active' : ''}" ${card_route} data-id="${id}">
+			<div class="hub-card is-local ${is_active ? 'active' : ''}" data-id="${id}">
 				<div class="hub-card-header">
-					<div class="title">
-						<div class="hub-card-title ellipsis bold">${title}</div>
-						<div class="hub-card-subtitle ellipsis text-muted">${subtitle}</div>
-					</div>
+					<div class="hub-card-title ellipsis bold">${title}</div>
+					<div class="hub-card-subtitle ellipsis text-muted">${subtitle}</div>
 					<i class="octicon octicon-check text-success"></i>
 				</div>
 				<div class="hub-card-body">
 					<img class="hub-card-image" src="${img_url}" />
-					<div class="overlay hub-card-overlay"></div>
-					${show_local_item_button}
+					<div class="hub-card-overlay">
+						<div class="hub-card-overlay-body">
+							${edit_item_button}
+						</div>
+					</div>
 				</div>
 			</div>
 		</div>
@@ -1064,6 +1098,7 @@
 	return item_html;
 }
 
+
 function get_rating_html(rating) {
 	let rating_html = ``;
 	for (var i = 0; i < 5; i++) {
@@ -1074,13 +1109,13 @@
 	return rating_html;
 }
 
-function make_search_bar($wrapper, on_search) {
+function make_search_bar({wrapper, on_search, placeholder = __('Search for anything')}) {
 	const $search = $(`
 		<div class="hub-search-container">
-			<input type="text" class="form-control" placeholder="${__('Search for anything')}">
+			<input type="text" class="form-control" placeholder="${placeholder}">
 		</div>`
 	);
-	$wrapper.append($search);
+	wrapper.append($search);
 	const $search_input = $search.find('input');
 
 	$search_input.on('keydown', frappe.utils.debounce((e) => {
diff --git a/erpnext/public/less/hub.less b/erpnext/public/less/hub.less
index 20bf3c1..a432156 100644
--- a/erpnext/public/less/hub.less
+++ b/erpnext/public/less/hub.less
@@ -43,10 +43,12 @@
 		overflow: hidden;
 		cursor: pointer;
 
-		&:hover .overlay {
+		&:hover .hub-card-overlay {
 			display: block;
 		}
+	}
 
+	.hub-card.is-local {
 		&.active {
 			.hub-card-header {
 				background-color: #f4ffe5;
@@ -56,42 +58,45 @@
 				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;
+			position: absolute;
 			font-size: 20px;
+			right: 15px;
+			top: 50%;
+			transform: translateY(-50%);
 		}
 	}
 
+	.hub-card-header {
+		position: relative;
+		padding: 12px 15px;
+		height: 60px;
+		border-bottom: 1px solid @border-color;
+	}
+
 	.hub-card-body {
 		position: relative;
 		height: 200px;
 	}
 
-	.overlay {
+	.hub-card-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-overlay-body {
+		position: relative;
+		height: 100%;
+	}
+
+	.hub-card-overlay-button {
+		position: absolute;
 	}
 
 	.hub-card-image {
@@ -185,6 +190,14 @@
 		padding-bottom: 0;
 		border-bottom: none;
 	}
+
+	.hub-item-container {
+		overflow: hidden;
+	}
+
+	.hub-item-review-container {
+		margin-top: calc(30vh);
+	}
 }
 
 body[data-route^="Hub/"] {