[hub][vue] init item card container in vue
diff --git a/erpnext/public/js/hub/App.vue b/erpnext/public/js/hub/App.vue
new file mode 100644
index 0000000..37ec4f3
--- /dev/null
+++ b/erpnext/public/js/hub/App.vue
@@ -0,0 +1,34 @@
+<template>
+	<div class="app">
+		<hello></hello>
+	</div>
+</template>
+
+<script>
+import ItemCardsContainer from './components/ItemCardsContainer.vue';
+export default {
+	name: 'app',
+	components: {
+		ItemCardsContainer
+	}
+}
+</script>
+
+<style>
+body {
+	display: flex;
+	align-items: center;
+	justify-content: center;
+	height: 100%;
+	margin: 0;
+}
+.app {
+	color: #444;
+	margin-top: 100px;
+	max-width: 600px;
+	font-family: Helvetica, sans-serif;
+	text-align: center;
+	display: flex;
+	align-items: center;
+}
+</style>
diff --git a/erpnext/public/js/hub/components/ItemCard.vue b/erpnext/public/js/hub/components/ItemCard.vue
new file mode 100644
index 0000000..7a7f821
--- /dev/null
+++ b/erpnext/public/js/hub/components/ItemCard.vue
@@ -0,0 +1,42 @@
+<template>
+	<div class="col-md-3 col-sm-4 col-xs-6 hub-card-container">
+		<div class="hub-card is_local">
+			<div class="hub-card-header flex">
+				<div>
+					<div class="hub-card-title ellipsis bold">{{ title }}</div>
+					<div class="hub-card-subtitle ellipsis text-muted" v-html='subtitle'></div>
+				</div>
+			</div>
+			<div class="hub-card-body">
+				<img class="hub-card-image" :src="item.image"/>
+				<div class="hub-card-overlay">
+					<div class="hub-card-overlay-body">
+						<div class="hub-card-overlay-button" style="right: 15px; bottom: 15px;">
+							<button class="btn btn-default zoom-view">
+								<i class="octicon octicon-pencil text-muted"></i>
+							</button>
+						</div>
+					</div>
+				</div>
+			</div>
+		</div>
+	</div>
+</template>
+
+<script>
+
+export default {
+	name: 'item-card',
+	props: ['item', 'is_local'],
+	computed: {
+		title() {
+			return this.item.item_name
+		},
+		subtitle() {
+			return comment_when(this.item.creation);
+		}
+	}
+}
+</script>
+
+<style scoped></style>
diff --git a/erpnext/public/js/hub/components/ItemCardsContainer.vue b/erpnext/public/js/hub/components/ItemCardsContainer.vue
new file mode 100644
index 0000000..8014296
--- /dev/null
+++ b/erpnext/public/js/hub/components/ItemCardsContainer.vue
@@ -0,0 +1,30 @@
+<template>
+	<div>
+		<item-card
+			v-for="item in items"
+			:key="item[item_id]"
+			:item="item"
+			:is_local="is_local"
+		>
+		</item-card>
+	</div>
+</template>
+
+<script>
+import ItemCard from './ItemCard.vue';
+
+export default {
+	name: 'item-cards-container',
+	props: ['items', 'is_local'],
+	components: {
+		ItemCard
+	},
+	computed: {
+		item_id() {
+			return this.is_local ? 'item_code' : 'hub_item_code';
+		}
+	}
+}
+</script>
+
+<style scoped></style>
diff --git a/erpnext/public/js/hub/components/PublishPage.vue b/erpnext/public/js/hub/components/PublishPage.vue
new file mode 100644
index 0000000..06241d6
--- /dev/null
+++ b/erpnext/public/js/hub/components/PublishPage.vue
@@ -0,0 +1,58 @@
+<template>
+	<div
+		class="marketplace-page"
+		:data-page-name="page_name"
+	>
+		<item-cards-container
+			:items="valid_items"
+			:is_local="1"
+		>
+		</item-cards-container>
+	</div>
+</template>
+
+<script>
+import ItemCardsContainer from './ItemCardsContainer.vue';
+
+export default {
+	name: 'publish-page',
+	data() {
+		return {
+			page_name: frappe.get_route()[1],
+			valid_items: [],
+			search_value: ''
+		};
+	},
+	components: {
+		ItemCardsContainer
+	},
+	// watch: {
+	// 	// whenever search term changes, this function will run
+	// 	question: function (newQuestion, oldQuestion) {
+	// 		this.answer = 'Waiting for you to stop typing...'
+	// 		this.debouncedGetAnswer()
+	// 	}
+	// },
+	created() {
+		this.get_valid_items();
+	},
+	methods: {
+		get_valid_items() {
+			var vm = this;
+
+			frappe.call(
+				'erpnext.hub_node.api.get_valid_items',
+				{
+					search_value: this.search_value
+				}
+			)
+			.then(function (r) {
+				vm.valid_items = r.message;
+				frappe.dom.handle_broken_images(this.$wrapper);
+			})
+		}
+	}
+}
+</script>
+
+<style scoped></style>
diff --git a/erpnext/public/js/hub/components/publishing_area.js b/erpnext/public/js/hub/components/publishing_area.js
index 4775c3b..c19bf6b 100644
--- a/erpnext/public/js/hub/components/publishing_area.js
+++ b/erpnext/public/js/hub/components/publishing_area.js
@@ -17,6 +17,7 @@
                 ${title_html}
                 ${publish_button_html}
             </div>
