[hub] merge conflicts
diff --git a/erpnext/public/js/hub/PageContainer.vue b/erpnext/public/js/hub/PageContainer.vue
index 794d4cb..bb9ba3e 100644
--- a/erpnext/public/js/hub/PageContainer.vue
+++ b/erpnext/public/js/hub/PageContainer.vue
@@ -86,6 +86,10 @@
 				}
 			}
 
+			if (!route) {
+				return NotFound;
+			}
+
 			return route_map[route];
 		}
 	}
diff --git a/erpnext/public/js/hub/Sidebar.vue b/erpnext/public/js/hub/Sidebar.vue
new file mode 100644
index 0000000..208d8fe
--- /dev/null
+++ b/erpnext/public/js/hub/Sidebar.vue
@@ -0,0 +1,102 @@
+<template>
+	<div ref="sidebar-container">
+		<ul class="list-unstyled hub-sidebar-group" data-nav-buttons>
+			<li class="hub-sidebar-item" v-for="item in items" :key="item.label" v-route="item.route" v-show="item.condition === undefined || item.condition()">
+				{{ item.label }}
+			</li>
+		</ul>
+		<ul class="list-unstyled hub-sidebar-group" data-categories>
+			<li class="hub-sidebar-item" v-for="category in categories" :key="category.label" v-route="category.route">
+				{{ category.label }}
+			</li>
+		</ul>
+	</div>
+</template>
+<script>
+export default {
+	data() {
+		return {
+			items: [
+				{
+					label: __('Browse'),
+					route: 'marketplace/home'
+				},
+				{
+					label: __('Become a Seller'),
+					action: this.show_register_dialog,
+					condition: () => !hub.settings.registered
+				},
+				{
+					label: __('Saved Products'),
+					route: 'marketplace/saved-products',
+					condition: () => hub.settings.registered
+				},
+				{
+					label: __('Your Profile'),
+					route: 'marketplace/profile',
+					condition: () => hub.settings.registered
+				},
+				{
+					label: __('Your Products'),
+					route: 'marketplace/my-products',
+					condition: () => hub.settings.registered
+				},
+				{
+					label: __('Publish Products'),
+					route: 'marketplace/publish',
+					condition: () => hub.settings.registered
+				},
+				{
+					label: __('Selling'),
+					route: 'marketplace/selling',
+					condition: () => hub.settings.registered
+				},
+				{
+					label: __('Buying'),
+					route: 'marketplace/buying',
+					condition: () => hub.settings.registered
+				},
+			],
+			categories: []
+		}
+	},
+	created() {
+		this.get_categories()
+			.then(categories => {
+				this.categories = categories.map(c => {
+					return {
+						label: __(c.name),
+						route: 'marketplace/category/' + c.name
+					}
+				});
+				this.categories.unshift({
+					label: __('All'),
+					route: 'marketplace/home'
+				});
+				this.$nextTick(() => {
+					this.update_sidebar_state();
+				});
+			});
+	},
+	mounted() {
+		this.update_sidebar_state();
+		frappe.route.on('change', () => this.update_sidebar_state());
+	},
+	methods: {
+		get_categories() {
+			return hub.call('get_categories');
+		},
+		update_sidebar_state() {
+			const container = $(this.$refs['sidebar-container']);
+			const route = frappe.get_route();
+			const route_str = route.join('/');
+			const part_route_str = route.slice(0, 2).join('/');
+			const $sidebar_item = container.find(`[data-route="${route_str}"], [data-route="${part_route_str}"]`);
+
+			const $siblings = container.find('[data-route]');
+			$siblings.removeClass('active').addClass('text-muted');
+			$sidebar_item.addClass('active').removeClass('text-muted');
+		}
+	}
+}
+</script>
diff --git a/erpnext/public/js/hub/marketplace.js b/erpnext/public/js/hub/marketplace.js
index c54a402..95a7542 100644
--- a/erpnext/public/js/hub/marketplace.js
+++ b/erpnext/public/js/hub/marketplace.js
@@ -3,6 +3,7 @@
 
 // components
 import PageContainer from './PageContainer.vue';
+import Sidebar from './Sidebar.vue';
 import { ProfileDialog } from './components/profile_dialog';
 
 // helpers
