[hub] hub page using BaseList
diff --git a/erpnext/config/desktop.py b/erpnext/config/desktop.py
index 18bb36b..7556083 100644
--- a/erpnext/config/desktop.py
+++ b/erpnext/config/desktop.py
@@ -336,7 +336,7 @@
 			"color": "#009248",
 			"icon": "/assets/erpnext/images/hub_logo.svg",
 			"type": "page",
-			"link": "hub",
+			"link": "Hub/Home",
 			"label": _("Hub")
 		},
 		{
diff --git a/erpnext/hub_node/__init__.py b/erpnext/hub_node/__init__.py
index 8eb1074..31f30f8 100644
--- a/erpnext/hub_node/__init__.py
+++ b/erpnext/hub_node/__init__.py
@@ -17,9 +17,11 @@
 @frappe.whitelist()
 def get_items(start=0, limit=20, category=None, order_by=None, company=None, text=None):
 	connection = get_client_connection()
-	filters = {
-		'hub_category': category,
-	}
+
+	filters = {}
+
+	if category:
+		filters.update({ 'hub_category': category })
 	if text:
 		filters.update({'item_name': ('like', '%' + text + '%')})
 	if company:
diff --git a/erpnext/public/build.json b/erpnext/public/build.json
index 0dd3ed7..21cd289 100644
--- a/erpnext/public/build.json
+++ b/erpnext/public/build.json
@@ -36,7 +36,8 @@
         "public/js/utils/item_quick_entry.js",
 	"public/js/utils/customer_quick_entry.js",
         "public/js/education/student_button.html",
-        "public/js/education/assessment_result_tool.html"
+        "public/js/education/assessment_result_tool.html",
+        "public/js/hub/hub_factory.js"
     ],
     "js/item-dashboard.min.js": [
         "stock/dashboard/item_dashboard.html",
diff --git a/erpnext/public/js/hub/hub_factory.js b/erpnext/public/js/hub/hub_factory.js
new file mode 100644
index 0000000..f260fcc
--- /dev/null
+++ b/erpnext/public/js/hub/hub_factory.js
@@ -0,0 +1,32 @@
+frappe.provide('erpnext.hub.pages');
+
+frappe.views.HubFactory = frappe.views.Factory.extend({
+	make(route) {
+		const page_name = frappe.get_route_str();
+		const page = route[1];
+
+		if (!erpnext.hub.pages[page_name]) {
+			if (page === 'Item' && !route[2]) {
+				frappe.require('/assets/erpnext/js/hub/hub_page.js', () => {
+					erpnext.hub.pages[page_name] = new erpnext.hub.HubPage({
+						doctype: 'Hub Settings',
+						parent: this.make_page(true, page_name)
+					});
+					window.hub_page = erpnext.hub.pages[page_name];
+				});
+			} else if(route[2]) {
+				frappe.require('/assets/erpnext/js/hub/hub_form.js', () => {
+					erpnext.hub.pages[page_name] = new erpnext.hub.HubForm({
+						hub_item_code: route[2],
+						doctype: 'Hub Settings',
+						parent: this.make_page(true, page_name)
+					});
+					window.hub_page = erpnext.hub.pages[page_name];
+				});
+			}
+		} else {
+			frappe.container.change_to(page_name);
+			window.hub_page = erpnext.hub.pages[page_name];
+		}
+	}
+});
diff --git a/erpnext/public/js/hub/hub_form.js b/erpnext/public/js/hub/hub_form.js
new file mode 100644
index 0000000..0a8b5b0
--- /dev/null
+++ b/erpnext/public/js/hub/hub_form.js
@@ -0,0 +1,80 @@
+frappe.provide('erpnext.hub');
+
+erpnext.hub.HubForm = class HubForm extends frappe.views.BaseList {
+	setup_defaults() {
+		super.setup_defaults();
+		this.page_title = this.data.item_name || this.hub_item_code || __('Hub Item');
+		this.method = 'erpnext.hub_node.get_item_details';
+	}
+
+	setup_fields() {
+
+	}
+
+	set_breadcrumbs() {
+		frappe.breadcrumbs.add({
+			label: __('Hub'),
+			route: '#Hub/Item',
+			type: 'Custom'
+		});
+	}
+
+	setup_side_bar() {
+		this.sidebar = new frappe.ui.Sidebar({
+			wrapper: this.$page.find('.layout-side-section'),
+			css_class: 'hub-form-sidebar'
+		});
+	}
+
+	setup_filter_area() {
+
+	}
+
+	setup_sort_selector() {
+
+	}
+
+	get_args() {
+		return {
+			hub_sync_id: this.hub_item_code
+		};
+	}
+
+	update_data(r) {
+		const data = r.message;
+		this.data = data;
+	}
+
+	render() {
+		this.sidebar.add_item({
+			label: `<img src="${this.data.image}" />`
+		});
+
+		let fields = [];
+		for (let fieldname in this.data) {
+			fields.push({
+				label: toTitle(fieldname),
+				fieldname,
+				fieldtype: 'Data'
+			});
+		}
+
+		this.form = new frappe.ui.FieldGroup({
+			parent: this.$result,
+			fields
+		});
+
+		this.form.make();
+		this.form.set_values(this.data);
+	}
+
+	toggle_result_area() {
+		this.$result.toggle(this.data.hub_item_code);
+		this.$paging_area.toggle(this.data.length > 0);
+		this.$no_result.toggle(this.data.length == 0);
+
+		const show_more = (this.start + this.page_length) <= this.data.length;
+		this.$paging_area.find('.btn-more')
+			.toggle(show_more);
+	}
+};
diff --git a/erpnext/public/js/hub/hub_page.js b/erpnext/public/js/hub/hub_page.js
new file mode 100644
index 0000000..8ecd28b
--- /dev/null
+++ b/erpnext/public/js/hub/hub_page.js
@@ -0,0 +1,108 @@
+frappe.provide('erpnext.hub');
+
+erpnext.hub.HubPage = class HubPage extends frappe.views.BaseList {
+	setup_defaults() {
+		super.setup_defaults();
+		this.page_title = __('Hub');
+		this.method = 'erpnext.hub_node.get_items';
+
+		const route = frappe.get_route();
+		this.page_name = route[1];
+	}
+
+	setup_fields() {
+
+	}
+
+	set_breadcrumbs() {
+
+	}
+
+	setup_side_bar() {
+
+	}
+
+	setup_filter_area() {
+
+	}
+
+	setup_sort_selector() {
+
+	}
+
+	get_args() {
+		return {
+			start: this.start,
+			limit: this.page_length,
+			category: this.category || '',
+			order_by: this.order_by,
+			company: this.company || '',
+			text: this.search_text || ''
+		};
+	}
+
+	update_data(r) {
+		const data = r.message;
+
+		if (this.start === 0) {
+			this.data = data;
+		} else {
+			this.data = this.data.concat(data);
+		}
+	}
+
+	render() {
+		this.render_image_view();
+	}
+
+	render_image_view() {
+		var html = this.data.map(this.card_html.bind(this)).join("");
+
+		this.$result.html(`
+			<div class="image-view-container small">
+				${html}
+			</div>
+		`);
+	}
+
+	card_html(item) {
+		item._name = encodeURI(item.name);
+		const encoded_name = item._name;
+		const title = strip_html(item['item_name' || 'item_code']);
+
+		const _class = !item.image ? 'no-image' : '';
+		const _html = item.image ?
+			`<img data-name="${encoded_name}" src="${ item.image }" alt="${ title }">` :
+			`<span class="placeholder-text">
+				${ frappe.get_abbr(title) }
+			</span>`;
+
+		return `
+			<div class="image-view-item">
+				<div class="image-view-header">
+					<div class="list-row-col list-subject ellipsis level">
+						<div class="list-row-col">
+							<span>${title}</span>
+						</div>
+					</div>
+				</div>
+				<div class="image-view-body">
+					<a  data-name="${encoded_name}"
+						title="${encoded_name}"
+						href="#Hub/Item/${item.hub_item_code}"
+					>
+						<div class="image-field ${_class}"
+							data-name="${encoded_name}"
+						>
+							${_html}
+						</div>
+					</a>
+				</div>
+			</div>
+		`;
+	}
+
+	show_hub_form() {
+
+	}
+};