+            <div id="vue-area"></div>
             <div class="empty-items-container flex align-center flex-column justify-center">
                 <p class="text-muted">${__('No Items Selected')}</p>
             </div>
diff --git a/erpnext/public/js/hub/pages/publish.js b/erpnext/public/js/hub/pages/publish.js
index f493658..d94a755 100644
--- a/erpnext/public/js/hub/pages/publish.js
+++ b/erpnext/public/js/hub/pages/publish.js
@@ -4,29 +4,35 @@
 import { make_search_bar } from '../components/search_bar';
 import { get_publishing_header } from '../components/publishing_area';
 import { ItemPublishDialog } from '../components/item_publish_dialog';
+import PublishPage from '../components/PublishPage.vue';
 
-erpnext.hub.Publish = class Publish extends SubPage {
-	make_wrapper() {
-		super.make_wrapper();
+erpnext.hub.Publish = class Publish {
+	constructor(parent) {
 		this.items_data_to_publish = {};
 		this.unpublished_items = [];
 		this.fetched_items = [];
 		this.fetched_items_dict = {};
 
-		frappe.realtime.on("items-sync", (data) => {
-			this.$wrapper.find('.progress-bar').css('width', data.progress_percent+'%');
+		this.$wrapper = $(`<div id="vue-area">`).appendTo($(parent));
 
-			if(data.progress_percent === 100 || data.progress_percent === '100') {
-				setTimeout(() => {
-					hub.settings.sync_in_progress = 0;
-					frappe.db.get_doc('Hub Settings')
-						.then(doc => {
-							hub.settings = doc;
-							this.refresh();
-						});
-				}, 500);
-			}
-		});
+		frappe.app = new Vue({
+			render: h => h(PublishPage),
+			mounted() {
+				console.log('Mounted For Publish page');
+			},
+		}).$mount('#vue-area');
+	}
+
+	show() {
+		this.$wrapper.show();
+	}
+
+	hide() {
+		this.$wrapper.hide();
+	}
+
+	show_message(message) {
+		this.$wrapper.prepend(NotificationMessage(message));
 	}
 
 	refresh() {
diff --git a/erpnext/public/js/hub/pages/subpage.js b/erpnext/public/js/hub/pages/subpage.js
index c833446..cded179 100644
--- a/erpnext/public/js/hub/pages/subpage.js
+++ b/erpnext/public/js/hub/pages/subpage.js
@@ -28,7 +28,10 @@
 
 	make_wrapper() {
 		const page_name = frappe.get_route()[1];
-		this.$wrapper = $(`<div class="marketplace-page" data-page-name="${page_name}">`).appendTo(this.$parent);
+		this.$wrapper = $(`<div class="marketplace-page"
+			data-page-name="${page_name}">`
+		).appendTo(this.$parent);
+
 		this.hide();
 	}