@@ -57,79 +58,10 @@
 	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 = $('<ul class="list-unstyled hub-sidebar-group" data-nav-buttons>').appendTo(this.$sidebar);
-		}
-		$nav_group.empty();
-
-		const user_specific_items_html = this.registered
-			? `<li class="hub-sidebar-item" data-route="marketplace/saved-products">
-					${__('Saved Products')}
-				</li>
-				<li class="hub-sidebar-item text-muted" data-route="marketplace/profile">
-					${__('Your Profile')}
-				</li>
-				<li class="hub-sidebar-item text-muted" data-route="marketplace/publish">
-					${__('Publish Products')}
-				</li>
-				<li class="hub-sidebar-item text-muted" data-route="marketplace/selling">
-					${__('Selling')}
-				</li>
-				<li class="hub-sidebar-item text-muted" data-route="marketplace/buying">
-					${__('Buying')}
-				</li>
-				`
-
-			: `<li class="hub-sidebar-item text-muted" data-action="show_register_dialog">
-					${__('Become a seller')}
-				</li>`;
-
-		$nav_group.append(`
-			<li class="hub-sidebar-item" data-route="marketplace/home">
-				${__('Browse')}
-			</li>
-			${user_specific_items_html}
-		`);
-	}
-
-	make_sidebar_categories() {
-		hub.call('get_categories')
-			.then(categories => {
-				categories = categories.map(d => d.name);
-
-				const sidebar_items = [
-					`<li class="hub-sidebar-item bold is-title">
-						${__('Category')}
-					</li>`,
-					`<li class="hub-sidebar-item active" data-route="marketplace/home">
-						${__('All')}
-					</li>`,
-					...(this.registered
-						? [`<li class="hub-sidebar-item active" data-route="marketplace/my-products">
-							${__('Your Products')}
-						</li>`]
-						: []),
-					...categories.map(category => `
-						<li class="hub-sidebar-item text-muted" data-route="marketplace/category/${category}">
-							${__(category)}
-						</li>
-					`)
-				];
-
-				this.$sidebar.append(`
-					<ul class="list-unstyled">
-						${sidebar_items.join('')}
-					</ul>
-				`);
-
-				this.update_sidebar();
-			});
+		new Vue({
+			el: $('<div>').appendTo(this.$sidebar)[0],
+			render: h => h(Sidebar)
+		});
 	}
 
 	make_body() {
@@ -147,18 +79,6 @@
 		});
 	}
 
-	update_sidebar() {
-		const route = frappe.get_route();
-		const route_str = route.join('/');
-		const part_route_str = route.slice(0, 2).join('/');
-		const $sidebar_item = this.$sidebar.find(`[data-route="${route_str}"], [data-route="${part_route_str}"]`);
-
-
-		const $siblings = this.$sidebar.find('[data-route]');
-		$siblings.removeClass('active').addClass('text-muted');
-		$sidebar_item.addClass('active').removeClass('text-muted');
-	}
-
 	refresh() {
 
 	}
diff --git a/erpnext/public/js/hub/pages/BuyingMessages.vue b/erpnext/public/js/hub/pages/BuyingMessages.vue
index 2ac956f..448c24a 100644
--- a/erpnext/public/js/hub/pages/BuyingMessages.vue
+++ b/erpnext/public/js/hub/pages/BuyingMessages.vue
@@ -1,6 +1,8 @@
 <template>
 	<div v-if="item_details">
-
+		<div>
+			<a class="text-muted" v-route="'marketplace/buying'">← {{ __('Back to Messages') }}</a>
+		</div>
 		<section-header>
 			<div class="flex flex-column margin-bottom">
 				<h4>{{ item_details.item_name }}</h4>
diff --git a/erpnext/public/js/hub/pages/Profile.vue b/erpnext/public/js/hub/pages/Profile.vue
index 209fa1b..a0bc6cb 100644
--- a/erpnext/public/js/hub/pages/Profile.vue
+++ b/erpnext/public/js/hub/pages/Profile.vue
@@ -7,7 +7,6 @@
 
 		<detail-view
 			:title="title"
-			:subtitles="subtitles"
 			:image="image"
 			:sections="sections"
 			:show_skeleton="init"
