[hub] Register components, init Item Page

- commonly used components are pre-registered
- add DetailHeaderItem component
- begin ItemPage
This commit is contained in:
Prateeksha Singh 2018-08-27 10:12:45 +05:30
parent ea82fb9f80
commit d0a952bcda
17 changed files with 235 additions and 248 deletions

View File

@ -3,25 +3,37 @@
<component :is="current_page"></component>
</div>
</template>
<script>
import Home from './pages/Home.vue';
import SavedProducts from './pages/SavedProducts.vue';
import Publish from './pages/Publish.vue';
import Category from './pages/Category.vue';
import Search from './pages/Search.vue';
import Category from './pages/Category.vue';
import SavedProducts from './pages/SavedProducts.vue';
import PublishedProducts from './pages/PublishedProducts.vue';
import Item from './pages/Item.vue';
import Seller from './pages/Seller.vue';
import Publish from './pages/Publish.vue';
import Buying from './pages/Buying.vue';
import BuyingMessages from './pages/BuyingMessages.vue';
import Profile from './pages/Profile.vue';
import NotFound from './pages/NotFound.vue';
const route_map = {
'marketplace/home': Home,
'marketplace/saved-products': SavedProducts,
'marketplace/my-products': PublishedProducts,
'marketplace/publish': Publish,
'marketplace/category/:category': Category,
'marketplace/search/:keyword': Search,
'marketplace/category/:category': Category,
'marketplace/item/:item': Item,
'marketplace/seller/:seller': Seller,
'marketplace/not-found': NotFound,
// Registered seller routes
'marketplace/profile': Profile,
'marketplace/saved-products': SavedProducts,
'marketplace/publish': Publish,
'marketplace/my-products': PublishedProducts,
'marketplace/buying': Buying,
'marketplace/buying/:item': BuyingMessages
'marketplace/buying/:item': BuyingMessages,
}
export default {

View File

@ -0,0 +1,20 @@
<template>
<p class="text-muted" v-html="header_item"></p>
</template>
<script>
const spacer = '<span aria-hidden="true"> · </span>';
export default {
name: 'header-item',
props: ['value'],
data() {
return {
header_item: Array.isArray(this.value)
? this.value.join(spacer)
: this.value
}
},
}
</script>

View File

@ -31,16 +31,31 @@
<img :src="image">
</div>
</div>
<div class="col-md-6">
<div class="col-md-8">
<h2>{{ title }}</h2>
<div class="text-muted">
<p v-for="subtitle in subtitles"
:key="subtitle"
v-html="subtitle"
>
</p>
<slot name="subtitle"></slot>
</div>
<button v-if="primary_action" class="btn btn-primary" @click="primary_action.action">
{{ primary_action.label }}
</button>
</div>
<div v-if="menu_items" 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 v-for="menu_item in menu_items"
v-if="menu_item.condition"
:key="menu_item.label"
>
<a @click="menu_item.action">{{ menu_item.label }}</a>
</li>
</ul>
</div>
</div>
</div>
<div v-for="section in sections" class="row hub-item-description margin-bottom"
@ -61,7 +76,7 @@
export default {
name: 'detail-view',
props: ['title', 'subtitles', 'image', 'sections', 'show_skeleton'],
props: ['title', 'subtitles', 'image', 'sections', 'show_skeleton', 'menu_items', 'primary_action'],
data() {
return {
back_to_home_text: __('Back to Home')

View File

@ -2,20 +2,10 @@ import Vue from 'vue/dist/vue.js';
import './vue-plugins';
// pages
import './pages/item';
import './pages/messages';
import './pages/buying_messages';
import PageContainer from './PageContainer.vue';
import Home from './pages/Home.vue';
import SavedProducts from './pages/SavedProducts.vue';
import Publish from './pages/Publish.vue';
import Category from './pages/Category.vue';
import Search from './pages/Search.vue';
import PublishedProducts from './pages/PublishedProducts.vue';
import Profile from './pages/Profile.vue';
import Seller from './pages/Seller.vue';
import NotFound from './pages/NotFound.vue';
// components
import { ProfileDialog } from './components/profile_dialog';
@ -199,7 +189,7 @@ erpnext.hub.Marketplace = class Marketplace {
}
if (route[1] === 'item' && route[2] && !this.subpages.item) {
this.subpages.item = new erpnext.hub.Item(this.$body);
this.subpages.item = new erpnext.hub.ItemPage(this.$body);
}
if (route[1] === 'seller' && !this.subpages['seller']) {
@ -282,167 +272,3 @@ erpnext.hub.Marketplace = class Marketplace {
});
}
}
erpnext.hub.HomePage = class {
constructor(parent) {
this.$wrapper = $(`<div id="vue-area-home">`).appendTo($(parent));
new Vue({
render: h => h(Home)
}).$mount('#vue-area-home');
}
show() {
$('[data-page-name="home"]').show();
}
hide() {
$('[data-page-name="home"]').hide();
}
}
erpnext.hub.SavedProductsPage = class {
constructor(parent) {
this.$wrapper = $(`<div id="vue-area-saved">`).appendTo($(parent));
new Vue({
render: h => h(SavedProducts)
}).$mount('#vue-area-saved');
}
show() {
$('[data-page-name="saved-products"]').show();
}
hide() {
$('[data-page-name="saved-products"]').hide();
}
}
erpnext.hub.PublishPage = class {
constructor(parent) {
this.$wrapper = $(`<div id="vue-area">`).appendTo($(parent));
new Vue({
render: h => h(Publish)
}).$mount('#vue-area');
}
show() {
$('[data-page-name="publish"]').show();
}
hide() {
$('[data-page-name="publish"]').hide();
}
}
erpnext.hub.CategoryPage = class {
constructor(parent) {
this.$wrapper = $(`<div id="vue-area-category">`).appendTo($(parent));
new Vue({
render: h => h(Category)
}).$mount('#vue-area-category');
}
show() {
$('[data-page-name="category"]').show();
}
hide() {
$('[data-page-name="category"]').hide();
}
}
erpnext.hub.PublishedProductsPage = class {
constructor(parent) {
this.$wrapper = $(`<div id="vue-area-published-products">`).appendTo($(parent));
new Vue({
render: h => h(PublishedProducts)
}).$mount('#vue-area-published-products');
}
show() {
$('[data-page-name="published-products"]').show();
}
hide() {
$('[data-page-name="published-products"]').hide();
}
}
erpnext.hub.SearchPage = class {
constructor(parent) {
this.$wrapper = $(`<div id="vue-area-search">`).appendTo($(parent));
new Vue({
render: h => h(Search)
}).$mount('#vue-area-search');
}
show() {
$('[data-page-name="search"]').show();
}
hide() {
$('[data-page-name="search"]').hide();
}
}
erpnext.hub.ProfilePage = class {
constructor(parent) {
this.$wrapper = $(`<div id="vue-area-profile">`).appendTo($(parent));
new Vue({
render: h => h(Profile)
}).$mount('#vue-area-profile');
}
show() {
$('[data-page-name="profile"]').show();
}
hide() {
$('[data-page-name="profile"]').hide();
}
}
erpnext.hub.SellerPage = class {
constructor(parent) {
this.$wrapper = $(`<div id="vue-area-seller">`).appendTo($(parent));
new Vue({
render: h => h(Seller)
}).$mount('#vue-area-seller');
}
show() {
$('[data-page-name="seller"]').show();
}
hide() {
$('[data-page-name="seller"]').hide();
}
}
erpnext.hub.NotFoundPage = class {
constructor(parent) {
this.$wrapper = $(`<div id="vue-area-not-found">`).appendTo($(parent));
new Vue({
render: h => h(NotFound)
}).$mount('#vue-area-not-found');
}
show() {
$('[data-page-name="not-found"]').show();
}
hide() {
$('[data-page-name="not-found"]').hide();
}
}

View File

@ -17,13 +17,8 @@
</template>
<script>
import ItemCardsContainer from '../components/ItemCardsContainer.vue';
export default {
name: 'saved-products-page',
components: {
ItemCardsContainer
},
data() {
return {
page_name: frappe.get_route()[1],

View File

@ -27,17 +27,8 @@
</template>
<script>
import SearchInput from '../components/SearchInput.vue';
import SectionHeader from '../components/SectionHeader.vue';
import ItemCardsContainer from '../components/ItemCardsContainer.vue';
export default {
name: 'home-page',
components: {
SectionHeader,
SearchInput,
ItemCardsContainer
},
data() {
return {
page_name: frappe.get_route()[1],

View File

@ -0,0 +1,150 @@
<template>
<div
class="marketplace-page"
:data-page-name="page_name"
v-if="init || item"
>
<detail-view
:title="title"
:subtitles="subtitles"
:image="image"
:sections="sections"
:menu_items="menu_items"
:show_skeleton="init"
>
<detail-header-item slot="subtitle"
:value="item_subtitle"
></detail-header-item>
<detail-header-item slot="subtitle"
:value="item_views_and_ratings"
></detail-header-item>
</detail-view>
</div>
</template>
<script>
import { get_rating_html } from '../components/reviews';
export default {
name: 'item-page',
data() {
return {
page_name: frappe.get_route()[1],
hub_item_code: frappe.get_route()[2],
init: true,
item: null,
title: null,
subtitles: [],
image: null,
sections: [],
menu_items: [
{
label: __('Report this Product'),
condition: !this.is_own_item,
action: this.report_item
},
{
label: __('Edit Details'),
condition: this.is_own_item,
action: this.report_item
},
{
label: __('Unpublish Product'),
condition: this.is_own_item,
action: this.report_item
}
]
};
},
computed: {
is_own_item() {
let is_own_item = false;
if(this.item) {
if(this.item.hub_seller === hub.setting.company_email) {
is_own_item = true;
}
}
return is_own_item;
},
item_subtitle() {
if(!this.item) {
return '';
}
const dot_spacer = '<span aria-hidden="true"> · </span>';
let subtitle_items = [comment_when(this.item.creation)];
const rating = this.item.average_rating;
if (rating > 0) {
subtitle_items.push(rating + `<i class='fa fa-fw fa-star-o'></i>`)
}
subtitle_items.push(this.item.company);
return subtitle_items;
},
item_views_and_ratings() {
if(!this.item) {
return '';
}
let stats = __('No views yet');
if(this.item.view_count) {
const views_message = __(`${this.item.view_count} Views`);
const rating_html = get_rating_html(this.item.average_rating);
const rating_count = this.item.no_of_ratings > 0 ? `${this.item.no_of_ratings} reviews` : __('No reviews yet');
stats = [views_message, rating_html, rating_count];
}
return stats;
}
},
created() {
this.get_profile();
},
methods: {
get_profile() {
hub.call('get_item_details',{ hub_item_code: this.hub_item_code })
.then(item => {
this.init = false;
this.item = item;
this.build_data();
});
},
build_data() {
this.title = this.item.item_name || this.item.name;
this.image = this.item.image;
this.sections = [
{
title: __('Product Description'),
content: this.item.description
? __(this.item.description)
: __('No description')
},
{
title: __('Seller Information'),
content: ''
}
];
},
report_item(){
//
}
}
}
</script>
<style scoped></style>

View File

@ -14,13 +14,8 @@
</template>
<script>
import EmptyState from '../components/EmptyState.vue';
export default {
name: 'not-found-page',
components: {
EmptyState
},
data() {
return {
page_name: 'not-found',

View File

@ -17,13 +17,8 @@
</template>
<script>
import DetailView from '../components/DetailView.vue';
export default {
name: 'profile-page',
components: {
DetailView
},
data() {
return {
page_name: frappe.get_route()[1],

View File

@ -54,13 +54,14 @@
</template>
<script>
import SearchInput from '../components/SearchInput.vue';
import ItemCardsContainer from '../components/ItemCardsContainer.vue';
import NotificationMessage from '../components/NotificationMessage.vue';
import { ItemPublishDialog } from '../components/item_publish_dialog';
export default {
name: 'publish-page',
components: {
NotificationMessage
},
data() {
return {
page_name: frappe.get_route()[1],
@ -87,11 +88,6 @@ export default {
: ''
};
},
components: {
SearchInput,
ItemCardsContainer,
NotificationMessage
},
computed: {
no_selected_items() {
return this.selected_items.length === 0;

View File

@ -17,13 +17,8 @@
</template>
<script>
import ItemCardsContainer from '../components/ItemCardsContainer.vue';
export default {
name: 'saved-products-page',
components: {
ItemCardsContainer
},
data() {
return {
page_name: frappe.get_route()[1],

View File

@ -19,8 +19,6 @@
</template>
<script>
import ItemCardsContainer from '../components/ItemCardsContainer.vue';
export default {
name: 'saved-products-page',
data() {
@ -34,9 +32,6 @@ export default {
empty_state_message: __(`You haven't favourited any items yet.`)
};
},
components: {
ItemCardsContainer
},
created() {
this.get_items();
},

View File

@ -24,15 +24,8 @@
</template>
<script>
import SearchInput from '../components/SearchInput.vue';
import ItemCardsContainer from '../components/ItemCardsContainer.vue';
export default {
name: 'saved-products-page',
components: {
SearchInput,
ItemCardsContainer
},
data() {
return {
page_name: frappe.get_route()[1],

View File

@ -25,15 +25,8 @@
</template>
<script>
import DetailView from '../components/DetailView.vue';
import ItemCardsContainer from '../components/ItemCardsContainer.vue';
export default {
name: 'seller-page',
components: {
DetailView,
ItemCardsContainer
},
data() {
return {
page_name: frappe.get_route()[1],

View File

@ -2,7 +2,7 @@ import SubPage from './subpage';
// import { get_item_card_container_html } from '../components/items_container';
import { get_buying_item_message_card_html } from '../components/item_card';
import { get_selling_item_message_card_html } from '../components/item_card';
import { get_empty_state } from '../components/empty_state';
// import { get_empty_state } from '../components/empty_state';
erpnext.hub.Buying = class Buying extends SubPage {
refresh() {
@ -27,8 +27,8 @@ erpnext.hub.Buying = class Buying extends SubPage {
}
render_empty_state() {
const empty_state = get_empty_state(__('You haven\'t interacted with any seller yet.'));
this.$wrapper.html(empty_state);
// const empty_state = get_empty_state(__('You haven\'t interacted with any seller yet.'));
// this.$wrapper.html(empty_state);
}
get_items_for_messages() {

View File

@ -1,7 +1,23 @@
import Vue from 'vue/dist/vue.js';
// Global components
import ItemCardsContainer from './components/ItemCardsContainer.vue';
import SectionHeader from './components/SectionHeader.vue';
import SearchInput from './components/SearchInput.vue';
import DetailView from './components/DetailView.vue';
import DetailHeaderItem from './components/DetailHeaderItem.vue';
import EmptyState from './components/EmptyState.vue';
Vue.prototype.__ = window.__;
Vue.prototype.frappe = window.frappe;
Vue.component('item-cards-container', ItemCardsContainer);
Vue.component('section-header', SectionHeader);
Vue.component('search-input', SearchInput);
Vue.component('detail-view', DetailView);
Vue.component('detail-header-item', DetailHeaderItem);
Vue.component('empty-state', EmptyState);
Vue.directive('route', {
bind(el, binding) {
const route = binding.value;