diff --git a/erpnext/public/js/hub/pages/buying_messages.js b/erpnext/public/js/hub/pages/buying_messages.js
deleted file mode 100644
index 7835fc3..0000000
--- a/erpnext/public/js/hub/pages/buying_messages.js
+++ /dev/null
@@ -1,101 +0,0 @@
-import SubPage from './subpage';
-
-erpnext.hub.MessageList = class BuyingMessages extends SubPage {
-	make_wrapper() {
-		const messages_of = this.options[0];
-		if (messages_of === 'Buying') {
-			this.back_route = 'marketplace/buying-messages'
-		} else {
-			this.back_route = 'marketplace/selling-messages'
-		}
-		super.make_wrapper();
-		this.add_back_link(__('Back to Messages'), this.back_route);
-		this.$message_container = this.add_section({ title: 'Buy' });
-	}
-
-	refresh() {
-		const item_code = frappe.get_route()[2] || null;
-		if (!item_code) {
-			frappe.set_route(this.back_route);
-			return;
-		}
-		this.get_item_details(item_code)
-			.then(item_details => {
-				this.item_details = item_details;
-				this.$message_container.find('.hub-section-header h4').text(this.item_details.item_name);
-
-				// make chat area
-				this.$message_container.find('.hub-section-body').html(`
-					<div class="col-md-7 message-container">
-						<div class="message-list"></div>
-						<div class="message-input"></div>
-					</div>
-				`)
-				this.make_message_input();
-
-				// fetch messages
-				this.get_messages(item_details)
-					.then(messages => {
-						const $message_list = this.$message_container.find('.message-list');
-						const html = messages.map(get_message_html).join('');
-						$message_list.html(html);
-						frappe.dom.scroll_to_bottom($message_list);
-					});
-			});
-
-	}
-
-	get_messages(item_details) {
-		 return hub.call('get_messages', {
-			against_seller: item_details.hub_seller,
-			against_item: item_details.hub_item_code
-		});
-	}
-
-	get_item_details(hub_item_code) {
-		return hub.call('get_item_details', { hub_item_code })
-	}
-
-	make_message_input() {
-		this.message_input = new frappe.ui.CommentArea({
-			parent: this.$message_container.find('.message-input'),
-			on_submit: (message) => {
-				this.message_input.reset();
-
-				// append message html
-				const $message_list = this.$message_container.find('.message-list');
-				const message_html = get_message_html({
-					sender: hub.settings.company_email,
-					content: message
-				});
-				$message_list.append(message_html);
-				frappe.dom.scroll_to_bottom($message_list);
-
-				// send message
-				hub.call('send_message', {
-					from_seller: hub.settings.company_email,
-					to_seller: this.item_details.hub_seller,
-					hub_item: this.item_details.hub_item_code,
-					message
-				});
-			},
-			no_wrapper: true
-		});
-	}
-}
-
-function get_message_html(message) {
-	return `
-		<div class="level margin-bottom">
-			<div class="level-left ellipsis" style="width: 80%;">
-				${frappe.avatar(message.sender)}
-				<div style="white-space: normal;">
-					${message.content}
-				</div>
-			</div>
-			<div class="level-right text-muted">
-				${comment_when(message.creation, true)}
-			</div>
-		</div>
-	`;
-}
\ No newline at end of file
diff --git a/erpnext/public/js/hub/vue-plugins.js b/erpnext/public/js/hub/vue-plugins.js
index 97bc66d..7d0619f 100644
--- a/erpnext/public/js/hub/vue-plugins.js
+++ b/erpnext/public/js/hub/vue-plugins.js
@@ -21,7 +21,9 @@
 Vue.directive('route', {
 	bind(el, binding) {
 		const route = binding.value;
+		if (!route) return;
 		el.classList.add('cursor-pointer');
+		el.dataset.route = route;
 		el.addEventListener('click', () => frappe.set_route(route));
 	},
 	unbind(el) {
diff --git a/erpnext/public/less/hub.less b/erpnext/public/less/hub.less
index 4cbe351..618f98f 100644
--- a/erpnext/public/less/hub.less
+++ b/erpnext/public/less/hub.less
@@ -100,6 +100,7 @@
 	}
 
 	.hub-card-image {
+		position: relative;
 		width: 100%;
 		height: 100%;
 		object-fit: contain;
@@ -212,6 +213,7 @@
 	}
 
 	.hub-list-image {
+		position: relative;
 		width: 58px;
 		height: 58px;
 		border-right: 1px solid @border-color;