Ankush Menat
3 years ago
committed by
GitHub
79 changed files with 17 additions and 5090 deletions
@ -1,19 +0,0 @@ |
|||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and contributors |
|
||||
# For license information, please see license.txt |
|
||||
|
|
||||
from __future__ import unicode_literals |
|
||||
|
|
||||
import frappe |
|
||||
|
|
||||
|
|
||||
@frappe.whitelist() |
|
||||
def enable_hub(): |
|
||||
hub_settings = frappe.get_doc('Marketplace Settings') |
|
||||
hub_settings.register() |
|
||||
frappe.db.commit() |
|
||||
return hub_settings |
|
||||
|
|
||||
@frappe.whitelist() |
|
||||
def sync(): |
|
||||
hub_settings = frappe.get_doc('Marketplace Settings') |
|
||||
hub_settings.sync() |
|
@ -1,233 +0,0 @@ |
|||||
from __future__ import unicode_literals |
|
||||
|
|
||||
import json |
|
||||
|
|
||||
import frappe |
|
||||
from frappe import _ |
|
||||
from frappe.desk.form.load import get_attachments |
|
||||
from frappe.frappeclient import FrappeClient |
|
||||
from six import string_types |
|
||||
|
|
||||
current_user = frappe.session.user |
|
||||
|
|
||||
|
|
||||
@frappe.whitelist() |
|
||||
def register_marketplace(company, company_description): |
|
||||
validate_registerer() |
|
||||
|
|
||||
settings = frappe.get_single('Marketplace Settings') |
|
||||
message = settings.register_seller(company, company_description) |
|
||||
|
|
||||
if message.get('hub_seller_name'): |
|
||||
settings.registered = 1 |
|
||||
settings.hub_seller_name = message.get('hub_seller_name') |
|
||||
settings.save() |
|
||||
|
|
||||
settings.add_hub_user(frappe.session.user) |
|
||||
|
|
||||
return { 'ok': 1 } |
|
||||
|
|
||||
|
|
||||
@frappe.whitelist() |
|
||||
def register_users(user_list): |
|
||||
user_list = json.loads(user_list) |
|
||||
|
|
||||
settings = frappe.get_single('Marketplace Settings') |
|
||||
|
|
||||
for user in user_list: |
|
||||
settings.add_hub_user(user) |
|
||||
|
|
||||
return user_list |
|
||||
|
|
||||
|
|
||||
def validate_registerer(): |
|
||||
if current_user == 'Administrator': |
|
||||
frappe.throw(_('Please login as another user to register on Marketplace')) |
|
||||
|
|
||||
valid_roles = ['System Manager', 'Item Manager'] |
|
||||
|
|
||||
if not frappe.utils.is_subset(valid_roles, frappe.get_roles()): |
|
||||
frappe.throw(_('Only users with {0} role can register on Marketplace').format(', '.join(valid_roles)), |
|
||||
frappe.PermissionError) |
|
||||
|
|
||||
|
|
||||
@frappe.whitelist() |
|
||||
def call_hub_method(method, params=None): |
|
||||
connection = get_hub_connection() |
|
||||
|
|
||||
if isinstance(params, string_types): |
|
||||
params = json.loads(params) |
|
||||
|
|
||||
params.update({ |
|
||||
'cmd': 'hub.hub.api.' + method |
|
||||
}) |
|
||||
|
|
||||
response = connection.post_request(params) |
|
||||
return response |
|
||||
|
|
||||
|
|
||||
def map_fields(items): |
|
||||
field_mappings = get_field_mappings() |
|
||||
table_fields = [d.fieldname for d in frappe.get_meta('Item').get_table_fields()] |
|
||||
|
|
||||
hub_seller_name = frappe.db.get_value('Marketplace Settings', 'Marketplace Settings', 'hub_seller_name') |
|
||||
|
|
||||
for item in items: |
|
||||
for fieldname in table_fields: |
|
||||
item.pop(fieldname, None) |
|
||||
|
|
||||
for mapping in field_mappings: |
|
||||
local_fieldname = mapping.get('local_fieldname') |
|
||||
remote_fieldname = mapping.get('remote_fieldname') |
|
||||
|
|
||||
value = item.get(local_fieldname) |
|
||||
item.pop(local_fieldname, None) |
|
||||
item[remote_fieldname] = value |
|
||||
|
|
||||
item['doctype'] = 'Hub Item' |
|
||||
item['hub_seller'] = hub_seller_name |
|
||||
item.pop('attachments', None) |
|
||||
|
|
||||
return items |
|
||||
|
|
||||
|
|
||||
@frappe.whitelist() |
|
||||
def get_valid_items(search_value=''): |
|
||||
items = frappe.get_list( |
|
||||
'Item', |
|
||||
fields=["*"], |
|
||||
filters={ |
|
||||
'disabled': 0, |
|
||||
'item_name': ['like', '%' + search_value + '%'], |
|
||||
'publish_in_hub': 0 |
|
||||
}, |
|
||||
order_by="modified desc" |
|
||||
) |
|
||||
|
|
||||
valid_items = filter(lambda x: x.image and x.description, items) |
|
||||
|
|
||||
def prepare_item(item): |
|
||||
item.source_type = "local" |
|
||||
item.attachments = get_attachments('Item', item.item_code) |
|
||||
return item |
|
||||
|
|
||||
valid_items = map(prepare_item, valid_items) |
|
||||
|
|
||||
return valid_items |
|
||||
|
|
||||
@frappe.whitelist() |
|
||||
def update_item(ref_doc, data): |
|
||||
data = json.loads(data) |
|
||||
|
|
||||
data.update(dict(doctype='Hub Item', name=ref_doc)) |
|
||||
try: |
|
||||
connection = get_hub_connection() |
|
||||
connection.update(data) |
|
||||
except Exception as e: |
|
||||
frappe.log_error(message=e, title='Hub Sync Error') |
|
||||
|
|
||||
@frappe.whitelist() |
|
||||
def publish_selected_items(items_to_publish): |
|
||||
items_to_publish = json.loads(items_to_publish) |
|
||||
items_to_update = [] |
|
||||
if not len(items_to_publish): |
|
||||
frappe.throw(_('No items to publish')) |
|
||||
|
|
||||
for item in items_to_publish: |
|
||||
item_code = item.get('item_code') |
|
||||
frappe.db.set_value('Item', item_code, 'publish_in_hub', 1) |
|
||||
|
|
||||
hub_dict = { |
|
||||
'doctype': 'Hub Tracked Item', |
|
||||
'item_code': item_code, |
|
||||
'published': 1, |
|
||||
'hub_category': item.get('hub_category'), |
|
||||
'image_list': item.get('image_list') |
|
||||
} |
|
||||
frappe.get_doc(hub_dict).insert(ignore_if_duplicate=True) |
|
||||
|
|
||||
items = map_fields(items_to_publish) |
|
||||
|
|
||||
try: |
|
||||
item_sync_preprocess(len(items)) |
|
||||
convert_relative_image_urls_to_absolute(items) |
|
||||
|
|
||||
# TODO: Publish Progress |
|
||||
connection = get_hub_connection() |
|
||||
connection.insert_many(items) |
|
||||
|
|
||||
item_sync_postprocess() |
|
||||
except Exception as e: |
|
||||
frappe.log_error(message=e, title='Hub Sync Error') |
|
||||
|
|
||||
@frappe.whitelist() |
|
||||
def unpublish_item(item_code, hub_item_name): |
|
||||
''' Remove item listing from the marketplace ''' |
|
||||
|
|
||||
response = call_hub_method('unpublish_item', { |
|
||||
'hub_item_name': hub_item_name |
|
||||
}) |
|
||||
|
|
||||
if response: |
|
||||
frappe.db.set_value('Item', item_code, 'publish_in_hub', 0) |
|
||||
frappe.delete_doc('Hub Tracked Item', item_code) |
|
||||
else: |
|
||||
frappe.throw(_('Unable to update remote activity')) |
|
||||
|
|
||||
@frappe.whitelist() |
|
||||
def get_unregistered_users(): |
|
||||
settings = frappe.get_single('Marketplace Settings') |
|
||||
registered_users = [user.user for user in settings.users] + ['Administrator', 'Guest'] |
|
||||
all_users = [user.name for user in frappe.db.get_all('User', filters={'enabled': 1})] |
|
||||
unregistered_users = [user for user in all_users if user not in registered_users] |
|
||||
return unregistered_users |
|
||||
|
|
||||
|
|
||||
def item_sync_preprocess(intended_item_publish_count): |
|
||||
response = call_hub_method('pre_items_publish', { |
|
||||
'intended_item_publish_count': intended_item_publish_count |
|
||||
}) |
|
||||
|
|
||||
if response: |
|
||||
frappe.db.set_value("Marketplace Settings", "Marketplace Settings", "sync_in_progress", 1) |
|
||||
return response |
|
||||
else: |
|
||||
frappe.throw(_('Unable to update remote activity')) |
|
||||
|
|
||||
|
|
||||
def item_sync_postprocess(): |
|
||||
response = call_hub_method('post_items_publish', {}) |
|
||||
if response: |
|
||||
frappe.db.set_value('Marketplace Settings', 'Marketplace Settings', 'last_sync_datetime', frappe.utils.now()) |
|
||||
else: |
|
||||
frappe.throw(_('Unable to update remote activity')) |
|
||||
|
|
||||
frappe.db.set_value('Marketplace Settings', 'Marketplace Settings', 'sync_in_progress', 0) |
|
||||
|
|
||||
|
|
||||
def convert_relative_image_urls_to_absolute(items): |
|
||||
from six.moves.urllib.parse import urljoin |
|
||||
|
|
||||
for item in items: |
|
||||
file_path = item['image'] |
|
||||
|
|
||||
if file_path.startswith('/files/'): |
|
||||
item['image'] = urljoin(frappe.utils.get_url(), file_path) |
|
||||
|
|
||||
|
|
||||
def get_hub_connection(): |
|
||||
settings = frappe.get_single('Marketplace Settings') |
|
||||
marketplace_url = settings.marketplace_url |
|
||||
hub_user = settings.get_hub_user(frappe.session.user) |
|
||||
|
|
||||
if hub_user: |
|
||||
password = hub_user.get_password() |
|
||||
hub_connection = FrappeClient(marketplace_url, hub_user.user, password) |
|
||||
return hub_connection |
|
||||
else: |
|
||||
read_only_hub_connection = FrappeClient(marketplace_url) |
|
||||
return read_only_hub_connection |
|
||||
|
|
||||
|
|
||||
def get_field_mappings(): |
|
||||
return [] |
|
@ -1,50 +0,0 @@ |
|||||
{ |
|
||||
"condition": "{'name': ('=', frappe.db.get_single_value('Hub Settings', 'company'))}", |
|
||||
"creation": "2017-09-07 11:38:43.169065", |
|
||||
"docstatus": 0, |
|
||||
"doctype": "Data Migration Mapping", |
|
||||
"fields": [ |
|
||||
{ |
|
||||
"is_child_table": 0, |
|
||||
"local_fieldname": "name", |
|
||||
"remote_fieldname": "company_name" |
|
||||
}, |
|
||||
{ |
|
||||
"is_child_table": 0, |
|
||||
"local_fieldname": "country", |
|
||||
"remote_fieldname": "country" |
|
||||
}, |
|
||||
{ |
|
||||
"is_child_table": 0, |
|
||||
"local_fieldname": "\"city\"", |
|
||||
"remote_fieldname": "seller_city" |
|
||||
}, |
|
||||
{ |
|
||||
"is_child_table": 0, |
|
||||
"local_fieldname": "eval:frappe.local.site", |
|
||||
"remote_fieldname": "site_name" |
|
||||
}, |
|
||||
{ |
|
||||
"is_child_table": 0, |
|
||||
"local_fieldname": "eval:frappe.session.user", |
|
||||
"remote_fieldname": "user" |
|
||||
}, |
|
||||
{ |
|
||||
"is_child_table": 0, |
|
||||
"local_fieldname": "company_logo", |
|
||||
"remote_fieldname": "company_logo" |
|
||||
} |
|
||||
], |
|
||||
"idx": 2, |
|
||||
"local_doctype": "Company", |
|
||||
"mapping_name": "Company to Hub Company", |
|
||||
"mapping_type": "Push", |
|
||||
"migration_id_field": "hub_sync_id", |
|
||||
"modified": "2020-09-18 17:26:09.703215", |
|
||||
"modified_by": "Administrator", |
|
||||
"name": "Company to Hub Company", |
|
||||
"owner": "Administrator", |
|
||||
"page_length": 10, |
|
||||
"remote_objectname": "Hub Company", |
|
||||
"remote_primary_key": "name" |
|
||||
} |
|
@ -1,31 +0,0 @@ |
|||||
{ |
|
||||
"condition": "{'reference_doctype': 'Lead', 'user': frappe.db.get_single_value('Hub Settings', 'user'), 'status': 'Pending'}", |
|
||||
"creation": "2017-09-20 15:06:40.279930", |
|
||||
"docstatus": 0, |
|
||||
"doctype": "Data Migration Mapping", |
|
||||
"fields": [ |
|
||||
{ |
|
||||
"is_child_table": 0, |
|
||||
"local_fieldname": "email_id", |
|
||||
"remote_fieldname": "email_id" |
|
||||
}, |
|
||||
{ |
|
||||
"is_child_table": 0, |
|
||||
"local_fieldname": "lead_name", |
|
||||
"remote_fieldname": "lead_name" |
|
||||
} |
|
||||
], |
|
||||
"idx": 0, |
|
||||
"local_doctype": "Lead", |
|
||||
"local_primary_key": "email_id", |
|
||||
"mapping_name": "Hub Message to Lead", |
|
||||
"mapping_type": "Pull", |
|
||||
"migration_id_field": "hub_sync_id", |
|
||||
"modified": "2020-09-18 17:26:09.703215", |
|
||||
"modified_by": "Administrator", |
|
||||
"name": "Hub Message to Lead", |
|
||||
"owner": "Administrator", |
|
||||
"page_length": 10, |
|
||||
"remote_objectname": "Hub Message", |
|
||||
"remote_primary_key": "name" |
|
||||
} |
|
@ -1,55 +0,0 @@ |
|||||
{ |
|
||||
"condition": "{\"publish_in_hub\": 1}", |
|
||||
"creation": "2017-09-07 13:27:52.726350", |
|
||||
"docstatus": 0, |
|
||||
"doctype": "Data Migration Mapping", |
|
||||
"fields": [ |
|
||||
{ |
|
||||
"is_child_table": 0, |
|
||||
"local_fieldname": "item_code", |
|
||||
"remote_fieldname": "item_code" |
|
||||
}, |
|
||||
{ |
|
||||
"is_child_table": 0, |
|
||||
"local_fieldname": "item_name", |
|
||||
"remote_fieldname": "item_name" |
|
||||
}, |
|
||||
{ |
|
||||
"is_child_table": 0, |
|
||||
"local_fieldname": "eval:frappe.db.get_value('Hub Settings' , 'Hub Settings', 'company_email')", |
|
||||
"remote_fieldname": "hub_seller" |
|
||||
}, |
|
||||
{ |
|
||||
"is_child_table": 0, |
|
||||
"local_fieldname": "image", |
|
||||
"remote_fieldname": "image" |
|
||||
}, |
|
||||
{ |
|
||||
"is_child_table": 0, |
|
||||
"local_fieldname": "image_list", |
|
||||
"remote_fieldname": "image_list" |
|
||||
}, |
|
||||
{ |
|
||||
"is_child_table": 0, |
|
||||
"local_fieldname": "item_group", |
|
||||
"remote_fieldname": "item_group" |
|
||||
}, |
|
||||
{ |
|
||||
"is_child_table": 0, |
|
||||
"local_fieldname": "hub_category", |
|
||||
"remote_fieldname": "hub_category" |
|
||||
} |
|
||||
], |
|
||||
"idx": 1, |
|
||||
"local_doctype": "Item", |
|
||||
"mapping_name": "Item to Hub Item", |
|
||||
"mapping_type": "Push", |
|
||||
"migration_id_field": "hub_sync_id", |
|
||||
"modified": "2018-08-19 22:20:25.727581", |
|
||||
"modified_by": "Administrator", |
|
||||
"name": "Item to Hub Item", |
|
||||
"owner": "Administrator", |
|
||||
"page_length": 10, |
|
||||
"remote_objectname": "Hub Item", |
|
||||
"remote_primary_key": "item_code" |
|
||||
} |
|
@ -1,19 +0,0 @@ |
|||||
{ |
|
||||
"creation": "2017-09-07 11:39:38.445902", |
|
||||
"docstatus": 0, |
|
||||
"doctype": "Data Migration Plan", |
|
||||
"idx": 1, |
|
||||
"mappings": [ |
|
||||
{ |
|
||||
"enabled": 1, |
|
||||
"mapping": "Item to Hub Item" |
|
||||
} |
|
||||
], |
|
||||
"modified": "2018-08-19 22:20:25.644602", |
|
||||
"modified_by": "Administrator", |
|
||||
"module": "Hub Node", |
|
||||
"name": "Hub Sync", |
|
||||
"owner": "Administrator", |
|
||||
"plan_name": "Hub Sync", |
|
||||
"postprocess_method": "erpnext.hub_node.api.item_sync_postprocess" |
|
||||
} |
|
@ -1,8 +0,0 @@ |
|||||
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
|
|
||||
// For license information, please see license.txt
|
|
||||
|
|
||||
frappe.ui.form.on('Hub Tracked Item', { |
|
||||
refresh: function(frm) { |
|
||||
|
|
||||
} |
|
||||
}); |
|
@ -1,210 +0,0 @@ |
|||||
{ |
|
||||
"allow_copy": 0, |
|
||||
"allow_guest_to_view": 0, |
|
||||
"allow_import": 0, |
|
||||
"allow_rename": 0, |
|
||||
"autoname": "field:item_code", |
|
||||
"beta": 0, |
|
||||
"creation": "2018-03-18 09:33:50.267762", |
|
||||
"custom": 0, |
|
||||
"docstatus": 0, |
|
||||
"doctype": "DocType", |
|
||||
"document_type": "", |
|
||||
"editable_grid": 1, |
|
||||
"engine": "InnoDB", |
|
||||
"fields": [ |
|
||||
{ |
|
||||
"allow_bulk_edit": 0, |
|
||||
"allow_in_quick_entry": 0, |
|
||||
"allow_on_submit": 0, |
|
||||
"bold": 0, |
|
||||
"collapsible": 0, |
|
||||
"columns": 0, |
|
||||
"fieldname": "item_code", |
|
||||
"fieldtype": "Data", |
|
||||
"hidden": 0, |
|
||||
"ignore_user_permissions": 0, |
|
||||
"ignore_xss_filter": 0, |
|
||||
"in_filter": 0, |
|
||||
"in_global_search": 0, |
|
||||
"in_list_view": 0, |
|
||||
"in_standard_filter": 0, |
|
||||
"label": "Item Code", |
|
||||
"length": 0, |
|
||||
"no_copy": 0, |
|
||||
"permlevel": 0, |
|
||||
"precision": "", |
|
||||
"print_hide": 0, |
|
||||
"print_hide_if_no_value": 0, |
|
||||
"read_only": 0, |
|
||||
"remember_last_selected_value": 0, |
|
||||
"report_hide": 0, |
|
||||
"reqd": 0, |
|
||||
"search_index": 0, |
|
||||
"set_only_once": 0, |
|
||||
"translatable": 0, |
|
||||
"unique": 1 |
|
||||
}, |
|
||||
{ |
|
||||
"allow_bulk_edit": 0, |
|
||||
"allow_in_quick_entry": 0, |
|
||||
"allow_on_submit": 0, |
|
||||
"bold": 0, |
|
||||
"collapsible": 0, |
|
||||
"columns": 0, |
|
||||
"fieldname": "hub_category", |
|
||||
"fieldtype": "Data", |
|
||||
"hidden": 0, |
|
||||
"ignore_user_permissions": 0, |
|
||||
"ignore_xss_filter": 0, |
|
||||
"in_filter": 0, |
|
||||
"in_global_search": 0, |
|
||||
"in_list_view": 0, |
|
||||
"in_standard_filter": 0, |
|
||||
"label": "Hub Category", |
|
||||
"length": 0, |
|
||||
"no_copy": 0, |
|
||||
"permlevel": 0, |
|
||||
"precision": "", |
|
||||
"print_hide": 0, |
|
||||
"print_hide_if_no_value": 0, |
|
||||
"read_only": 0, |
|
||||
"remember_last_selected_value": 0, |
|
||||
"report_hide": 0, |
|
||||
"reqd": 0, |
|
||||
"search_index": 0, |
|
||||
"set_only_once": 0, |
|
||||
"translatable": 0, |
|
||||
"unique": 0 |
|
||||
}, |
|
||||
{ |
|
||||
"allow_bulk_edit": 0, |
|
||||
"allow_in_quick_entry": 0, |
|
||||
"allow_on_submit": 0, |
|
||||
"bold": 0, |
|
||||
"collapsible": 0, |
|
||||
"columns": 0, |
|
||||
"fieldname": "published", |
|
||||
"fieldtype": "Check", |
|
||||
"hidden": 0, |
|
||||
"ignore_user_permissions": 0, |
|
||||
"ignore_xss_filter": 0, |
|
||||
"in_filter": 0, |
|
||||
"in_global_search": 0, |
|
||||
"in_list_view": 0, |
|
||||
"in_standard_filter": 0, |
|
||||
"label": "Published", |
|
||||
"length": 0, |
|
||||
"no_copy": 0, |
|
||||
"permlevel": 0, |
|
||||
"precision": "", |
|
||||
"print_hide": 0, |
|
||||
"print_hide_if_no_value": 0, |
|
||||
"read_only": 0, |
|
||||
"remember_last_selected_value": 0, |
|
||||
"report_hide": 0, |
|
||||
"reqd": 0, |
|
||||
"search_index": 0, |
|
||||
"set_only_once": 0, |
|
||||
"translatable": 0, |
|
||||
"unique": 0 |
|
||||
}, |
|
||||
{ |
|
||||
"allow_bulk_edit": 0, |
|
||||
"allow_in_quick_entry": 0, |
|
||||
"allow_on_submit": 0, |
|
||||
"bold": 0, |
|
||||
"collapsible": 0, |
|
||||
"columns": 0, |
|
||||
"fieldname": "image_list", |
|
||||
"fieldtype": "Long Text", |
|
||||
"hidden": 0, |
|
||||
"ignore_user_permissions": 0, |
|
||||
"ignore_xss_filter": 0, |
|
||||
"in_filter": 0, |
|
||||
"in_global_search": 0, |
|
||||
"in_list_view": 0, |
|
||||
"in_standard_filter": 0, |
|
||||
"label": "Image List", |
|
||||
"length": 0, |
|
||||
"no_copy": 0, |
|
||||
"permlevel": 0, |
|
||||
"precision": "", |
|
||||
"print_hide": 0, |
|
||||
"print_hide_if_no_value": 0, |
|
||||
"read_only": 0, |
|
||||
"remember_last_selected_value": 0, |
|
||||
"report_hide": 0, |
|
||||
"reqd": 0, |
|
||||
"search_index": 0, |
|
||||
"set_only_once": 0, |
|
||||
"translatable": 0, |
|
||||
"unique": 0 |
|
||||
} |
|
||||
], |
|
||||
"has_web_view": 0, |
|
||||
"hide_heading": 0, |
|
||||
"hide_toolbar": 0, |
|
||||
"idx": 0, |
|
||||
"image_view": 0, |
|
||||
"in_create": 0, |
|
||||
"is_submittable": 0, |
|
||||
"issingle": 0, |
|
||||
"istable": 0, |
|
||||
"max_attachments": 0, |
|
||||
"modified": "2019-12-10 11:37:35.951019", |
|
||||
"modified_by": "Administrator", |
|
||||
"module": "Hub Node", |
|
||||
"name": "Hub Tracked Item", |
|
||||
"name_case": "", |
|
||||
"owner": "Administrator", |
|
||||
"permissions": [ |
|
||||
{ |
|
||||
"amend": 0, |
|
||||
"cancel": 0, |
|
||||
"create": 1, |
|
||||
"delete": 1, |
|
||||
"email": 1, |
|
||||
"export": 1, |
|
||||
"if_owner": 0, |
|
||||
"import": 0, |
|
||||
"permlevel": 0, |
|
||||
"print": 1, |
|
||||
"read": 1, |
|
||||
"report": 1, |
|
||||
"role": "System Manager", |
|
||||
"set_user_permissions": 0, |
|
||||
"share": 1, |
|
||||
"submit": 0, |
|
||||
"write": 1 |
|
||||
}, |
|
||||
{ |
|
||||
"amend": 0, |
|
||||
"cancel": 0, |
|
||||
"create": 1, |
|
||||
"delete": 1, |
|
||||
"email": 1, |
|
||||
"export": 1, |
|
||||
"if_owner": 0, |
|
||||
"import": 0, |
|
||||
"permlevel": 0, |
|
||||
"print": 1, |
|
||||
"read": 1, |
|
||||
"report": 1, |
|
||||
"role": "Item Manager", |
|
||||
"set_user_permissions": 0, |
|
||||
"share": 1, |
|
||||
"submit": 0, |
|
||||
"write": 1 |
|
||||
} |
|
||||
], |
|
||||
"quick_entry": 1, |
|
||||
"read_only": 1, |
|
||||
"read_only_onload": 0, |
|
||||
"show_name_in_global_search": 0, |
|
||||
"sort_field": "modified", |
|
||||
"sort_order": "DESC", |
|
||||
"track_changes": 1, |
|
||||
"track_seen": 0, |
|
||||
"track_views": 0 |
|
||||
} |
|
@ -1,11 +0,0 @@ |
|||||
# -*- coding: utf-8 -*- |
|
||||
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors |
|
||||
# For license information, please see license.txt |
|
||||
|
|
||||
from __future__ import unicode_literals |
|
||||
|
|
||||
from frappe.model.document import Document |
|
||||
|
|
||||
|
|
||||
class HubTrackedItem(Document): |
|
||||
pass |
|
@ -1,10 +0,0 @@ |
|||||
# -*- coding: utf-8 -*- |
|
||||
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors |
|
||||
# See license.txt |
|
||||
from __future__ import unicode_literals |
|
||||
|
|
||||
import unittest |
|
||||
|
|
||||
|
|
||||
class TestHubTrackedItem(unittest.TestCase): |
|
||||
pass |
|
@ -1,140 +0,0 @@ |
|||||
{ |
|
||||
"allow_copy": 0, |
|
||||
"allow_guest_to_view": 0, |
|
||||
"allow_import": 0, |
|
||||
"allow_rename": 0, |
|
||||
"autoname": "", |
|
||||
"beta": 0, |
|
||||
"creation": "2018-08-31 12:36:45.627531", |
|
||||
"custom": 0, |
|
||||
"docstatus": 0, |
|
||||
"doctype": "DocType", |
|
||||
"document_type": "", |
|
||||
"editable_grid": 1, |
|
||||
"engine": "InnoDB", |
|
||||
"fields": [ |
|
||||
{ |
|
||||
"allow_bulk_edit": 0, |
|
||||
"allow_in_quick_entry": 0, |
|
||||
"allow_on_submit": 0, |
|
||||
"bold": 0, |
|
||||
"collapsible": 0, |
|
||||
"columns": 0, |
|
||||
"fieldname": "user", |
|
||||
"fieldtype": "Link", |
|
||||
"hidden": 0, |
|
||||
"ignore_user_permissions": 0, |
|
||||
"ignore_xss_filter": 0, |
|
||||
"in_filter": 0, |
|
||||
"in_global_search": 0, |
|
||||
"in_list_view": 1, |
|
||||
"in_standard_filter": 0, |
|
||||
"label": "User", |
|
||||
"length": 0, |
|
||||
"no_copy": 0, |
|
||||
"options": "User", |
|
||||
"permlevel": 0, |
|
||||
"precision": "", |
|
||||
"print_hide": 0, |
|
||||
"print_hide_if_no_value": 0, |
|
||||
"read_only": 0, |
|
||||
"remember_last_selected_value": 0, |
|
||||
"report_hide": 0, |
|
||||
"reqd": 1, |
|
||||
"search_index": 0, |
|
||||
"set_only_once": 0, |
|
||||
"translatable": 0, |
|
||||
"unique": 1 |
|
||||
}, |
|
||||
{ |
|
||||
"allow_bulk_edit": 0, |
|
||||
"allow_in_quick_entry": 0, |
|
||||
"allow_on_submit": 0, |
|
||||
"bold": 0, |
|
||||
"collapsible": 0, |
|
||||
"columns": 0, |
|
||||
"fieldname": "hub_user_name", |
|
||||
"fieldtype": "Data", |
|
||||
"hidden": 0, |
|
||||
"ignore_user_permissions": 0, |
|
||||
"ignore_xss_filter": 0, |
|
||||
"in_filter": 0, |
|
||||
"in_global_search": 0, |
|
||||
"in_list_view": 1, |
|
||||
"in_standard_filter": 0, |
|
||||
"label": "Hub User", |
|
||||
"length": 0, |
|
||||
"no_copy": 0, |
|
||||
"permlevel": 0, |
|
||||
"precision": "", |
|
||||
"print_hide": 0, |
|
||||
"print_hide_if_no_value": 0, |
|
||||
"read_only": 1, |
|
||||
"remember_last_selected_value": 0, |
|
||||
"report_hide": 0, |
|
||||
"reqd": 0, |
|
||||
"search_index": 0, |
|
||||
"set_only_once": 0, |
|
||||
"translatable": 0, |
|
||||
"unique": 0 |
|
||||
}, |
|
||||
{ |
|
||||
"allow_bulk_edit": 0, |
|
||||
"allow_in_quick_entry": 0, |
|
||||
"allow_on_submit": 0, |
|
||||
"bold": 0, |
|
||||
"collapsible": 0, |
|
||||
"columns": 0, |
|
||||
"fieldname": "password", |
|
||||
"fieldtype": "Password", |
|
||||
"hidden": 0, |
|
||||
"ignore_user_permissions": 0, |
|
||||
"ignore_xss_filter": 0, |
|
||||
"in_filter": 0, |
|
||||
"in_global_search": 0, |
|
||||
"in_list_view": 1, |
|
||||
"in_standard_filter": 0, |
|
||||
"label": "Hub Password", |
|
||||
"length": 0, |
|
||||
"no_copy": 0, |
|
||||
"permlevel": 0, |
|
||||
"precision": "", |
|
||||
"print_hide": 0, |
|
||||
"print_hide_if_no_value": 0, |
|
||||
"read_only": 1, |
|
||||
"remember_last_selected_value": 0, |
|
||||
"report_hide": 0, |
|
||||
"reqd": 0, |
|
||||
"search_index": 0, |
|
||||
"set_only_once": 0, |
|
||||
"translatable": 0, |
|
||||
"unique": 0 |
|
||||
} |
|
||||
], |
|
||||
"has_web_view": 0, |
|
||||
"hide_heading": 0, |
|
||||
"hide_toolbar": 0, |
|
||||
"idx": 0, |
|
||||
"image_view": 0, |
|
||||
"in_create": 0, |
|
||||
"is_submittable": 0, |
|
||||
"issingle": 0, |
|
||||
"istable": 1, |
|
||||
"max_attachments": 0, |
|
||||
"modified": "2020-09-18 17:26:09.703215", |
|
||||
"modified_by": "Administrator", |
|
||||
"module": "Hub Node", |
|
||||
"name": "Hub User", |
|
||||
"name_case": "", |
|
||||
"owner": "Administrator", |
|
||||
"permissions": [], |
|
||||
"quick_entry": 1, |
|
||||
"read_only": 0, |
|
||||
"read_only_onload": 0, |
|
||||
"show_name_in_global_search": 0, |
|
||||
"sort_field": "modified", |
|
||||
"sort_order": "DESC", |
|
||||
"track_changes": 1, |
|
||||
"track_seen": 0, |
|
||||
"track_views": 0 |
|
||||
} |
|
@ -1,11 +0,0 @@ |
|||||
# -*- coding: utf-8 -*- |
|
||||
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors |
|
||||
# For license information, please see license.txt |
|
||||
|
|
||||
from __future__ import unicode_literals |
|
||||
|
|
||||
from frappe.model.document import Document |
|
||||
|
|
||||
|
|
||||
class HubUser(Document): |
|
||||
pass |
|
@ -1,72 +0,0 @@ |
|||||
{ |
|
||||
"allow_copy": 0, |
|
||||
"allow_guest_to_view": 0, |
|
||||
"allow_import": 0, |
|
||||
"allow_rename": 0, |
|
||||
"beta": 0, |
|
||||
"creation": "2018-03-06 04:38:49.891787", |
|
||||
"custom": 0, |
|
||||
"docstatus": 0, |
|
||||
"doctype": "DocType", |
|
||||
"document_type": "", |
|
||||
"editable_grid": 1, |
|
||||
"engine": "InnoDB", |
|
||||
"fields": [ |
|
||||
{ |
|
||||
"allow_bulk_edit": 0, |
|
||||
"allow_on_submit": 0, |
|
||||
"bold": 0, |
|
||||
"collapsible": 0, |
|
||||
"columns": 0, |
|
||||
"fieldname": "user", |
|
||||
"fieldtype": "Link", |
|
||||
"hidden": 0, |
|
||||
"ignore_user_permissions": 0, |
|
||||
"ignore_xss_filter": 0, |
|
||||
"in_filter": 0, |
|
||||
"in_global_search": 0, |
|
||||
"in_list_view": 1, |
|
||||
"in_standard_filter": 0, |
|
||||
"label": "User", |
|
||||
"length": 0, |
|
||||
"no_copy": 0, |
|
||||
"options": "User", |
|
||||
"permlevel": 0, |
|
||||
"precision": "", |
|
||||
"print_hide": 0, |
|
||||
"print_hide_if_no_value": 0, |
|
||||
"read_only": 0, |
|
||||
"remember_last_selected_value": 0, |
|
||||
"report_hide": 0, |
|
||||
"reqd": 1, |
|
||||
"search_index": 0, |
|
||||
"set_only_once": 0, |
|
||||
"unique": 0 |
|
||||
} |
|
||||
], |
|
||||
"has_web_view": 0, |
|
||||
"hide_heading": 0, |
|
||||
"hide_toolbar": 0, |
|
||||
"idx": 0, |
|
||||
"image_view": 0, |
|
||||
"in_create": 0, |
|
||||
"is_submittable": 0, |
|
||||
"issingle": 0, |
|
||||
"istable": 1, |
|
||||
"max_attachments": 0, |
|
||||
"modified": "2020-09-18 17:26:09.703215", |
|
||||
"modified_by": "Administrator", |
|
||||
"module": "Hub Node", |
|
||||
"name": "Hub Users", |
|
||||
"name_case": "", |
|
||||
"owner": "Administrator", |
|
||||
"permissions": [], |
|
||||
"quick_entry": 1, |
|
||||
"read_only": 0, |
|
||||
"read_only_onload": 0, |
|
||||
"show_name_in_global_search": 0, |
|
||||
"sort_field": "modified", |
|
||||
"sort_order": "DESC", |
|
||||
"track_changes": 1, |
|
||||
"track_seen": 0 |
|
||||
} |
|
@ -1,11 +0,0 @@ |
|||||
# -*- coding: utf-8 -*- |
|
||||
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors |
|
||||
# For license information, please see license.txt |
|
||||
|
|
||||
from __future__ import unicode_literals |
|
||||
|
|
||||
from frappe.model.document import Document |
|
||||
|
|
||||
|
|
||||
class HubUsers(Document): |
|
||||
pass |
|
@ -1,8 +0,0 @@ |
|||||
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
|
|
||||
// For license information, please see license.txt
|
|
||||
|
|
||||
frappe.ui.form.on('Marketplace Settings', { |
|
||||
refresh: function(frm) { |
|
||||
$('#toolbar-user .marketplace-link').toggle(!frm.doc.disable_marketplace); |
|
||||
}, |
|
||||
}); |
|
@ -1,410 +0,0 @@ |
|||||
{ |
|
||||
"allow_copy": 0, |
|
||||
"allow_events_in_timeline": 0, |
|
||||
"allow_guest_to_view": 0, |
|
||||
"allow_import": 0, |
|
||||
"allow_rename": 0, |
|
||||
"beta": 1, |
|
||||
"creation": "2018-08-31 15:54:38.795263", |
|
||||
"custom": 0, |
|
||||
"description": "", |
|
||||
"docstatus": 0, |
|
||||
"doctype": "DocType", |
|
||||
"document_type": "", |
|
||||
"editable_grid": 0, |
|
||||
"engine": "InnoDB", |
|
||||
"fields": [ |
|
||||
{ |
|
||||
"allow_bulk_edit": 0, |
|
||||
"allow_in_quick_entry": 0, |
|
||||
"allow_on_submit": 0, |
|
||||
"bold": 0, |
|
||||
"collapsible": 0, |
|
||||
"columns": 0, |
|
||||
"fieldname": "disable_marketplace", |
|
||||
"fieldtype": "Check", |
|
||||
"hidden": 0, |
|
||||
"ignore_user_permissions": 0, |
|
||||
"ignore_xss_filter": 0, |
|
||||
"in_filter": 0, |
|
||||
"in_global_search": 0, |
|
||||
"in_list_view": 0, |
|
||||
"in_standard_filter": 0, |
|
||||
"label": "Disable Marketplace", |
|
||||
"length": 0, |
|
||||
"no_copy": 0, |
|
||||
"permlevel": 0, |
|
||||
"precision": "", |
|
||||
"print_hide": 0, |
|
||||
"print_hide_if_no_value": 0, |
|
||||
"read_only": 0, |
|
||||
"remember_last_selected_value": 0, |
|
||||
"report_hide": 0, |
|
||||
"reqd": 0, |
|
||||
"search_index": 0, |
|
||||
"set_only_once": 0, |
|
||||
"translatable": 0, |
|
||||
"unique": 0 |
|
||||
}, |
|
||||
{ |
|
||||
"allow_bulk_edit": 0, |
|
||||
"allow_in_quick_entry": 0, |
|
||||
"allow_on_submit": 0, |
|
||||
"bold": 0, |
|
||||
"collapsible": 0, |
|
||||
"columns": 0, |
|
||||
"depends_on": "eval:!doc.disable_marketplace", |
|
||||
"fieldname": "marketplace_settings_section", |
|
||||
"fieldtype": "Section Break", |
|
||||
"hidden": 0, |
|
||||
"ignore_user_permissions": 0, |
|
||||
"ignore_xss_filter": 0, |
|
||||
"in_filter": 0, |
|
||||
"in_global_search": 0, |
|
||||
"in_list_view": 0, |
|
||||
"in_standard_filter": 0, |
|
||||
"label": "Marketplace Settings", |
|
||||
"length": 0, |
|
||||
"no_copy": 0, |
|
||||
"permlevel": 0, |
|
||||
"precision": "", |
|
||||
"print_hide": 0, |
|
||||
"print_hide_if_no_value": 0, |
|
||||
"read_only": 0, |
|
||||
"remember_last_selected_value": 0, |
|
||||
"report_hide": 0, |
|
||||
"reqd": 0, |
|
||||
"search_index": 0, |
|
||||
"set_only_once": 0, |
|
||||
"translatable": 0, |
|
||||
"unique": 0 |
|
||||
}, |
|
||||
{ |
|
||||
"allow_bulk_edit": 0, |
|
||||
"allow_in_quick_entry": 0, |
|
||||
"allow_on_submit": 0, |
|
||||
"bold": 0, |
|
||||
"collapsible": 0, |
|
||||
"columns": 0, |
|
||||
"default": "https://hubmarket.org", |
|
||||
"fieldname": "marketplace_url", |
|
||||
"fieldtype": "Data", |
|
||||
"hidden": 0, |
|
||||
"ignore_user_permissions": 0, |
|
||||
"ignore_xss_filter": 0, |
|
||||
"in_filter": 0, |
|
||||
"in_global_search": 0, |
|
||||
"in_list_view": 0, |
|
||||
"in_standard_filter": 0, |
|
||||
"label": "Marketplace URL (to hide and update label)", |
|
||||
"length": 0, |
|
||||
"no_copy": 0, |
|
||||
"permlevel": 0, |
|
||||
"precision": "", |
|
||||
"print_hide": 0, |
|
||||
"print_hide_if_no_value": 0, |
|
||||
"read_only": 0, |
|
||||
"remember_last_selected_value": 0, |
|
||||
"report_hide": 0, |
|
||||
"reqd": 0, |
|
||||
"search_index": 0, |
|
||||
"set_only_once": 0, |
|
||||
"translatable": 0, |
|
||||
"unique": 0 |
|
||||
}, |
|
||||
{ |
|
||||
"allow_bulk_edit": 0, |
|
||||
"allow_in_quick_entry": 0, |
|
||||
"allow_on_submit": 0, |
|
||||
"bold": 0, |
|
||||
"collapsible": 0, |
|
||||
"columns": 0, |
|
||||
"fieldname": "registered", |
|
||||
"fieldtype": "Check", |
|
||||
"hidden": 0, |
|
||||
"ignore_user_permissions": 0, |
|
||||
"ignore_xss_filter": 0, |
|
||||
"in_filter": 0, |
|
||||
"in_global_search": 0, |
|
||||
"in_list_view": 0, |
|
||||
"in_standard_filter": 0, |
|
||||
"label": "Registered", |
|
||||
"length": 0, |
|
||||
"no_copy": 0, |
|
||||
"permlevel": 0, |
|
||||
"precision": "", |
|
||||
"print_hide": 0, |
|
||||
"print_hide_if_no_value": 0, |
|
||||
"read_only": 0, |
|
||||
"remember_last_selected_value": 0, |
|
||||
"report_hide": 0, |
|
||||
"reqd": 0, |
|
||||
"search_index": 0, |
|
||||
"set_only_once": 0, |
|
||||
"translatable": 0, |
|
||||
"unique": 0 |
|
||||
}, |
|
||||
{ |
|
||||
"allow_bulk_edit": 0, |
|
||||
"allow_in_quick_entry": 0, |
|
||||
"allow_on_submit": 0, |
|
||||
"bold": 0, |
|
||||
"collapsible": 0, |
|
||||
"columns": 0, |
|
||||
"fieldname": "sync_in_progress", |
|
||||
"fieldtype": "Check", |
|
||||
"hidden": 1, |
|
||||
"ignore_user_permissions": 0, |
|
||||
"ignore_xss_filter": 0, |
|
||||
"in_filter": 0, |
|
||||
"in_global_search": 0, |
|
||||
"in_list_view": 0, |
|
||||
"in_standard_filter": 0, |
|
||||
"label": "Sync in Progress", |
|
||||
"length": 0, |
|
||||
"no_copy": 0, |
|
||||
"permlevel": 0, |
|
||||
"precision": "", |
|
||||
"print_hide": 0, |
|
||||
"print_hide_if_no_value": 0, |
|
||||
"read_only": 0, |
|
||||
"remember_last_selected_value": 0, |
|
||||
"report_hide": 0, |
|
||||
"reqd": 0, |
|
||||
"search_index": 0, |
|
||||
"set_only_once": 0, |
|
||||
"translatable": 0, |
|
||||
"unique": 0 |
|
||||
}, |
|
||||
{ |
|
||||
"allow_bulk_edit": 0, |
|
||||
"allow_in_quick_entry": 0, |
|
||||
"allow_on_submit": 0, |
|
||||
"bold": 0, |
|
||||
"collapsible": 0, |
|
||||
"columns": 0, |
|
||||
"fieldname": "company", |
|
||||
"fieldtype": "Link", |
|
||||
"hidden": 0, |
|
||||
"ignore_user_permissions": 0, |
|
||||
"ignore_xss_filter": 0, |
|
||||
"in_filter": 0, |
|
||||
"in_global_search": 0, |
|
||||
"in_list_view": 1, |
|
||||
"in_standard_filter": 0, |
|
||||
"label": "Company", |
|
||||
"length": 0, |
|
||||
"no_copy": 0, |
|
||||
"options": "Company", |
|
||||
"permlevel": 0, |
|
||||
"precision": "", |
|
||||
"print_hide": 0, |
|
||||
"print_hide_if_no_value": 0, |
|
||||
"read_only": 0, |
|
||||
"remember_last_selected_value": 0, |
|
||||
"report_hide": 0, |
|
||||
"reqd": 0, |
|
||||
"search_index": 0, |
|
||||
"set_only_once": 0, |
|
||||
"translatable": 0, |
|
||||
"unique": 0 |
|
||||
}, |
|
||||
{ |
|
||||
"allow_bulk_edit": 0, |
|
||||
"allow_in_quick_entry": 0, |
|
||||
"allow_on_submit": 0, |
|
||||
"bold": 0, |
|
||||
"collapsible": 0, |
|
||||
"columns": 0, |
|
||||
"fieldname": "hub_seller_name", |
|
||||
"fieldtype": "Data", |
|
||||
"hidden": 1, |
|
||||
"ignore_user_permissions": 0, |
|
||||
"ignore_xss_filter": 0, |
|
||||
"in_filter": 0, |
|
||||
"in_global_search": 0, |
|
||||
"in_list_view": 0, |
|
||||
"in_standard_filter": 0, |
|
||||
"label": "Hub Seller Name", |
|
||||
"length": 0, |
|
||||
"no_copy": 0, |
|
||||
"permlevel": 0, |
|
||||
"precision": "", |
|
||||
"print_hide": 0, |
|
||||
"print_hide_if_no_value": 0, |
|
||||
"read_only": 0, |
|
||||
"remember_last_selected_value": 0, |
|
||||
"report_hide": 0, |
|
||||
"reqd": 0, |
|
||||
"search_index": 0, |
|
||||
"set_only_once": 0, |
|
||||
"translatable": 0, |
|
||||
"unique": 0 |
|
||||
}, |
|
||||
{ |
|
||||
"allow_bulk_edit": 0, |
|
||||
"allow_in_quick_entry": 0, |
|
||||
"allow_on_submit": 0, |
|
||||
"bold": 0, |
|
||||
"collapsible": 0, |
|
||||
"columns": 0, |
|
||||
"fieldname": "users", |
|
||||
"fieldtype": "Table", |
|
||||
"hidden": 0, |
|
||||
"ignore_user_permissions": 0, |
|
||||
"ignore_xss_filter": 0, |
|
||||
"in_filter": 0, |
|
||||
"in_global_search": 0, |
|
||||
"in_list_view": 0, |
|
||||
"in_standard_filter": 0, |
|
||||
"label": "Users", |
|
||||
"length": 0, |
|
||||
"no_copy": 0, |
|
||||
"options": "Hub User", |
|
||||
"permlevel": 0, |
|
||||
"precision": "", |
|
||||
"print_hide": 0, |
|
||||
"print_hide_if_no_value": 0, |
|
||||
"read_only": 0, |
|
||||
"remember_last_selected_value": 0, |
|
||||
"report_hide": 0, |
|
||||
"reqd": 0, |
|
||||
"search_index": 0, |
|
||||
"set_only_once": 0, |
|
||||
"translatable": 0, |
|
||||
"unique": 0 |
|
||||
}, |
|
||||
{ |
|
||||
"allow_bulk_edit": 0, |
|
||||
"allow_in_quick_entry": 0, |
|
||||
"allow_on_submit": 0, |
|
||||
"bold": 0, |
|
||||
"collapsible": 0, |
|
||||
"columns": 0, |
|
||||
"depends_on": "", |
|
||||
"fieldname": "last_sync_datetime", |
|
||||
"fieldtype": "Datetime", |
|
||||
"hidden": 0, |
|
||||
"ignore_user_permissions": 0, |
|
||||
"ignore_xss_filter": 0, |
|
||||
"in_filter": 0, |
|
||||
"in_global_search": 0, |
|
||||
"in_list_view": 0, |
|
||||
"in_standard_filter": 0, |
|
||||
"label": "Last Sync On", |
|
||||
"length": 0, |
|
||||
"no_copy": 0, |
|
||||
"permlevel": 0, |
|
||||
"precision": "", |
|
||||
"print_hide": 0, |
|
||||
"print_hide_if_no_value": 0, |
|
||||
"read_only": 0, |
|
||||
"remember_last_selected_value": 0, |
|
||||
"report_hide": 0, |
|
||||
"reqd": 0, |
|
||||
"search_index": 0, |
|
||||
"set_only_once": 0, |
|
||||
"translatable": 0, |
|
||||
"unique": 0 |
|
||||
}, |
|
||||
{ |
|
||||
"allow_bulk_edit": 0, |
|
||||
"allow_in_quick_entry": 0, |
|
||||
"allow_on_submit": 0, |
|
||||
"bold": 0, |
|
||||
"collapsible": 0, |
|
||||
"columns": 0, |
|
||||
"default": "", |
|
||||
"depends_on": "eval:1", |
|
||||
"fieldname": "custom_data", |
|
||||
"fieldtype": "Code", |
|
||||
"hidden": 0, |
|
||||
"ignore_user_permissions": 0, |
|
||||
"ignore_xss_filter": 0, |
|
||||
"in_filter": 0, |
|
||||
"in_global_search": 0, |
|
||||
"in_list_view": 0, |
|
||||
"in_standard_filter": 0, |
|
||||
"label": "Custom Data", |
|
||||
"length": 0, |
|
||||
"no_copy": 0, |
|
||||
"permlevel": 0, |
|
||||
"precision": "", |
|
||||
"print_hide": 0, |
|
||||
"print_hide_if_no_value": 0, |
|
||||
"read_only": 1, |
|
||||
"remember_last_selected_value": 0, |
|
||||
"report_hide": 0, |
|
||||
"reqd": 0, |
|
||||
"search_index": 0, |
|
||||
"set_only_once": 0, |
|
||||
"translatable": 0, |
|
||||
"unique": 0 |
|
||||
} |
|
||||
], |
|
||||
"has_web_view": 0, |
|
||||
"hide_heading": 0, |
|
||||
"hide_toolbar": 0, |
|
||||
"idx": 0, |
|
||||
"image_view": 0, |
|
||||
"in_create": 0, |
|
||||
"is_submittable": 0, |
|
||||
"issingle": 1, |
|
||||
"istable": 0, |
|
||||
"max_attachments": 0, |
|
||||
"modified": "2020-09-18 17:26:09.703215", |
|
||||
"modified_by": "Administrator", |
|
||||
"module": "Hub Node", |
|
||||
"name": "Marketplace Settings", |
|
||||
"name_case": "", |
|
||||
"owner": "Administrator", |
|
||||
"permissions": [ |
|
||||
{ |
|
||||
"amend": 0, |
|
||||
"cancel": 0, |
|
||||
"create": 1, |
|
||||
"delete": 0, |
|
||||
"email": 0, |
|
||||
"export": 0, |
|
||||
"if_owner": 0, |
|
||||
"import": 0, |
|
||||
"permlevel": 0, |
|
||||
"print": 0, |
|
||||
"read": 1, |
|
||||
"report": 0, |
|
||||
"role": "System Manager", |
|
||||
"set_user_permissions": 0, |
|
||||
"share": 0, |
|
||||
"submit": 0, |
|
||||
"write": 1 |
|
||||
}, |
|
||||
{ |
|
||||
"amend": 0, |
|
||||
"cancel": 0, |
|
||||
"create": 0, |
|
||||
"delete": 0, |
|
||||
"email": 1, |
|
||||
"export": 0, |
|
||||
"if_owner": 0, |
|
||||
"import": 0, |
|
||||
"permlevel": 0, |
|
||||
"print": 1, |
|
||||
"read": 1, |
|
||||
"report": 0, |
|
||||
"role": "All", |
|
||||
"set_user_permissions": 0, |
|
||||
"share": 1, |
|
||||
"submit": 0, |
|
||||
"write": 0 |
|
||||
} |
|
||||
], |
|
||||
"quick_entry": 0, |
|
||||
"read_only": 0, |
|
||||
"read_only_onload": 0, |
|
||||
"show_name_in_global_search": 0, |
|
||||
"sort_field": "modified", |
|
||||
"sort_order": "DESC", |
|
||||
"track_changes": 1, |
|
||||
"track_seen": 0, |
|
||||
"track_views": 0 |
|
||||
} |
|
@ -1,93 +0,0 @@ |
|||||
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors and contributors |
|
||||
# For license information, please see license.txt |
|
||||
|
|
||||
from __future__ import unicode_literals |
|
||||
|
|
||||
import json |
|
||||
|
|
||||
import frappe |
|
||||
from frappe.frappeclient import FrappeClient |
|
||||
from frappe.model.document import Document |
|
||||
from frappe.utils import cint |
|
||||
|
|
||||
|
|
||||
class MarketplaceSettings(Document): |
|
||||
|
|
||||
def register_seller(self, company, company_description): |
|
||||
|
|
||||
country, currency, company_logo = frappe.db.get_value('Company', company, |
|
||||
['country', 'default_currency', 'company_logo']) |
|
||||
|
|
||||
company_details = { |
|
||||
'company': company, |
|
||||
'country': country, |
|
||||
'currency': currency, |
|
||||
'company_description': company_description, |
|
||||
'company_logo': company_logo, |
|
||||
'site_name': frappe.utils.get_url() |
|
||||
} |
|
||||
|
|
||||
hub_connection = self.get_connection() |
|
||||
|
|
||||
response = hub_connection.post_request({ |
|
||||
'cmd': 'hub.hub.api.add_hub_seller', |
|
||||
'company_details': json.dumps(company_details) |
|
||||
}) |
|
||||
|
|
||||
return response |
|
||||
|
|
||||
|
|
||||
def add_hub_user(self, user_email): |
|
||||
'''Create a Hub User and User record on hub server |
|
||||
and if successfull append it to Hub User table |
|
||||
''' |
|
||||
|
|
||||
if not self.registered: |
|
||||
return |
|
||||
|
|
||||
hub_connection = self.get_connection() |
|
||||
|
|
||||
first_name, last_name = frappe.db.get_value('User', user_email, ['first_name', 'last_name']) |
|
||||
|
|
||||
hub_user = hub_connection.post_request({ |
|
||||
'cmd': 'hub.hub.api.add_hub_user', |
|
||||
'user_email': user_email, |
|
||||
'first_name': first_name, |
|
||||
'last_name': last_name, |
|
||||
'hub_seller': self.hub_seller_name |
|
||||
}) |
|
||||
|
|
||||
self.append('users', { |
|
||||
'user': hub_user.get('user_email'), |
|
||||
'hub_user_name': hub_user.get('hub_user_name'), |
|
||||
'password': hub_user.get('password') |
|
||||
}) |
|
||||
|
|
||||
self.save() |
|
||||
|
|
||||
def get_hub_user(self, user): |
|
||||
'''Return the Hub User doc from the `users` table if password is set''' |
|
||||
|
|
||||
filtered_users = list(filter( |
|
||||
lambda x: x.user == user and x.password, |
|
||||
self.users |
|
||||
)) |
|
||||
|
|
||||
if filtered_users: |
|
||||
return filtered_users[0] |
|
||||
|
|
||||
|
|
||||
def get_connection(self): |
|
||||
return FrappeClient(self.marketplace_url) |
|
||||
|
|
||||
|
|
||||
def unregister(self): |
|
||||
"""Disable the User on hubmarket.org""" |
|
||||
|
|
||||
@frappe.whitelist() |
|
||||
def is_marketplace_enabled(): |
|
||||
if not hasattr(frappe.local, 'is_marketplace_enabled'): |
|
||||
frappe.local.is_marketplace_enabled = cint(frappe.db.get_single_value('Marketplace Settings', |
|
||||
'disable_marketplace')) |
|
||||
|
|
||||
return frappe.local.is_marketplace_enabled |
|
@ -1,10 +0,0 @@ |
|||||
# -*- coding: utf-8 -*- |
|
||||
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors |
|
||||
# See license.txt |
|
||||
from __future__ import unicode_literals |
|
||||
|
|
||||
import unittest |
|
||||
|
|
||||
|
|
||||
class TestMarketplaceSettings(unittest.TestCase): |
|
||||
pass |
|
@ -1,148 +0,0 @@ |
|||||
from __future__ import unicode_literals |
|
||||
|
|
||||
import json |
|
||||
|
|
||||
import frappe |
|
||||
from frappe.contacts.doctype.contact.contact import get_default_contact |
|
||||
from frappe.frappeclient import FrappeClient |
|
||||
from frappe.utils import nowdate |
|
||||
from frappe.utils.nestedset import get_root_of |
|
||||
|
|
||||
|
|
||||
def get_list(doctype, start, limit, fields, filters, order_by): |
|
||||
pass |
|
||||
|
|
||||
def get_hub_connection(): |
|
||||
if frappe.db.exists('Data Migration Connector', 'Hub Connector'): |
|
||||
hub_connector = frappe.get_doc('Data Migration Connector', 'Hub Connector') |
|
||||
hub_connection = hub_connector.get_connection() |
|
||||
return hub_connection.connection |
|
||||
|
|
||||
# read-only connection |
|
||||
hub_connection = FrappeClient(frappe.conf.hub_url) |
|
||||
return hub_connection |
|
||||
|
|
||||
def make_opportunity(buyer_name, email_id): |
|
||||
buyer_name = "HUB-" + buyer_name |
|
||||
|
|
||||
if not frappe.db.exists('Lead', {'email_id': email_id}): |
|
||||
lead = frappe.new_doc("Lead") |
|
||||
lead.lead_name = buyer_name |
|
||||
lead.email_id = email_id |
|
||||
lead.save(ignore_permissions=True) |
|
||||
|
|
||||
o = frappe.new_doc("Opportunity") |
|
||||
o.opportunity_from = "Lead" |
|
||||
o.lead = frappe.get_all("Lead", filters={"email_id": email_id}, fields = ["name"])[0]["name"] |
|
||||
o.save(ignore_permissions=True) |
|
||||
|
|
||||
@frappe.whitelist() |
|
||||
def make_rfq_and_send_opportunity(item, supplier): |
|
||||
supplier = make_supplier(supplier) |
|
||||
contact = make_contact(supplier) |
|
||||
item = make_item(item) |
|
||||
rfq = make_rfq(item, supplier, contact) |
|
||||
status = send_opportunity(contact) |
|
||||
|
|
||||
return { |
|
||||
'rfq': rfq, |
|
||||
'hub_document_created': status |
|
||||
} |
|
||||
|
|
||||
def make_supplier(supplier): |
|
||||
# make supplier if not already exists |
|
||||
supplier = frappe._dict(json.loads(supplier)) |
|
||||
|
|
||||
if not frappe.db.exists('Supplier', {'supplier_name': supplier.supplier_name}): |
|
||||
supplier_doc = frappe.get_doc({ |
|
||||
'doctype': 'Supplier', |
|
||||
'supplier_name': supplier.supplier_name, |
|
||||
'supplier_group': supplier.supplier_group, |
|
||||
'supplier_email': supplier.supplier_email |
|
||||
}).insert() |
|
||||
else: |
|
||||
supplier_doc = frappe.get_doc('Supplier', supplier.supplier_name) |
|
||||
|
|
||||
return supplier_doc |
|
||||
|
|
||||
def make_contact(supplier): |
|
||||
contact_name = get_default_contact('Supplier', supplier.supplier_name) |
|
||||
# make contact if not already exists |
|
||||
if not contact_name: |
|
||||
contact = frappe.get_doc({ |
|
||||
'doctype': 'Contact', |
|
||||
'first_name': supplier.supplier_name, |
|
||||
'is_primary_contact': 1, |
|
||||
'links': [ |
|
||||
{'link_doctype': 'Supplier', 'link_name': supplier.supplier_name} |
|
||||
] |
|
||||
}) |
|
||||
contact.add_email(supplier.supplier_email, is_primary=True) |
|
||||
contact.insert() |
|
||||
else: |
|
||||
contact = frappe.get_doc('Contact', contact_name) |
|
||||
|
|
||||
return contact |
|
||||
|
|
||||
def make_item(item): |
|
||||
# make item if not already exists |
|
||||
item = frappe._dict(json.loads(item)) |
|
||||
|
|
||||
if not frappe.db.exists('Item', {'item_code': item.item_code}): |
|
||||
item_doc = frappe.get_doc({ |
|
||||
'doctype': 'Item', |
|
||||
'item_code': item.item_code, |
|
||||
'item_group': item.item_group, |
|
||||
'is_item_from_hub': 1 |
|
||||
}).insert() |
|
||||
else: |
|
||||
item_doc = frappe.get_doc('Item', item.item_code) |
|
||||
|
|
||||
return item_doc |
|
||||
|
|
||||
def make_rfq(item, supplier, contact): |
|
||||
# make rfq |
|
||||
rfq = frappe.get_doc({ |
|
||||
'doctype': 'Request for Quotation', |
|
||||
'transaction_date': nowdate(), |
|
||||
'status': 'Draft', |
|
||||
'company': frappe.db.get_single_value('Marketplace Settings', 'company'), |
|
||||
'message_for_supplier': 'Please supply the specified items at the best possible rates', |
|
||||
'suppliers': [ |
|
||||
{ 'supplier': supplier.name, 'contact': contact.name } |
|
||||
], |
|
||||
'items': [ |
|
||||
{ |
|
||||
'item_code': item.item_code, |
|
||||
'qty': 1, |
|
||||
'schedule_date': nowdate(), |
|
||||
'warehouse': item.default_warehouse or get_root_of("Warehouse"), |
|
||||
'description': item.description, |
|
||||
'uom': item.stock_uom |
|
||||
} |
|
||||
] |
|
||||
}).insert() |
|
||||
|
|
||||
rfq.save() |
|
||||
rfq.submit() |
|
||||
return rfq |
|
||||
|
|
||||
def send_opportunity(contact): |
|
||||
# Make Hub Message on Hub with lead data |
|
||||
doc = { |
|
||||
'doctype': 'Lead', |
|
||||
'lead_name': frappe.db.get_single_value('Marketplace Settings', 'company'), |
|
||||
'email_id': frappe.db.get_single_value('Marketplace Settings', 'user') |
|
||||
} |
|
||||
|
|
||||
args = frappe._dict(dict( |
|
||||
doctype='Hub Message', |
|
||||
reference_doctype='Lead', |
|
||||
data=json.dumps(doc), |
|
||||
user=contact.email_id |
|
||||
)) |
|
||||
|
|
||||
connection = get_hub_connection() |
|
||||
response = connection.insert('Hub Message', args) |
|
||||
|
|
||||
return response.ok |
|
@ -1,19 +0,0 @@ |
|||||
from __future__ import unicode_literals |
|
||||
|
|
||||
import frappe |
|
||||
|
|
||||
|
|
||||
def execute(): |
|
||||
for dt, dn in (("Page", "Hub"), ("DocType", "Hub Settings"), ("DocType", "Hub Category")): |
|
||||
frappe.delete_doc(dt, dn, ignore_missing=True) |
|
||||
|
|
||||
if frappe.db.exists("DocType", "Data Migration Plan"): |
|
||||
data_migration_plans = frappe.get_all("Data Migration Plan", filters={"module": 'Hub Node'}) |
|
||||
for plan in data_migration_plans: |
|
||||
plan_doc = frappe.get_doc("Data Migration Plan", plan.name) |
|
||||
for m in plan_doc.get("mappings"): |
|
||||
frappe.delete_doc("Data Migration Mapping", m.mapping, force=True) |
|
||||
docs = frappe.get_all("Data Migration Run", filters={"data_migration_plan": plan.name}) |
|
||||
for doc in docs: |
|
||||
frappe.delete_doc("Data Migration Run", doc.name) |
|
||||
frappe.delete_doc("Data Migration Plan", plan.name) |
|
@ -1,8 +0,0 @@ |
|||||
from __future__ import unicode_literals |
|
||||
|
|
||||
import frappe |
|
||||
|
|
||||
|
|
||||
def execute(): |
|
||||
frappe.reload_doc('stock', 'doctype', 'item') |
|
||||
frappe.db.sql("""update `tabItem` set publish_in_hub = 0""") |
|
@ -1,8 +0,0 @@ |
|||||
from __future__ import unicode_literals |
|
||||
|
|
||||
import frappe |
|
||||
|
|
||||
|
|
||||
def execute(): |
|
||||
frappe.reload_doc('hub_node', 'doctype', 'Marketplace Settings') |
|
||||
frappe.db.set_value('Marketplace Settings', 'Marketplace Settings', 'marketplace_url', 'https://hubmarket.org') |
|
@ -1,14 +0,0 @@ |
|||||
from __future__ import unicode_literals |
|
||||
|
|
||||
import frappe |
|
||||
|
|
||||
|
|
||||
def execute(): |
|
||||
frappe.reload_doc("Hub Node", "doctype", "Hub Tracked Item") |
|
||||
if not frappe.db.a_row_exists("Hub Tracked Item"): |
|
||||
return |
|
||||
|
|
||||
frappe.db.sql(''' |
|
||||
Update `tabHub Tracked Item` |
|
||||
SET published = 1 |
|
||||
''') |
|
@ -0,0 +1,10 @@ |
|||||
|
import frappe |
||||
|
|
||||
|
|
||||
|
def execute(): |
||||
|
|
||||
|
doctypes = frappe.get_all("DocType", {"module": "Hub Node", "custom": 0}, pluck='name') |
||||
|
for doctype in doctypes: |
||||
|
frappe.delete_doc("DocType", doctype, ignore_missing=True) |
||||
|
|
||||
|
frappe.delete_doc("Module Def", "Hub Node", ignore_missing=True, force=True) |
Before Width: | Height: | Size: 8.2 KiB |
@ -1,119 +0,0 @@ |
|||||
<template> |
|
||||
<div class="hub-page-container"> |
|
||||
<component :is="current_page.component" :key="current_page.key"></component> |
|
||||
</div> |
|
||||
</template> |
|
||||
|
|
||||
<script> |
|
||||
|
|
||||
import Home from './pages/Home.vue'; |
|
||||
import Search from './pages/Search.vue'; |
|
||||
import Category from './pages/Category.vue'; |
|
||||
import SavedItems from './pages/SavedItems.vue'; |
|
||||
import FeaturedItems from './pages/FeaturedItems.vue'; |
|
||||
import PublishedItems from './pages/PublishedItems.vue'; |
|
||||
import Item from './pages/Item.vue'; |
|
||||
import Seller from './pages/Seller.vue'; |
|
||||
import SellerItems from './pages/SellerItems.vue'; |
|
||||
import Publish from './pages/Publish.vue'; |
|
||||
import Buying from './pages/Buying.vue'; |
|
||||
import Selling from './pages/Selling.vue'; |
|
||||
import Messages from './pages/Messages.vue'; |
|
||||
import NotFound from './pages/NotFound.vue'; |
|
||||
|
|
||||
function get_route_map() { |
|
||||
const read_only_routes = { |
|
||||
'marketplace/home': Home, |
|
||||
'marketplace/search/:category/:keyword': Search, |
|
||||
'marketplace/category/:category': Category, |
|
||||
'marketplace/item/:item': Item, |
|
||||
'marketplace/seller/:seller': Seller, |
|
||||
'marketplace/seller/:seller/items': SellerItems, |
|
||||
'marketplace/not-found': NotFound, |
|
||||
} |
|
||||
const registered_routes = { |
|
||||
'marketplace/profile': Seller, |
|
||||
'marketplace/saved-items': SavedItems, |
|
||||
'marketplace/featured-items': FeaturedItems, |
|
||||
'marketplace/publish': Publish, |
|
||||
'marketplace/published-items': PublishedItems, |
|
||||
'marketplace/buying': Buying, |
|
||||
'marketplace/buying/:item': Messages, |
|
||||
'marketplace/selling': Selling, |
|
||||
'marketplace/selling/:buyer/:item': Messages |
|
||||
} |
|
||||
|
|
||||
return hub.is_seller_registered() |
|
||||
? Object.assign({}, read_only_routes, registered_routes) |
|
||||
: read_only_routes; |
|
||||
} |
|
||||
|
|
||||
export default { |
|
||||
data() { |
|
||||
return { |
|
||||
current_page: this.get_current_page() |
|
||||
} |
|
||||
}, |
|
||||
mounted() { |
|
||||
frappe.route.on('change', () => { |
|
||||
if (frappe.get_route()[0] === 'marketplace') { |
|
||||
this.set_current_page(); |
|
||||
frappe.utils.scroll_to(0); |
|
||||
} |
|
||||
}); |
|
||||
}, |
|
||||
methods: { |
|
||||
set_current_page() { |
|
||||
this.current_page = this.get_current_page(); |
|
||||
}, |
|
||||
get_current_page() { |
|
||||
const route_map = get_route_map(); |
|
||||
const curr_route = frappe.get_route_str(); |
|
||||
let route = Object.keys(route_map).filter(route => route == curr_route)[0]; |
|
||||
if (!route) { |
|
||||
// find route by matching it with dynamic part |
|
||||
const curr_route_parts = curr_route.split('/'); |
|
||||
const weighted_routes = Object.keys(route_map) |
|
||||
.map(route_str => route_str.split('/')) |
|
||||
.filter(route_parts => route_parts.length === curr_route_parts.length) |
|
||||
.reduce((obj, route_parts) => { |
|
||||
const key = route_parts.join('/'); |
|
||||
let weight = 0; |
|
||||
route_parts.forEach((part, i) => { |
|
||||
const curr_route_part = curr_route_parts[i]; |
|
||||
if (part === curr_route_part || part.includes(':')) { |
|
||||
weight += 1; |
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
obj[key] = weight; |
|
||||
return obj; |
|
||||
}, {}); |
|
||||
|
|
||||
// get the route with the highest weight |
|
||||
for (let key in weighted_routes) { |
|
||||
const route_weight = weighted_routes[key]; |
|
||||
if (route_weight === curr_route_parts.length) { |
|
||||
route = key; |
|
||||
break; |
|
||||
} else { |
|
||||
route = null; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
if (!route) { |
|
||||
return { |
|
||||
key: 'not-found', |
|
||||
component: NotFound |
|
||||
}; |
|
||||
} |
|
||||
|
|
||||
return { |
|
||||
key: curr_route, |
|
||||
component: route_map[route] |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
</script> |
|
@ -1,110 +0,0 @@ |
|||||
<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 is-title bold text-muted"> |
|
||||
{{ __('Categories') }} |
|
||||
</li> |
|
||||
<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 { |
|
||||
hub_registered: hub.is_user_registered(), |
|
||||
items: [ |
|
||||
{ |
|
||||
label: __('Browse'), |
|
||||
route: 'marketplace/home' |
|
||||
}, |
|
||||
{ |
|
||||
label: __('Saved Items'), |
|
||||
route: 'marketplace/saved-items', |
|
||||
condition: () => this.hub_registered |
|
||||
}, |
|
||||
{ |
|
||||
label: __('Your Featured Items'), |
|
||||
route: 'marketplace/featured-items', |
|
||||
condition: () => this.hub_registered |
|
||||
}, |
|
||||
{ |
|
||||
label: __('Your Profile'), |
|
||||
route: 'marketplace/profile', |
|
||||
condition: () => this.hub_registered |
|
||||
}, |
|
||||
{ |
|
||||
label: __('Your Items'), |
|
||||
route: 'marketplace/published-items', |
|
||||
condition: () => this.hub_registered |
|
||||
}, |
|
||||
{ |
|
||||
label: __('Publish Items'), |
|
||||
route: 'marketplace/publish', |
|
||||
condition: () => this.hub_registered |
|
||||
}, |
|
||||
{ |
|
||||
label: __('Selling'), |
|
||||
route: 'marketplace/selling', |
|
||||
condition: () => this.hub_registered |
|
||||
}, |
|
||||
{ |
|
||||
label: __('Buying'), |
|
||||
route: 'marketplace/buying', |
|
||||
condition: () => this.hub_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(); |
|
||||
}); |
|
||||
}); |
|
||||
|
|
||||
erpnext.hub.on('seller-registered', () => { |
|
||||
this.hub_registered = true; |
|
||||
}) |
|
||||
}, |
|
||||
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> |
|
@ -1,39 +0,0 @@ |
|||||
<template> |
|
||||
<div> |
|
||||
<div ref="comment-input"></div> |
|
||||
<div class="level"> |
|
||||
<div class="level-left"> |
|
||||
<span class="text-muted">{{ __('Ctrl + Enter to submit') }}</span> |
|
||||
</div> |
|
||||
<div class="level-right"> |
|
||||
<button class="btn btn-primary btn-xs" @click="submit_input">{{ __('Submit') }}</button> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
</template> |
|
||||
<script> |
|
||||
export default { |
|
||||
mounted() { |
|
||||
this.make_input(); |
|
||||
}, |
|
||||
methods: { |
|
||||
make_input() { |
|
||||
this.message_input = frappe.ui.form.make_control({ |
|
||||
parent: this.$refs['comment-input'], |
|
||||
on_submit: (message) => { |
|
||||
this.message_input.reset(); |
|
||||
this.$emit('change', message); |
|
||||
}, |
|
||||
only_input: true, |
|
||||
no_wrapper: true |
|
||||
}); |
|
||||
}, |
|
||||
submit_input() { |
|
||||
if (!this.message_input) return; |
|
||||
const value = this.message_input.get_value(); |
|
||||
if (!value) return; |
|
||||
this.message_input.submit(); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
</script> |
|
@ -1,26 +0,0 @@ |
|||||
<template> |
|
||||
<p class="text-muted" v-if="!Array.isArray(this.header_items)" v-html="header_items"></p> |
|
||||
<p class="text-muted" v-else> |
|
||||
<span v-for="(header_item , index) in header_items" :key="index"> |
|
||||
<span v-if="index" v-html="spacer"></span> |
|
||||
<span v-if="typeof(header_item) == 'string'" v-html="header_item"></span> |
|
||||
<a v-else-if="typeof(header_item) == 'object'" @click="header_item.on_click(header_item.value)" v-html="header_item.value"></a> |
|
||||
</span> |
|
||||
</p> |
|
||||
</template> |
|
||||
|
|
||||
<script> |
|
||||
|
|
||||
const spacer = '<span aria-hidden="true"> · </span>'; |
|
||||
|
|
||||
export default { |
|
||||
name: 'detail-header-item', |
|
||||
props: ['value'], |
|
||||
data() { |
|
||||
return { |
|
||||
header_items: this.value, |
|
||||
spacer: spacer |
|
||||
} |
|
||||
}, |
|
||||
} |
|
||||
</script> |
|
@ -1,86 +0,0 @@ |
|||||
<template> |
|
||||
<div class="hub-item-container"> |
|
||||
<div class="row visible-xs"> |
|
||||
<div class="col-xs-12 margin-bottom"> |
|
||||
<button class="btn btn-xs btn-default" data-route="marketplace/home">{{ back_to_home_text }}</button> |
|
||||
</div> |
|
||||
</div> |
|
||||
|
|
||||
<div v-if="show_skeleton" class="row margin-bottom"> |
|
||||
<div class="col-md-3"> |
|
||||
<div class="hub-item-skeleton-image"></div> |
|
||||
</div> |
|
||||
<div class="col-md-6"> |
|
||||
<h2 class="hub-skeleton" style="width: 75%;">Name</h2> |
|
||||
<div class="text-muted"> |
|
||||
<p class="hub-skeleton" style="width: 35%;">Details</p> |
|
||||
<p class="hub-skeleton" style="width: 50%;">Ratings</p> |
|
||||
</div> |
|
||||
<hr> |
|
||||
<div class="hub-item-description"> |
|
||||
<p class="hub-skeleton">Desc</p> |
|
||||
<p class="hub-skeleton" style="width: 85%;">Desc</p> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
|
|
||||
<div v-else> |
|
||||
<div class="row margin-bottom"> |
|
||||
<div class="col-md-3"> |
|
||||
<div class="hub-item-image"> |
|
||||
<base-image :src="image" :alt="title" /> |
|
||||
</div> |
|
||||
</div> |
|
||||
<div class="col-md-8" style='padding-left: 30px;'> |
|
||||
<h2>{{ title }}</h2> |
|
||||
<div class="text-muted"> |
|
||||
<slot name="detail-header-item"></slot> |
|
||||
</div> |
|
||||
</div> |
|
||||
|
|
||||
<div v-if="menu_items && menu_items.length" 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" |
|
||||
:key="section.title" |
|
||||
> |
|
||||
<h6 class="col-md-12 margin-top"> |
|
||||
<b class="text-muted">{{ section.title }}</b> |
|
||||
</h6> |
|
||||
<p class="col-md-12" v-html="section.content"> |
|
||||
</p> |
|
||||
</div> |
|
||||
</div> |
|
||||
|
|
||||
</div> |
|
||||
</template> |
|
||||
|
|
||||
<script> |
|
||||
|
|
||||
export default { |
|
||||
name: 'detail-view', |
|
||||
props: ['title', 'image', 'sections', 'show_skeleton', 'menu_items'], |
|
||||
data() { |
|
||||
return { |
|
||||
back_to_home_text: __('Back to Home') |
|
||||
} |
|
||||
}, |
|
||||
computed: {} |
|
||||
} |
|
||||
</script> |
|
||||
|
|
||||
<style lang="less" scoped> |
|
||||
</style> |
|
@ -1,50 +0,0 @@ |
|||||
<template> |
|
||||
<div class="empty-state flex flex-column" |
|
||||
:class="{ 'bordered': bordered, 'align-center': centered, 'justify-center': centered }" |
|
||||
:style="{ height: height + 'px' }" |
|
||||
> |
|
||||
<p class="text-muted" v-html="message" ></p> |
|
||||
<p v-if="action"> |
|
||||
<button class="btn btn-default btn-xs" |
|
||||
@click="action.on_click" |
|
||||
> |
|
||||
{{ action.label }} |
|
||||
</button> |
|
||||
</p> |
|
||||
</div> |
|
||||
</template> |
|
||||
|
|
||||
<script> |
|
||||
|
|
||||
export default { |
|
||||
name: 'empty-state', |
|
||||
props: { |
|
||||
message: String, |
|
||||
bordered: Boolean, |
|
||||
height: Number, |
|
||||
action: Object, |
|
||||
centered: { |
|
||||
type: Boolean, |
|
||||
default: true |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
</script> |
|
||||
|
|
||||
<style lang="less"> |
|
||||
@import "../../../../../../frappe/frappe/public/less/variables.less"; |
|
||||
|
|
||||
.empty-state { |
|
||||
height: 500px; |
|
||||
} |
|
||||
|
|
||||
.empty-state.bordered { |
|
||||
border-radius: 4px; |
|
||||
border: 1px solid @border-color; |
|
||||
border-style: dashed; |
|
||||
|
|
||||
// bad, due to item card column layout, that is inner 15px margin |
|
||||
margin: 0 15px; |
|
||||
} |
|
||||
|
|
||||
</style> |
|
@ -1,40 +0,0 @@ |
|||||
<template> |
|
||||
<div class="hub-image"> |
|
||||
<img :src="src" :alt="alt" v-show="!is_loading && !is_broken"/> |
|
||||
<div class="hub-image-loading" v-if="is_loading"> |
|
||||
<span class="octicon octicon-cloud-download"></span> |
|
||||
</div> |
|
||||
<div class="hub-image-broken" v-if="is_broken"> |
|
||||
<span class="octicon octicon-file-media"></span> |
|
||||
</div> |
|
||||
</div> |
|
||||
</template> |
|
||||
<script> |
|
||||
export default { |
|
||||
name: 'Image', |
|
||||
props: ['src', 'alt'], |
|
||||
data() { |
|
||||
return { |
|
||||
is_loading: true, |
|
||||
is_broken: false |
|
||||
} |
|
||||
}, |
|
||||
created() { |
|
||||
this.handle_image(); |
|
||||
}, |
|
||||
methods: { |
|
||||
handle_image() { |
|
||||
let img = new Image(); |
|
||||
img.src = this.src; |
|
||||
|
|
||||
img.onload = () => { |
|
||||
this.is_loading = false; |
|
||||
}; |
|
||||
img.onerror = () => { |
|
||||
this.is_loading = false; |
|
||||
this.is_broken = true; |
|
||||
}; |
|
||||
} |
|
||||
} |
|
||||
}; |
|
||||
</script> |
|
@ -1,142 +0,0 @@ |
|||||
<template> |
|
||||
<div v-if="seen" class="col-md-3 col-sm-4 col-xs-6 hub-card-container"> |
|
||||
<div class="hub-card" |
|
||||
@click="on_click(item_id)" |
|
||||
> |
|
||||
<div class="hub-card-header flex justify-between"> |
|
||||
<div class="ellipsis" :style="{ width: '85%' }"> |
|
||||
<div class="hub-card-title ellipsis bold">{{ title }}</div> |
|
||||
<div class="hub-card-subtitle ellipsis text-muted" v-html='subtitle'></div> |
|
||||
</div> |
|
||||
<i v-if="allow_clear" |
|
||||
class="octicon octicon-x text-extra-muted" |
|
||||
@click.stop="$emit('remove-item', item_id)" |
|
||||
> |
|
||||
</i> |
|
||||
</div> |
|
||||
<div class="hub-card-body"> |
|
||||
<base-image class="hub-card-image" :src="item.image" :alt="title" /> |
|
||||
<div class="hub-card-overlay"> |
|
||||
<div v-if="is_local" class="hub-card-overlay-body"> |
|
||||
<div class="hub-card-overlay-button"> |
|
||||
<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', 'item_id_fieldname', 'is_local', 'on_click', 'allow_clear', 'seen'], |
|
||||
computed: { |
|
||||
title() { |
|
||||
const item_name = this.item.item_name || this.item.name; |
|
||||
return strip_html(item_name); |
|
||||
}, |
|
||||
subtitle() { |
|
||||
const dot_spacer = '<span aria-hidden="true"> · </span>'; |
|
||||
if(this.is_local){ |
|
||||
return comment_when(this.item.creation); |
|
||||
} else { |
|
||||
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.join(dot_spacer); |
|
||||
} |
|
||||
}, |
|
||||
item_id() { |
|
||||
return this.item[this.item_id_fieldname]; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
</script> |
|
||||
|
|
||||
<style lang="less" scoped> |
|
||||
@import "../../../../../../frappe/frappe/public/less/variables.less"; |
|
||||
|
|
||||
.hub-card { |
|
||||
margin-bottom: 25px; |
|
||||
position: relative; |
|
||||
border: 1px solid @border-color; |
|
||||
border-radius: 4px; |
|
||||
overflow: hidden; |
|
||||
cursor: pointer; |
|
||||
|
|
||||
&:hover .hub-card-overlay { |
|
||||
display: block; |
|
||||
} |
|
||||
|
|
||||
.octicon-x { |
|
||||
display: block; |
|
||||
font-size: 20px; |
|
||||
margin-left: 10px; |
|
||||
cursor: pointer; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
.hub-card.closable { |
|
||||
.octicon-x { |
|
||||
display: block; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
.hub-card.is-local { |
|
||||
&.active { |
|
||||
.hub-card-header { |
|
||||
background-color: #f4ffe5; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
.hub-card-header { |
|
||||
position: relative; |
|
||||
padding: 12px 15px; |
|
||||
height: 60px; |
|
||||
border-bottom: 1px solid @border-color; |
|
||||
} |
|
||||
|
|
||||
.hub-card-body { |
|
||||
position: relative; |
|
||||
height: 200px; |
|
||||
} |
|
||||
|
|
||||
.hub-card-overlay { |
|
||||
display: none; |
|
||||
position: absolute; |
|
||||
top: 0; |
|
||||
width: 100%; |
|
||||
height: 100%; |
|
||||
background-color: rgba(0, 0, 0, 0.05); |
|
||||
} |
|
||||
|
|
||||
.hub-card-overlay-body { |
|
||||
position: relative; |
|
||||
height: 100%; |
|
||||
} |
|
||||
|
|
||||
.hub-card-overlay-button { |
|
||||
position: absolute; |
|
||||
right: 15px; |
|
||||
bottom: 15px; |
|
||||
} |
|
||||
|
|
||||
.hub-card-image { |
|
||||
width: 100%; |
|
||||
height: 100%; |
|
||||
object-fit: contain; |
|
||||
} |
|
||||
|
|
||||
</style> |
|
@ -1,62 +0,0 @@ |
|||||
<template> |
|
||||
<div class="item-cards-container"> |
|
||||
<empty-state |
|
||||
v-if="items.length === 0" |
|
||||
:message="empty_state_message" |
|
||||
:action="empty_state_action" |
|
||||
:bordered="true" |
|
||||
:height="empty_state_height" |
|
||||
/> |
|
||||
<item-card |
|
||||
v-for="item in items" |
|
||||
:key="container_name + '_' +item[item_id_fieldname]" |
|
||||
:item="item" |
|
||||
:item_id_fieldname="item_id_fieldname" |
|
||||
:is_local="is_local" |
|
||||
:on_click="on_click" |
|
||||
:allow_clear="editable" |
|
||||
:seen="item.hasOwnProperty('seen') ? item.seen : true" |
|
||||
@remove-item="$emit('remove-item', item[item_id_fieldname])" |
|
||||
> |
|
||||
</item-card> |
|
||||
</div> |
|
||||
</template> |
|
||||
|
|
||||
<script> |
|
||||
import ItemCard from './ItemCard.vue'; |
|
||||
import EmptyState from './EmptyState.vue'; |
|
||||
|
|
||||
export default { |
|
||||
name: 'item-cards-container', |
|
||||
props: { |
|
||||
container_name: String, |
|
||||
items: Array, |
|
||||
item_id_fieldname: String, |
|
||||
is_local: Boolean, |
|
||||
on_click: Function, |
|
||||
editable: Boolean, |
|
||||
|
|
||||
empty_state_message: String, |
|
||||
empty_state_action: Object, |
|
||||
empty_state_height: Number, |
|
||||
empty_state_bordered: Boolean |
|
||||
}, |
|
||||
components: { |
|
||||
ItemCard, |
|
||||
EmptyState |
|
||||
}, |
|
||||
watch: { |
|
||||
items() { |
|
||||
// TODO: handling doesn't work |
|
||||
frappe.dom.handle_broken_images($(this.$el)); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
</script> |
|
||||
|
|
||||
<style scoped> |
|
||||
.item-cards-container { |
|
||||
margin: 0 -15px; |
|
||||
overflow: overlay; |
|
||||
} |
|
||||
</style> |
|
@ -1,21 +0,0 @@ |
|||||
<template> |
|
||||
<div class="hub-list-item" :data-route="item.route"> |
|
||||
<div class="hub-list-left"> |
|
||||
<base-image class="hub-list-image" :src="item.image" /> |
|
||||
<div class="hub-list-body ellipsis"> |
|
||||
<div class="hub-list-title">{{item.item_name}}</div> |
|
||||
<div class="hub-list-subtitle ellipsis"> |
|
||||
<slot name="subtitle"></slot> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
<div class="hub-list-right" v-if="message"> |
|
||||
<span class="text-muted" v-html="frappe.datetime.comment_when(message.creation, true)" /> |
|
||||
</div> |
|
||||
</div> |
|
||||
</template> |
|
||||
<script> |
|
||||
export default { |
|
||||
props: ['item', 'message'] |
|
||||
} |
|
||||
</script> |
|
@ -1,38 +0,0 @@ |
|||||
<template> |
|
||||
<div v-if="message" class="subpage-message"> |
|
||||
<p class="text-muted flex"> |
|
||||
<span v-html="message"></span> |
|
||||
<i class="octicon octicon-x text-extra-muted" |
|
||||
@click="$emit('remove-message')" |
|
||||
> |
|
||||
</i> |
|
||||
</p> |
|
||||
</div> |
|
||||
</template> |
|
||||
|
|
||||
<script> |
|
||||
|
|
||||
export default { |
|
||||
name: 'notification-message', |
|
||||
props: { |
|
||||
message: String, |
|
||||
} |
|
||||
} |
|
||||
</script> |
|
||||
|
|
||||
<style lang="less" scoped> |
|
||||
.subpage-message { |
|
||||
p { |
|
||||
padding: 10px 15px; |
|
||||
margin-top: 0px; |
|
||||
margin-bottom: 15px; |
|
||||
background-color: #f9fbf7; |
|
||||
border-radius: 4px; |
|
||||
justify-content: space-between; |
|
||||
} |
|
||||
|
|
||||
.octicon-x { |
|
||||
cursor: pointer; |
|
||||
} |
|
||||
} |
|
||||
</style> |
|
@ -1,16 +0,0 @@ |
|||||
<template> |
|
||||
<span> |
|
||||
<i v-for="index in max_rating" |
|
||||
:key="index" |
|
||||
class="fa fa-fw star-icon" |
|
||||
:class="{'fa-star': index <= rating, 'fa-star-o': index > rating}" |
|
||||
> |
|
||||
</i> |
|
||||
</span> |
|
||||
</template> |
|
||||
|
|
||||
<script> |
|
||||
export default { |
|
||||
props: ['rating', 'max_rating'] |
|
||||
} |
|
||||
</script> |
|
@ -1,140 +0,0 @@ |
|||||
<template> |
|
||||
<div> |
|
||||
<div class="timeline-head"> |
|
||||
<div class="comment-input-wrapper"> |
|
||||
<div class="comment-input-header"> |
|
||||
<span class="text-muted">{{ __('Add your review') }}</span> |
|
||||
<div class="btn btn-default btn-xs pull-right" |
|
||||
@click="on_submit_review" |
|
||||
:disabled="!(user_review.rating && user_review.subject)" |
|
||||
> |
|
||||
{{ __('Submit Review') }} |
|
||||
</div> |
|
||||
</div> |
|
||||
<div class="comment-input-container"> |
|
||||
<div class="rating-area text-muted"> |
|
||||
<span>{{ __('Your rating:') }}</span> |
|
||||
<div |
|
||||
v-for="i in [1, 2, 3, 4, 5]" |
|
||||
:key="i" |
|
||||
:class="['fa fa-fw', user_review.rating < i ? 'fa-star-o' : 'fa-star']" |
|
||||
:data-index="i" |
|
||||
@click="set_rating(i)" |
|
||||
> |
|
||||
</div> |
|
||||
</div> |
|
||||
<div class="comment-input-body margin-top" v-show="user_review.rating"> |
|
||||
<input |
|
||||
type="text" |
|
||||
placeholder="Subject" |
|
||||
class="form-control margin-bottom" |
|
||||
style="border-color: #ebeff2" |
|
||||
v-model="user_review.subject" |
|
||||
> |
|
||||
<div ref="review-content"></div> |
|
||||
<div> |
|
||||
<span class="text-muted text-small">{{ __('Ctrl+Enter to submit') }}</span> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
<div class="timeline-items"> |
|
||||
<review-timeline-item v-for="review in reviews" |
|
||||
:key="review.user" |
|
||||
:username="review.username" |
|
||||
:avatar="review.user_image" |
|
||||
:comment_when="when(review.modified)" |
|
||||
:rating="review.rating" |
|
||||
:subject="review.subject" |
|
||||
:content="review.content" |
|
||||
> |
|
||||
</review-timeline-item> |
|
||||
</div> |
|
||||
</div> |
|
||||
</template> |
|
||||
<script> |
|
||||
import ReviewTimelineItem from '../components/ReviewTimelineItem.vue'; |
|
||||
|
|
||||
export default { |
|
||||
props: ['hub_item_name'], |
|
||||
data() { |
|
||||
return { |
|
||||
user_review: { |
|
||||
rating: 0, |
|
||||
subject: '', |
|
||||
content: '' |
|
||||
}, |
|
||||
reviews: [] |
|
||||
} |
|
||||
}, |
|
||||
components: { |
|
||||
ReviewTimelineItem |
|
||||
}, |
|
||||
created() { |
|
||||
this.get_item_reviews(); |
|
||||
}, |
|
||||
mounted() { |
|
||||
this.make_input(); |
|
||||
}, |
|
||||
methods: { |
|
||||
set_rating(i) { |
|
||||
this.user_review.rating = i; |
|
||||
}, |
|
||||
|
|
||||
when(datetime) { |
|
||||
return comment_when(datetime); |
|
||||
}, |
|
||||
|
|
||||
get_item_reviews() { |
|
||||
hub.call('get_item_reviews', { hub_item_name: this.hub_item_name }) |
|
||||
.then(reviews => { |
|
||||
this.reviews = reviews; |
|
||||
}) |
|
||||
.catch(() => {}); |
|
||||
}, |
|
||||
|
|
||||
make_input() { |
|
||||
this.review_content = frappe.ui.form.make_control({ |
|
||||
parent: this.$refs['review-content'], |
|
||||
on_submit: this.on_submit_review.bind(this), |
|
||||
no_wrapper: true, |
|
||||
only_input: true, |
|
||||
render_input: true, |
|
||||
df: { |
|
||||
fieldtype: 'Comment', |
|
||||
fieldname: 'comment' |
|
||||
} |
|
||||
}); |
|
||||
}, |
|
||||
|
|
||||
on_submit_review() { |
|
||||
const review = Object.assign({}, this.user_review, { |
|
||||
content: this.review_content.get_value() |
|
||||
}); |
|
||||
|
|
||||
if (!hub.is_seller_registered()) { |
|
||||
frappe.throw(__('You need to login as a Marketplace User before you can add any reviews.')); |
|
||||
} |
|
||||
|
|
||||
hub.call('add_item_review', { |
|
||||
hub_item_name: this.hub_item_name, |
|
||||
review: JSON.stringify(review) |
|
||||
}) |
|
||||
.then(this.push_review.bind(this)); |
|
||||
|
|
||||
this.reset_user_review(); |
|
||||
}, |
|
||||
|
|
||||
reset_user_review() { |
|
||||
this.user_review.rating = 0; |
|
||||
this.user_review.subject = ''; |
|
||||
this.review_content.set_value(''); |
|
||||
}, |
|
||||
|
|
||||
push_review(review){ |
|
||||
this.reviews.unshift(review); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
</script> |
|
@ -1,53 +0,0 @@ |
|||||
<template> |
|
||||
<div class="media timeline-item user-content" data-doctype="${''}" data-name="${''}"> |
|
||||
<span class="pull-left avatar avatar-medium hidden-xs" style="margin-top: 1px"> |
|
||||
<!-- ${image_html} --> |
|
||||
</span> |
|
||||
<div class="pull-left media-body"> |
|
||||
<div class="media-content-wrapper"> |
|
||||
<div class="action-btns"> |
|
||||
<!-- ${edit_html} --> |
|
||||
</div> |
|
||||
|
|
||||
<div class="comment-header clearfix"> |
|
||||
<span class="pull-left avatar avatar-small visible-xs"> |
|
||||
<!-- ${image_html} --> |
|
||||
</span> |
|
||||
|
|
||||
<div class="asset-details"> |
|
||||
<span class="author-wrap"> |
|
||||
<i class="octicon octicon-quote hidden-xs fa-fw"></i> |
|
||||
<span> |
|
||||
{{ username }} |
|
||||
</span> |
|
||||
</span> |
|
||||
<a class="text-muted"> |
|
||||
<span class="text-muted hidden-xs">–</span> |
|
||||
<span class="hidden-xs" v-html="comment_when"></span> |
|
||||
</a> |
|
||||
</div> |
|
||||
</div> |
|
||||
<div class="reply timeline-content-show"> |
|
||||
<div class="timeline-item-content"> |
|
||||
<p class="text-muted"> |
|
||||
<rating :rating="rating" :max_rating="5"></rating> |
|
||||
</p> |
|
||||
<h6 class="bold">{{ subject }}</h6> |
|
||||
<p class="text-muted" v-html="content"></p> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
</template> |
|
||||
|
|
||||
<script> |
|
||||
import Rating from '../components/Rating.vue'; |
|
||||
|
|
||||
export default { |
|
||||
props: ['username', 'comment_when', 'avatar', 'rating', 'subject', 'content'], |
|
||||
components: { |
|
||||
Rating |
|
||||
} |
|
||||
} |
|
||||
</script> |
|
@ -1,26 +0,0 @@ |
|||||
<template> |
|
||||
<div class="hub-search-container"> |
|
||||
<input |
|
||||
type="text" |
|
||||
class="form-control" |
|
||||
:placeholder="placeholder" |
|
||||
:value="value" |
|
||||
@keydown.enter="on_input"> |
|
||||
</div> |
|
||||
</template> |
|
||||
|
|
||||
<script> |
|
||||
export default { |
|
||||
props: { |
|
||||
placeholder: String, |
|
||||
value: String, |
|
||||
on_search: Function |
|
||||
}, |
|
||||
methods: { |
|
||||
on_input(event) { |
|
||||
this.$emit('input', event.target.value); |
|
||||
this.on_search(); |
|
||||
} |
|
||||
} |
|
||||
}; |
|
||||
</script> |
|
@ -1,3 +0,0 @@ |
|||||
<template> |
|
||||
<div class="hub-items-header level"><slot></slot></div> |
|
||||
</template> |
|
@ -1,9 +0,0 @@ |
|||||
/* Saving this for later */ |
|
||||
<template> |
|
||||
<div class="media timeline-item notification-content"> |
|
||||
<div class="small"> |
|
||||
<i class="octicon octicon-bookmark fa-fw"></i> |
|
||||
<span title="Administrator"><b>4 weeks ago</b> Published 1 item to Marketplace</span> |
|
||||
</div> |
|
||||
</div> |
|
||||
</template> |
|
@ -1,41 +0,0 @@ |
|||||
function edit_details_dialog(params) { |
|
||||
let dialog = new frappe.ui.Dialog({ |
|
||||
title: __('Update Details'), |
|
||||
fields: [ |
|
||||
{ |
|
||||
label: 'Item Name', |
|
||||
fieldname: 'item_name', |
|
||||
fieldtype: 'Data', |
|
||||
default: params.defaults.item_name, |
|
||||
reqd: 1 |
|
||||
}, |
|
||||
{ |
|
||||
label: 'Hub Category', |
|
||||
fieldname: 'hub_category', |
|
||||
fieldtype: 'Autocomplete', |
|
||||
default: params.defaults.hub_category, |
|
||||
options: [], |
|
||||
reqd: 1 |
|
||||
}, |
|
||||
{ |
|
||||
label: 'Description', |
|
||||
fieldname: 'description', |
|
||||
fieldtype: 'Text', |
|
||||
default: params.defaults.description, |
|
||||
options: [], |
|
||||
reqd: 1 |
|
||||
} |
|
||||
], |
|
||||
primary_action_label: params.primary_action.label || __('Update Details'), |
|
||||
primary_action: params.primary_action.fn |
|
||||
}); |
|
||||
|
|
||||
hub.call('get_categories').then(categories => { |
|
||||
categories = categories.map(d => d.name); |
|
||||
dialog.fields_dict.hub_category.set_data(categories); |
|
||||
}); |
|
||||
|
|
||||
return dialog; |
|
||||
} |
|
||||
|
|
||||
export { edit_details_dialog }; |
|
@ -1,39 +0,0 @@ |
|||||
function ItemPublishDialog(primary_action, secondary_action) { |
|
||||
let dialog = new frappe.ui.Dialog({ |
|
||||
title: __('Edit Publishing Details'), |
|
||||
fields: [ |
|
||||
{ |
|
||||
label: __('Item Code'), |
|
||||
fieldname: 'item_code', |
|
||||
fieldtype: 'Data', |
|
||||
read_only: 1 |
|
||||
}, |
|
||||
{ |
|
||||
label: __('Hub Category'), |
|
||||
fieldname: 'hub_category', |
|
||||
fieldtype: 'Autocomplete', |
|
||||
options: [], |
|
||||
reqd: 1 |
|
||||
}, |
|
||||
{ |
|
||||
label: __('Images'), |
|
||||
fieldname: 'image_list', |
|
||||
fieldtype: 'MultiSelect', |
|
||||
options: [], |
|
||||
reqd: 1 |
|
||||
} |
|
||||
], |
|
||||
primary_action_label: primary_action.label || __('Set Details'), |
|
||||
primary_action: primary_action.fn, |
|
||||
secondary_action: secondary_action.fn |
|
||||
}); |
|
||||
|
|
||||
hub.call('get_categories').then(categories => { |
|
||||
categories = categories.map(d => d.name); |
|
||||
dialog.fields_dict.hub_category.set_data(categories); |
|
||||
}); |
|
||||
|
|
||||
return dialog; |
|
||||
} |
|
||||
|
|
||||
export { ItemPublishDialog }; |
|
@ -1,56 +0,0 @@ |
|||||
const ProfileDialog = (title = __('Edit Profile'), action={}) => { |
|
||||
const fields = [ |
|
||||
{ |
|
||||
fieldtype: 'Link', |
|
||||
fieldname: 'company', |
|
||||
label: __('Company'), |
|
||||
options: 'Company' |
|
||||
}, |
|
||||
{ |
|
||||
fieldtype: 'Read Only', |
|
||||
fieldname: 'email', |
|
||||
label: __('Email') |
|
||||
}, |
|
||||
{ |
|
||||
label: __('About your company'), |
|
||||
fieldname: 'company_description', |
|
||||
fieldtype: 'Text' |
|
||||
} |
|
||||
]; |
|
||||
|
|
||||
let dialog = new frappe.ui.Dialog({ |
|
||||
title: title, |
|
||||
fields: fields, |
|
||||
primary_action_label: action.label || __('Update'), |
|
||||
primary_action: () => { |
|
||||
const form_values = dialog.get_values(); |
|
||||
let values_filled = true; |
|
||||
|
|
||||
// TODO: Say "we notice that the company description and logo isn't set. Please set them in master."
|
|
||||
// Only then allow to register
|
|
||||
|
|
||||
const mandatory_fields = ['company', 'company_description']; |
|
||||
mandatory_fields.forEach(field => { |
|
||||
const value = form_values[field]; |
|
||||
if (!value) { |
|
||||
dialog.set_df_property(field, 'reqd', 1); |
|
||||
values_filled = false; |
|
||||
} |
|
||||
}); |
|
||||
if (!values_filled) return; |
|
||||
|
|
||||
action.on_submit(form_values); |
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
// Post create
|
|
||||
const default_company = frappe.defaults.get_default('company'); |
|
||||
dialog.set_value('company', default_company); |
|
||||
dialog.set_value('email', frappe.session.user); |
|
||||
|
|
||||
return dialog; |
|
||||
} |
|
||||
|
|
||||
export { |
|
||||
ProfileDialog |
|
||||
} |
|
@ -1,80 +0,0 @@ |
|||||
function get_review_html(review) { |
|
||||
let username = review.username || review.user || __("Anonymous"); |
|
||||
|
|
||||
let image_html = review.user_image |
|
||||
? `<div class="avatar-frame" style="background-image: url(${review.user_image})"></div>` |
|
||||
: `<div class="standard-image" style="background-color: #fafbfc">${frappe.get_abbr(username)}</div>` |
|
||||
|
|
||||
let edit_html = review.own |
|
||||
? `<div class="pull-right hidden-xs close-btn-container">
|
|
||||
<span class="small text-muted"> |
|
||||
${'data.delete'} |
|
||||
</span> |
|
||||
</div> |
|
||||
<div class="pull-right edit-btn-container"> |
|
||||
<span class="small text-muted"> |
|
||||
${'data.edit'} |
|
||||
</span> |
|
||||
</div>` |
|
||||
: ''; |
|
||||
|
|
||||
let rating_html = get_rating_html(review.rating); |
|
||||
|
|
||||
return get_timeline_item(review, image_html, edit_html, rating_html); |
|
||||
} |
|
||||
|
|
||||
function get_timeline_item(data, image_html, edit_html, rating_html) { |
|
||||
return `<div class="media timeline-item user-content" data-doctype="${''}" data-name="${''}">
|
|
||||
<span class="pull-left avatar avatar-medium hidden-xs" style="margin-top: 1px"> |
|
||||
${image_html} |
|
||||
</span> |
|
||||
<div class="pull-left media-body"> |
|
||||
<div class="media-content-wrapper"> |
|
||||
<div class="action-btns">${edit_html}</div> |
|
||||
|
|
||||
<div class="comment-header clearfix"> |
|
||||
<span class="pull-left avatar avatar-small visible-xs"> |
|
||||
${image_html} |
|
||||
</span> |
|
||||
|
|
||||
<div class="asset-details"> |
|
||||
<span class="author-wrap"> |
|
||||
<i class="octicon octicon-quote hidden-xs fa-fw"></i> |
|
||||
<span>${data.username}</span> |
|
||||
</span> |
|
||||
<a class="text-muted"> |
|
||||
<span class="text-muted hidden-xs">–</span> |
|
||||
<span class="hidden-xs">${comment_when(data.modified)}</span> |
|
||||
</a> |
|
||||
</div> |
|
||||
</div> |
|
||||
<div class="reply timeline-content-show"> |
|
||||
<div class="timeline-item-content"> |
|
||||
<p class="text-muted"> |
|
||||
${rating_html} |
|
||||
</p> |
|
||||
<h6 class="bold">${data.subject}</h6> |
|
||||
<p class="text-muted"> |
|
||||
${data.content} |
|
||||
</p> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div>`; |
|
||||
} |
|
||||
|
|
||||
function get_rating_html(rating) { |
|
||||
let rating_html = ``; |
|
||||
for (var i = 0; i < 5; i++) { |
|
||||
let star_class = 'fa-star'; |
|
||||
if (i >= rating) star_class = 'fa-star-o'; |
|
||||
rating_html += `<i class='fa fa-fw ${star_class} star-icon' data-index=${i}></i>`; |
|
||||
} |
|
||||
return rating_html; |
|
||||
} |
|
||||
|
|
||||
export { |
|
||||
get_review_html, |
|
||||
get_rating_html |
|
||||
} |
|
@ -1,68 +0,0 @@ |
|||||
frappe.provide('hub'); |
|
||||
frappe.provide('erpnext.hub'); |
|
||||
|
|
||||
erpnext.hub.cache = {}; |
|
||||
hub.call = function call_hub_method(method, args={}, clear_cache_on_event) { // eslint-disable-line
|
|
||||
return new Promise((resolve, reject) => { |
|
||||
|
|
||||
// cache
|
|
||||
const key = method + JSON.stringify(args); |
|
||||
if (erpnext.hub.cache[key]) { |
|
||||
resolve(erpnext.hub.cache[key]); |
|
||||
} |
|
||||
|
|
||||
// cache invalidation
|
|
||||
const clear_cache = () => delete erpnext.hub.cache[key]; |
|
||||
|
|
||||
if (!clear_cache_on_event) { |
|
||||
invalidate_after_5_mins(clear_cache); |
|
||||
} else { |
|
||||
erpnext.hub.on(clear_cache_on_event, () => { |
|
||||
clear_cache(key); |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
let res; |
|
||||
if (hub.is_server) { |
|
||||
res = frappe.call({ |
|
||||
method: 'hub.hub.api.' + method, |
|
||||
args |
|
||||
}); |
|
||||
} else { |
|
||||
res = frappe.call({ |
|
||||
method: 'erpnext.hub_node.api.call_hub_method', |
|
||||
args: { |
|
||||
method, |
|
||||
params: args |
|
||||
} |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
res.then(r => { |
|
||||
if (r.message) { |
|
||||
const response = r.message; |
|
||||
if (response.error) { |
|
||||
frappe.throw({ |
|
||||
title: __('Marketplace Error'), |
|
||||
message: response.error |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
erpnext.hub.cache[key] = response; |
|
||||
erpnext.hub.trigger(`response:${key}`, { response }); |
|
||||
resolve(response); |
|
||||
} |
|
||||
reject(r); |
|
||||
|
|
||||
}).fail(reject); |
|
||||
}); |
|
||||
}; |
|
||||
|
|
||||
function invalidate_after_5_mins(clear_cache) { |
|
||||
// cache invalidation after 5 minutes
|
|
||||
const timeout = 5 * 60 * 1000; |
|
||||
|
|
||||
setTimeout(() => { |
|
||||
clear_cache(); |
|
||||
}, timeout); |
|
||||
} |
|
@ -1,34 +0,0 @@ |
|||||
frappe.provide('erpnext.hub'); |
|
||||
|
|
||||
frappe.views.MarketplaceFactory = class MarketplaceFactory extends frappe.views.Factory { |
|
||||
show() { |
|
||||
is_marketplace_disabled() |
|
||||
.then(disabled => { |
|
||||
if (disabled) { |
|
||||
frappe.show_not_found('Marketplace'); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
if (frappe.pages.marketplace) { |
|
||||
frappe.container.change_to('marketplace'); |
|
||||
erpnext.hub.marketplace.refresh(); |
|
||||
} else { |
|
||||
this.make('marketplace'); |
|
||||
} |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
make(page_name) { |
|
||||
frappe.require('marketplace.bundle.js', () => { |
|
||||
erpnext.hub.marketplace = new erpnext.hub.Marketplace({ |
|
||||
parent: this.make_page(true, page_name) |
|
||||
}); |
|
||||
}); |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
function is_marketplace_disabled() { |
|
||||
return frappe.call({ |
|
||||
method: "erpnext.hub_node.doctype.marketplace_settings.marketplace_settings.is_marketplace_enabled" |
|
||||
}).then(r => r.message) |
|
||||
} |
|
@ -1,225 +0,0 @@ |
|||||
import Vue from 'vue/dist/vue.js'; |
|
||||
import './vue-plugins'; |
|
||||
|
|
||||
// components
|
|
||||
import PageContainer from './PageContainer.vue'; |
|
||||
import Sidebar from './Sidebar.vue'; |
|
||||
import { ProfileDialog } from './components/profile_dialog'; |
|
||||
|
|
||||
// helpers
|
|
||||
import './hub_call'; |
|
||||
|
|
||||
frappe.provide('hub'); |
|
||||
frappe.provide('erpnext.hub'); |
|
||||
frappe.provide('frappe.route'); |
|
||||
|
|
||||
frappe.utils.make_event_emitter(frappe.route); |
|
||||
frappe.utils.make_event_emitter(erpnext.hub); |
|
||||
|
|
||||
erpnext.hub.Marketplace = class Marketplace { |
|
||||
constructor({ parent }) { |
|
||||
this.$parent = $(parent); |
|
||||
this.page = parent.page; |
|
||||
|
|
||||
this.update_hub_settings().then(() => { |
|
||||
|
|
||||
this.setup_header(); |
|
||||
this.make_sidebar(); |
|
||||
this.make_body(); |
|
||||
this.setup_events(); |
|
||||
this.refresh(); |
|
||||
|
|
||||
if (!hub.is_server) { |
|
||||
if (!hub.is_seller_registered()) { |
|
||||
this.page.set_primary_action('Become a Seller', this.show_register_dialog.bind(this)) |
|
||||
} else { |
|
||||
this.page.set_secondary_action('Add Users', this.show_add_user_dialog.bind(this)); |
|
||||
} |
|
||||
} |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
setup_header() { |
|
||||
if (hub.is_server) return; |
|
||||
this.page.set_title(__('Marketplace')); |
|
||||
} |
|
||||
|
|
||||
setup_events() { |
|
||||
this.$parent.on('click', '[data-route]', (e) => { |
|
||||
const $target = $(e.currentTarget); |
|
||||
const route = $target.data().route; |
|
||||
frappe.set_route(route); |
|
||||
}); |
|
||||
|
|
||||
// generic action handler
|
|
||||
this.$parent.on('click', '[data-action]', e => { |
|
||||
const $target = $(e.currentTarget); |
|
||||
const action = $target.data().action; |
|
||||
|
|
||||
if (action && this[action]) { |
|
||||
this[action].apply(this, $target); |
|
||||
} |
|
||||
}) |
|
||||
} |
|
||||
|
|
||||
make_sidebar() { |
|
||||
this.$sidebar = this.$parent.find('.layout-side-section').addClass('hidden-xs'); |
|
||||
|
|
||||
new Vue({ |
|
||||
el: $('<div>').appendTo(this.$sidebar)[0], |
|
||||
render: h => h(Sidebar) |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
make_body() { |
|
||||
this.$body = this.$parent.find('.layout-main-section'); |
|
||||
this.$page_container = $('<div class="hub-page-container">').appendTo(this.$body); |
|
||||
|
|
||||
new Vue({ |
|
||||
el: '.hub-page-container', |
|
||||
render: h => h(PageContainer) |
|
||||
}); |
|
||||
|
|
||||
if (!hub.is_server) { |
|
||||
erpnext.hub.on('seller-registered', () => { |
|
||||
this.page.clear_primary_action(); |
|
||||
}); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
refresh() { |
|
||||
|
|
||||
} |
|
||||
|
|
||||
show_register_dialog() { |
|
||||
if(frappe.session.user === 'Administrator') { |
|
||||
frappe.msgprint(__('You need to be a user other than Administrator with System Manager and Item Manager roles to register on Marketplace.')); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
if (!is_subset(['System Manager', 'Item Manager'], frappe.user_roles)) { |
|
||||
frappe.msgprint(__('You need to be a user with System Manager and Item Manager roles to register on Marketplace.')); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
this.register_dialog = ProfileDialog( |
|
||||
__('Become a Seller'), |
|
||||
{ |
|
||||
label: __('Register'), |
|
||||
on_submit: this.register_marketplace.bind(this) |
|
||||
} |
|
||||
); |
|
||||
|
|
||||
this.register_dialog.show(); |
|
||||
} |
|
||||
|
|
||||
register_marketplace({company, company_description}) { |
|
||||
frappe.call({ |
|
||||
method: 'erpnext.hub_node.api.register_marketplace', |
|
||||
args: { |
|
||||
company, |
|
||||
company_description |
|
||||
} |
|
||||
}).then((r) => { |
|
||||
if (r.message && r.message.ok) { |
|
||||
this.register_dialog.hide(); |
|
||||
|
|
||||
this.update_hub_settings() |
|
||||
.then(() => { |
|
||||
frappe.set_route('marketplace', 'publish'); |
|
||||
erpnext.hub.trigger('seller-registered'); |
|
||||
}); |
|
||||
} |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
show_add_user_dialog() { |
|
||||
if (!is_subset(['System Manager', 'Item Manager'], frappe.user_roles)) { |
|
||||
frappe.msgprint(__('You need to be a user with System Manager and Item Manager roles to add users to Marketplace.')); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
this.get_unregistered_users() |
|
||||
.then(r => { |
|
||||
const user_list = r.message; |
|
||||
|
|
||||
const d = new frappe.ui.Dialog({ |
|
||||
title: __('Add Users to Marketplace'), |
|
||||
fields: [ |
|
||||
{ |
|
||||
label: __('Users'), |
|
||||
fieldname: 'users', |
|
||||
fieldtype: 'MultiSelect', |
|
||||
reqd: 1, |
|
||||
get_data() { |
|
||||
return user_list; |
|
||||
} |
|
||||
} |
|
||||
], |
|
||||
primary_action({ users }) { |
|
||||
const selected_users = users.split(',').map(d => d.trim()).filter(Boolean); |
|
||||
|
|
||||
if (!selected_users.every(user => user_list.includes(user))) { |
|
||||
d.set_df_property('users', 'description', __('Some emails are invalid')); |
|
||||
return; |
|
||||
} else { |
|
||||
d.set_df_property('users', 'description', ''); |
|
||||
} |
|
||||
|
|
||||
frappe.call('erpnext.hub_node.api.register_users', { |
|
||||
user_list: selected_users |
|
||||
}) |
|
||||
.then(r => { |
|
||||
d.hide(); |
|
||||
|
|
||||
if (r.message && r.message.length) { |
|
||||
frappe.show_alert(__('Added {0} users', [r.message.length])); |
|
||||
} |
|
||||
}); |
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
d.show(); |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
get_unregistered_users() { |
|
||||
return frappe.call('erpnext.hub_node.api.get_unregistered_users') |
|
||||
} |
|
||||
|
|
||||
update_hub_settings() { |
|
||||
return hub.get_settings().then(doc => { |
|
||||
hub.settings = doc; |
|
||||
}); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
Object.assign(hub, { |
|
||||
is_seller_registered() { |
|
||||
return hub.settings.registered; |
|
||||
}, |
|
||||
|
|
||||
is_user_registered() { |
|
||||
return this.is_seller_registered() && hub.settings.users |
|
||||
.filter(hub_user => hub_user.user === frappe.session.user) |
|
||||
.length === 1; |
|
||||
}, |
|
||||
|
|
||||
get_settings() { |
|
||||
if (frappe.session.user === 'Guest') { |
|
||||
return Promise.resolve({ |
|
||||
registered: 0 |
|
||||
}); |
|
||||
} |
|
||||
return frappe.db.get_doc('Marketplace Settings'); |
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
/** |
|
||||
* Returns true if list_a is subset of list_b |
|
||||
* @param {Array} list_a |
|
||||
* @param {Array} list_b |
|
||||
*/ |
|
||||
function is_subset(list_a, list_b) { |
|
||||
return list_a.every(item => list_b.includes(item)); |
|
||||
} |
|
@ -1,56 +0,0 @@ |
|||||
<template> |
|
||||
<div> |
|
||||
<section-header> |
|
||||
<h4>{{ __('Buying') }}</h4> |
|
||||
</section-header> |
|
||||
<div class="row" v-if="items && items.length"> |
|
||||
<div class="col-md-7 margin-bottom" |
|
||||
v-for="item of items" |
|
||||
:key="item.name" |
|
||||
> |
|
||||
<item-list-card |
|
||||
:item="item" |
|
||||
v-route="'marketplace/buying/' + item.name" |
|
||||
> |
|
||||
<div slot="subtitle"> |
|
||||
<span>{{ get_sender(item.recent_message) }}: </span> |
|
||||
<span>{{ item.recent_message.message | striphtml }}</span> |
|
||||
</div> |
|
||||
</item-list-card> |
|
||||
</div> |
|
||||
</div> |
|
||||
<empty-state v-else :message="__('This page keeps track of items you want to buy from sellers.')" :centered="false" /> |
|
||||
</div> |
|
||||
</template> |
|
||||
<script> |
|
||||
import EmptyState from '../components/EmptyState.vue'; |
|
||||
import SectionHeader from '../components/SectionHeader.vue'; |
|
||||
import ItemListCard from '../components/ItemListCard.vue'; |
|
||||
|
|
||||
export default { |
|
||||
components: { |
|
||||
SectionHeader, |
|
||||
ItemListCard, |
|
||||
EmptyState |
|
||||
}, |
|
||||
data() { |
|
||||
return { |
|
||||
items: null |
|
||||
} |
|
||||
}, |
|
||||
created() { |
|
||||
this.get_items_for_messages() |
|
||||
.then(items => { |
|
||||
this.items = items; |
|
||||
}); |
|
||||
}, |
|
||||
methods: { |
|
||||
get_items_for_messages() { |
|
||||
return hub.call('get_buying_items_for_messages', {}, 'action:send_message'); |
|
||||
}, |
|
||||
get_sender(message) { |
|
||||
return message.sender === frappe.session.user ? __('You') : (message.sender_name || message.sender); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
</script> |
|
@ -1,76 +0,0 @@ |
|||||
<template> |
|
||||
<div |
|
||||
class="marketplace-page" |
|
||||
:data-page-name="page_name" |
|
||||
> |
|
||||
<search-input |
|
||||
:placeholder="search_placeholder" |
|
||||
:on_search="set_search_route" |
|
||||
v-model="search_value" |
|
||||
/> |
|
||||
|
|
||||
<h5>{{ page_title }}</h5> |
|
||||
|
|
||||
<item-cards-container |
|
||||
:container_name="page_title" |
|
||||
:items="items" |
|
||||
:item_id_fieldname="item_id_fieldname" |
|
||||
:on_click="go_to_item_details_page" |
|
||||
:empty_state_message="empty_state_message" |
|
||||
> |
|
||||
</item-cards-container> |
|
||||
</div> |
|
||||
</template> |
|
||||
|
|
||||
<script> |
|
||||
export default { |
|
||||
data() { |
|
||||
return { |
|
||||
page_name: frappe.get_route()[1], |
|
||||
category: frappe.get_route()[2], |
|
||||
items: [], |
|
||||
item_id_fieldname: 'name', |
|
||||
|
|
||||
// Constants |
|
||||
empty_state_message: __('No items in this category yet.'), |
|
||||
|
|
||||
search_value: '', |
|
||||
|
|
||||
// Constants |
|
||||
search_placeholder: __('Search for anything ...'), |
|
||||
|
|
||||
}; |
|
||||
}, |
|
||||
computed: { |
|
||||
page_title() { |
|
||||
return __(this.category); |
|
||||
} |
|
||||
}, |
|
||||
created() { |
|
||||
this.search_value = ''; |
|
||||
this.get_items(); |
|
||||
}, |
|
||||
methods: { |
|
||||
get_items() { |
|
||||
hub.call('get_items', { |
|
||||
filters: { |
|
||||
hub_category: this.category |
|
||||
} |
|
||||
}) |
|
||||
.then((items) => { |
|
||||
this.items = items; |
|
||||
}) |
|
||||
}, |
|
||||
|
|
||||
go_to_item_details_page(hub_item_name) { |
|
||||
frappe.set_route(`marketplace/item/${hub_item_name}`); |
|
||||
}, |
|
||||
|
|
||||
set_search_route() { |
|
||||
frappe.set_route('marketplace', 'search', this.category, this.search_value); |
|
||||
}, |
|
||||
} |
|
||||
} |
|
||||
</script> |
|
||||
|
|
||||
<style scoped></style> |
|
@ -1,116 +0,0 @@ |
|||||
<template> |
|
||||
<div |
|
||||
class="marketplace-page" |
|
||||
:data-page-name="page_name" |
|
||||
> |
|
||||
<h5>{{ page_title }}</h5> |
|
||||
<p v-if="items.length" |
|
||||
class="text-muted margin-bottom"> |
|
||||
{{ __('You can Feature upto 8 items.') }} |
|
||||
</p> |
|
||||
|
|
||||
<item-cards-container |
|
||||
:container_name="page_title" |
|
||||
:items="items" |
|
||||
:item_id_fieldname="item_id_fieldname" |
|
||||
:on_click="go_to_item_details_page" |
|
||||
:editable="true" |
|
||||
@remove-item="on_item_remove" |
|
||||
:empty_state_message="empty_state_message" |
|
||||
> |
|
||||
</item-cards-container> |
|
||||
</div> |
|
||||
</template> |
|
||||
|
|
||||
<script> |
|
||||
export default { |
|
||||
name: 'featured-items-page', |
|
||||
data() { |
|
||||
return { |
|
||||
page_name: frappe.get_route()[1], |
|
||||
items: [], |
|
||||
item_id_fieldname: 'name', |
|
||||
|
|
||||
// Constants |
|
||||
page_title: __('Your Featured Items'), |
|
||||
empty_state_message: __('No featured items yet. Got to your {0} and feature up to eight items that you want to highlight to your customers.', |
|
||||
[`<a href="#marketplace/published-items">${__("Published Items")}</a>`]) |
|
||||
}; |
|
||||
}, |
|
||||
created() { |
|
||||
this.get_items(); |
|
||||
}, |
|
||||
methods: { |
|
||||
get_items() { |
|
||||
hub.call( |
|
||||
'get_featured_items_of_seller', {}, |
|
||||
'action:item_feature' |
|
||||
) |
|
||||
.then((items) => { |
|
||||
this.items = items; |
|
||||
}) |
|
||||
}, |
|
||||
|
|
||||
go_to_item_details_page(hub_item_name) { |
|
||||
frappe.set_route(`marketplace/item/${hub_item_name}`); |
|
||||
}, |
|
||||
|
|
||||
on_item_remove(hub_item_name) { |
|
||||
const grace_period = 5000; |
|
||||
let reverted = false; |
|
||||
let alert; |
|
||||
|
|
||||
const undo_remove = () => { |
|
||||
this.toggle_item(hub_item_name);; |
|
||||
reverted = true; |
|
||||
alert.hide(); |
|
||||
return false; |
|
||||
} |
|
||||
|
|
||||
const item_name = this.items.filter(item => item.hub_item_name === hub_item_name); |
|
||||
|
|
||||
alert_message = __('{0} removed. {1}', [item_name, |
|
||||
`<a href="#" data-action="undo-remove"><b>${__('Undo')}</b></a>`]); |
|
||||
alert = frappe.show_alert(alert_message, grace_period / 1000, |
|
||||
{ |
|
||||
'undo-remove': undo_remove.bind(this) |
|
||||
} |
|
||||
); |
|
||||
|
|
||||
this.toggle_item(hub_item_name, false); |
|
||||
|
|
||||
setTimeout(() => { |
|
||||
if(!reverted) { |
|
||||
this.remove_item_from_featured_items(hub_item_name); |
|
||||
} |
|
||||
}, grace_period); |
|
||||
}, |
|
||||
|
|
||||
remove_item_from_featured_items(hub_item_name) { |
|
||||
erpnext.hub.trigger('action:item_feature'); |
|
||||
hub.call('remove_item_from_seller_featured_items', { |
|
||||
hub_item_name, |
|
||||
hub_user: frappe.session.user |
|
||||
}) |
|
||||
.then(() => { |
|
||||
this.get_items(); |
|
||||
}) |
|
||||
.catch(e => { |
|
||||
console.log(e); |
|
||||
}); |
|
||||
}, |
|
||||
|
|
||||
// By default show |
|
||||
toggle_item(hub_item_name, show=true) { |
|
||||
this.items = this.items.map(item => { |
|
||||
if(item.name === hub_item_name) { |
|
||||
item.seen = show; |
|
||||
} |
|
||||
return item; |
|
||||
}); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
</script> |
|
||||
|
|
||||
<style scoped></style> |
|
@ -1,114 +0,0 @@ |
|||||
<template> |
|
||||
<div |
|
||||
class="marketplace-page" |
|
||||
:data-page-name="page_name" |
|
||||
> |
|
||||
<search-input |
|
||||
:placeholder="search_placeholder" |
|
||||
:on_search="set_search_route" |
|
||||
v-model="search_value" |
|
||||
/> |
|
||||
|
|
||||
<div v-if="show_skeleton"> |
|
||||
<section-header> |
|
||||
<h4 class="hub-skeleton">Explore Explore Explore</h4> |
|
||||
</section-header> |
|
||||
<div class="row"> |
|
||||
<div class="col-md-3 col-sm-4 col-xs-6 hub-card-container" v-for="(f, $index) in [1, 2, 3, 4, 5, 6, 7]" :key="$index"> |
|
||||
<div class="hub-skeleton" style="height: 262px; width: 100%; margin-bottom: 25px;"></div> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
|
|
||||
<div v-else v-for="section in sections" :key="section.title"> |
|
||||
|
|
||||
<section-header> |
|
||||
<h4>{{ section.title }}</h4> |
|
||||
<p v-if="section.expandable" :data-route="'marketplace/category/' + section.title">{{ 'See All' }}</p> |
|
||||
</section-header> |
|
||||
|
|
||||
<item-cards-container |
|
||||
:container_name="section.title" |
|
||||
:items="section.items" |
|
||||
:item_id_fieldname="item_id_fieldname" |
|
||||
:on_click="go_to_item_details_page" |
|
||||
/> |
|
||||
</div> |
|
||||
</div> |
|
||||
</template> |
|
||||
|
|
||||
<script> |
|
||||
export default { |
|
||||
name: 'home-page', |
|
||||
data() { |
|
||||
return { |
|
||||
page_name: frappe.get_route()[1], |
|
||||
item_id_fieldname: 'name', |
|
||||
search_value: '', |
|
||||
|
|
||||
sections: [], |
|
||||
show_skeleton: true, |
|
||||
|
|
||||
// Constants |
|
||||
search_placeholder: __('Search for anything ...'), |
|
||||
}; |
|
||||
}, |
|
||||
created() { |
|
||||
// refreshed |
|
||||
this.search_value = ''; |
|
||||
this.get_items(); |
|
||||
}, |
|
||||
mounted() { |
|
||||
frappe.route.on('change', () => { |
|
||||
if (frappe.get_route_str() === 'marketplace/home') { |
|
||||
this.get_items(); |
|
||||
} |
|
||||
}) |
|
||||
}, |
|
||||
methods: { |
|
||||
get_items() { |
|
||||
hub.call('get_data_for_homepage', frappe.defaults ? { |
|
||||
country: frappe.defaults.get_user_default('country') |
|
||||
} : null) |
|
||||
.then((data) => { |
|
||||
this.show_skeleton = false; |
|
||||
|
|
||||
this.sections.push({ |
|
||||
title: __('Explore'), |
|
||||
items: data.random_items |
|
||||
}); |
|
||||
if (data.items_by_country.length) { |
|
||||
this.sections.push({ |
|
||||
title: __('Near you'), |
|
||||
items: data.items_by_country |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
const category_items = data.category_items; |
|
||||
|
|
||||
if (category_items) { |
|
||||
Object.keys(category_items).map(category => { |
|
||||
const items = category_items[category]; |
|
||||
|
|
||||
this.sections.push({ |
|
||||
title: __(category), |
|
||||
expandable: true, |
|
||||
items |
|
||||
}); |
|
||||
}); |
|
||||
} |
|
||||
}) |
|
||||
}, |
|
||||
|
|
||||
go_to_item_details_page(hub_item_name) { |
|
||||
frappe.set_route(`marketplace/item/${hub_item_name}`); |
|
||||
}, |
|
||||
|
|
||||
set_search_route() { |
|
||||
frappe.set_route('marketplace', 'search', 'All', this.search_value); |
|
||||
}, |
|
||||
} |
|
||||
} |
|
||||
</script> |
|
||||
|
|
||||
<style scoped></style> |
|
@ -1,356 +0,0 @@ |
|||||
<template> |
|
||||
<div class="marketplace-page" :data-page-name="page_name" v-if="init || item"> |
|
||||
<detail-view |
|
||||
:title="title" |
|
||||
:image="image" |
|
||||
:sections="sections" |
|
||||
:menu_items="menu_items" |
|
||||
:show_skeleton="init" |
|
||||
> |
|
||||
<detail-header-item slot="detail-header-item" :value="item_subtitle"></detail-header-item> |
|
||||
<detail-header-item slot="detail-header-item" :value="item_views_and_ratings"></detail-header-item> |
|
||||
|
|
||||
<button |
|
||||
v-if="primary_action" |
|
||||
slot="detail-header-item" |
|
||||
class="btn btn-primary btn-sm margin-top" |
|
||||
@click="primary_action.action" |
|
||||
>{{ primary_action.label }}</button> |
|
||||
</detail-view> |
|
||||
|
|
||||
<review-area v-if="!init" :hub_item_name="hub_item_name"></review-area> |
|
||||
</div> |
|
||||
</template> |
|
||||
|
|
||||
<script> |
|
||||
import ReviewArea from '../components/ReviewArea.vue'; |
|
||||
import { get_rating_html } from '../components/reviews'; |
|
||||
import { edit_details_dialog } from '../components/edit_details_dialog'; |
|
||||
|
|
||||
export default { |
|
||||
name: 'item-page', |
|
||||
components: { |
|
||||
ReviewArea |
|
||||
}, |
|
||||
data() { |
|
||||
return { |
|
||||
page_name: frappe.get_route()[1], |
|
||||
hub_item_name: frappe.get_route()[2], |
|
||||
|
|
||||
init: true, |
|
||||
|
|
||||
item: null, |
|
||||
title: null, |
|
||||
image: null, |
|
||||
sections: [] |
|
||||
}; |
|
||||
}, |
|
||||
computed: { |
|
||||
is_own_item() { |
|
||||
let is_own_item = false; |
|
||||
if (this.item) { |
|
||||
if (this.item.hub_seller === hub.settings.hub_seller_name) { |
|
||||
is_own_item = true; |
|
||||
} |
|
||||
} |
|
||||
return is_own_item; |
|
||||
}, |
|
||||
menu_items() { |
|
||||
return [ |
|
||||
{ |
|
||||
label: __('Save Item'), |
|
||||
condition: hub.is_user_registered() && !this.is_own_item, |
|
||||
action: this.add_to_saved_items |
|
||||
}, |
|
||||
{ |
|
||||
label: __('Add to Featured Item'), |
|
||||
condition: hub.is_user_registered() && this.is_own_item, |
|
||||
action: this.add_to_featured_items |
|
||||
}, |
|
||||
{ |
|
||||
label: __('Report this Item'), |
|
||||
condition: !this.is_own_item, |
|
||||
action: this.report_item |
|
||||
}, |
|
||||
{ |
|
||||
label: __('Edit Details'), |
|
||||
condition: hub.is_user_registered() && this.is_own_item, |
|
||||
action: this.edit_details |
|
||||
}, |
|
||||
{ |
|
||||
label: __('Unpublish Item'), |
|
||||
condition: hub.is_user_registered() && this.is_own_item, |
|
||||
action: this.unpublish_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({ |
|
||||
value: this.item.company, |
|
||||
on_click: this.go_to_seller_profile_page |
|
||||
}); |
|
||||
|
|
||||
return subtitle_items; |
|
||||
}, |
|
||||
|
|
||||
item_views_and_ratings() { |
|
||||
if (!this.item) { |
|
||||
return ''; |
|
||||
} |
|
||||
|
|
||||
let stats = __('No views yet'); |
|
||||
if (this.item.view_count) { |
|
||||
const views_message = __('{0} Views', [this.item.view_count]); |
|
||||
|
|
||||
const rating_html = get_rating_html(this.item.average_rating); |
|
||||
const rating_count = |
|
||||
this.item.no_of_ratings > 0 |
|
||||
? __('{0} reviews', [this.item.no_of_ratings]) |
|
||||
: __('No reviews yet'); |
|
||||
|
|
||||
stats = [views_message, rating_html, rating_count]; |
|
||||
} |
|
||||
|
|
||||
return stats; |
|
||||
}, |
|
||||
|
|
||||
primary_action() { |
|
||||
if (hub.is_user_registered()) { |
|
||||
return { |
|
||||
label: __('Contact Seller'), |
|
||||
action: this.contact_seller.bind(this) |
|
||||
}; |
|
||||
} else { |
|
||||
return undefined; |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
created() { |
|
||||
this.get_item_details(); |
|
||||
}, |
|
||||
mounted() { |
|
||||
// To record a single view per session, (later) |
|
||||
// erpnext.hub.item_view_cache = erpnext.hub.item_view_cache || []; |
|
||||
// if (erpnext.hub.item_view_cache.includes(this.hub_item_name)) { |
|
||||
// return; |
|
||||
// } |
|
||||
|
|
||||
this.item_received.then(() => { |
|
||||
setTimeout(() => { |
|
||||
hub.call('add_item_view', { |
|
||||
hub_item_name: this.hub_item_name |
|
||||
}); |
|
||||
// .then(() => { |
|
||||
// erpnext.hub.item_view_cache.push(this.hub_item_name); |
|
||||
// }); |
|
||||
}, 5000); |
|
||||
}); |
|
||||
}, |
|
||||
methods: { |
|
||||
get_item_details() { |
|
||||
this.item_received = hub |
|
||||
.call('get_item_details', { hub_item_name: this.hub_item_name }) |
|
||||
.then(item => { |
|
||||
this.init = false; |
|
||||
this.item = item; |
|
||||
|
|
||||
this.build_data(); |
|
||||
this.make_dialogs(); |
|
||||
}); |
|
||||
}, |
|
||||
go_to_seller_profile_page(seller_name) { |
|
||||
frappe.set_route(`marketplace/seller/${seller_name}`); |
|
||||
}, |
|
||||
build_data() { |
|
||||
this.title = this.item.item_name || this.item.name; |
|
||||
this.image = this.item.image; |
|
||||
|
|
||||
this.sections = [ |
|
||||
{ |
|
||||
title: __('Item Description'), |
|
||||
content: this.item.description |
|
||||
? __(this.item.description) |
|
||||
: __('No description') |
|
||||
}, |
|
||||
{ |
|
||||
title: __('Seller Information'), |
|
||||
content: this.item.seller_description |
|
||||
? __(this.item.seller_description) |
|
||||
: __('No description') |
|
||||
} |
|
||||
]; |
|
||||
}, |
|
||||
|
|
||||
make_dialogs() { |
|
||||
this.make_contact_seller_dialog(); |
|
||||
this.make_report_item_dialog(); |
|
||||
this.make_editing_dialog(); |
|
||||
}, |
|
||||
|
|
||||
add_to_saved_items() { |
|
||||
hub.call('add_item_to_user_saved_items', { |
|
||||
hub_item_name: this.hub_item_name, |
|
||||
hub_user: frappe.session.user |
|
||||
}) |
|
||||
.then(() => { |
|
||||
const saved_items_link = `<b><a href="#marketplace/saved-items">${__('Saved')}</a></b>`; |
|
||||
frappe.show_alert(saved_items_link); |
|
||||
erpnext.hub.trigger('action:item_save'); |
|
||||
}) |
|
||||
.catch(e => { |
|
||||
console.error(e); |
|
||||
}); |
|
||||
}, |
|
||||
|
|
||||
add_to_featured_items() { |
|
||||
hub.call('add_item_to_seller_featured_items', { |
|
||||
hub_item_name: this.hub_item_name, |
|
||||
hub_user: frappe.session.user |
|
||||
}) |
|
||||
.then(() => { |
|
||||
const featured_items_link = `<b><a href="#marketplace/featured-items">${__('Added to Featured Items')}</a></b>`; |
|
||||
frappe.show_alert(featured_items_link); |
|
||||
erpnext.hub.trigger('action:item_feature'); |
|
||||
}) |
|
||||
.catch(e => { |
|
||||
console.error(e); |
|
||||
}); |
|
||||
}, |
|
||||
|
|
||||
make_contact_seller_dialog() { |
|
||||
this.contact_seller_dialog = new frappe.ui.Dialog({ |
|
||||
title: __('Send a message'), |
|
||||
fields: [ |
|
||||
{ |
|
||||
fieldname: 'to', |
|
||||
fieldtype: 'Read Only', |
|
||||
label: __('To'), |
|
||||
default: this.item.company |
|
||||
}, |
|
||||
{ |
|
||||
fieldtype: 'Text', |
|
||||
fieldname: 'message', |
|
||||
label: __('Message') |
|
||||
} |
|
||||
], |
|
||||
primary_action: ({ message }) => { |
|
||||
if (!message) return; |
|
||||
|
|
||||
hub.call('send_message', { |
|
||||
hub_item: this.item.name, |
|
||||
message |
|
||||
}) |
|
||||
.then(() => { |
|
||||
this.contact_seller_dialog.hide(); |
|
||||
frappe.set_route('marketplace', 'buying', this.item.name); |
|
||||
erpnext.hub.trigger('action:send_message'); |
|
||||
}); |
|
||||
} |
|
||||
}); |
|
||||
}, |
|
||||
|
|
||||
make_report_item_dialog() { |
|
||||
this.report_item_dialog = new frappe.ui.Dialog({ |
|
||||
title: __('Report Item'), |
|
||||
fields: [ |
|
||||
{ |
|
||||
label: __('Why do think this Item should be removed?'), |
|
||||
fieldtype: 'Text', |
|
||||
fieldname: 'message' |
|
||||
} |
|
||||
], |
|
||||
primary_action: ({ message }) => { |
|
||||
hub.call('add_reported_item', { |
|
||||
hub_item_name: this.item.name, |
|
||||
message |
|
||||
}) |
|
||||
.then(() => { |
|
||||
d.hide(); |
|
||||
frappe.show_alert(__('Item Reported')); |
|
||||
}); |
|
||||
} |
|
||||
}); |
|
||||
}, |
|
||||
|
|
||||
make_editing_dialog() { |
|
||||
this.edit_dialog = edit_details_dialog({ |
|
||||
primary_action: { |
|
||||
fn: values => { |
|
||||
this.update_details(values); |
|
||||
this.edit_dialog.hide(); |
|
||||
} |
|
||||
}, |
|
||||
defaults: { |
|
||||
item_name: this.item.item_name, |
|
||||
hub_category: this.item.hub_category, |
|
||||
description: this.item.description |
|
||||
} |
|
||||
}); |
|
||||
}, |
|
||||
|
|
||||
update_details(values) { |
|
||||
frappe.call('erpnext.hub_node.api.update_item', { |
|
||||
ref_doc: this.item.name, |
|
||||
data: values |
|
||||
}) |
|
||||
.then(r => { |
|
||||
return this.get_item_details(); |
|
||||
}) |
|
||||
.then(() => { |
|
||||
frappe.show_alert(__('{0} Updated', [this.item.item_name])); |
|
||||
}); |
|
||||
}, |
|
||||
|
|
||||
contact_seller() { |
|
||||
this.contact_seller_dialog.show(); |
|
||||
}, |
|
||||
|
|
||||
report_item() { |
|
||||
if (!hub.is_seller_registered()) { |
|
||||
frappe.throw( |
|
||||
__('Please login as a Marketplace User to report this item.') |
|
||||
); |
|
||||
} |
|
||||
this.report_item_dialog.show(); |
|
||||
}, |
|
||||
|
|
||||
edit_details() { |
|
||||
if (!hub.is_seller_registered()) { |
|
||||
frappe.throw( |
|
||||
__('Please login as a Marketplace User to edit this item.') |
|
||||
); |
|
||||
} |
|
||||
this.edit_dialog.show(); |
|
||||
}, |
|
||||
|
|
||||
unpublish_item() { |
|
||||
frappe.confirm(__('Unpublish {0}?', [this.item.item_name]), () => { |
|
||||
frappe |
|
||||
.call('erpnext.hub_node.api.unpublish_item', { |
|
||||
item_code: this.item.item_code, |
|
||||
hub_item_name: this.hub_item_name |
|
||||
}) |
|
||||
.then(r => { |
|
||||
frappe.set_route(`marketplace/home`); |
|
||||
frappe.show_alert(__('Item listing removed')); |
|
||||
}); |
|
||||
}); |
|
||||
} |
|
||||
} |
|
||||
}; |
|
||||
</script> |
|
||||
|
|
||||
<style scoped></style> |
|
@ -1,104 +0,0 @@ |
|||||
<template> |
|
||||
<div v-if="item_details"> |
|
||||
<div> |
|
||||
<a class="text-muted" v-route="back_link">← {{ __('Back to Messages') }}</a> |
|
||||
</div> |
|
||||
<section-header> |
|
||||
<div class="flex flex-column margin-bottom"> |
|
||||
<h4>{{ item_details.item_name }}</h4> |
|
||||
<span class="text-muted">{{ item_details.company }}</span> |
|
||||
</div> |
|
||||
</section-header> |
|
||||
<div class="row"> |
|
||||
<div class="col-md-7"> |
|
||||
<div class="message-container"> |
|
||||
<div class="message-list"> |
|
||||
<div class="level margin-bottom" v-for="message in messages" :key="message.name"> |
|
||||
<div class="level-left ellipsis" style="width: 80%;"> |
|
||||
<div v-html="frappe.avatar(message.sender)" /> |
|
||||
<div style="white-space: normal;" v-html="message.message" /> |
|
||||
</div> |
|
||||
<div class="level-right text-muted" v-html="frappe.datetime.comment_when(message.creation, true)" /> |
|
||||
</div> |
|
||||
</div> |
|
||||
<div class="message-input"> |
|
||||
<comment-input @change="send_message" /> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
</template> |
|
||||
<script> |
|
||||
import CommentInput from '../components/CommentInput.vue'; |
|
||||
import ItemListCard from '../components/ItemListCard.vue'; |
|
||||
|
|
||||
export default { |
|
||||
components: { |
|
||||
CommentInput, |
|
||||
ItemListCard |
|
||||
}, |
|
||||
data() { |
|
||||
return { |
|
||||
message_type: frappe.get_route()[1], |
|
||||
item_details: null, |
|
||||
messages: [] |
|
||||
} |
|
||||
}, |
|
||||
created() { |
|
||||
const hub_item_name = this.get_hub_item_name(); |
|
||||
this.get_item_details(hub_item_name) |
|
||||
.then(item_details => { |
|
||||
this.item_details = item_details; |
|
||||
this.get_messages() |
|
||||
.then(messages => { |
|
||||
this.messages = messages; |
|
||||
}); |
|
||||
}); |
|
||||
}, |
|
||||
computed: { |
|
||||
back_link() { |
|
||||
return 'marketplace/' + this.message_type; |
|
||||
} |
|
||||
}, |
|
||||
methods: { |
|
||||
send_message(message) { |
|
||||
this.messages.push({ |
|
||||
sender: frappe.session.user, |
|
||||
message: message, |
|
||||
creation: Date.now(), |
|
||||
name: frappe.utils.get_random(6) |
|
||||
}); |
|
||||
hub.call('send_message', { |
|
||||
to_seller: this.get_against_seller(), |
|
||||
hub_item: this.item_details.name, |
|
||||
message |
|
||||
}); |
|
||||
}, |
|
||||
get_item_details(hub_item_name) { |
|
||||
return hub.call('get_item_details', { hub_item_name }) |
|
||||
}, |
|
||||
get_messages() { |
|
||||
if (!this.item_details) return []; |
|
||||
return hub.call('get_messages', { |
|
||||
against_seller: this.get_against_seller(), |
|
||||
against_item: this.item_details.name |
|
||||
}); |
|
||||
}, |
|
||||
get_against_seller() { |
|
||||
if (this.message_type === 'buying') { |
|
||||
return this.item_details.hub_seller; |
|
||||
} else if (this.message_type === 'selling') { |
|
||||
return frappe.get_route()[2]; |
|
||||
} |
|
||||
}, |
|
||||
get_hub_item_name() { |
|
||||
if (this.message_type === 'buying') { |
|
||||
return frappe.get_route()[2]; |
|
||||
} else if (this.message_type === 'selling') { |
|
||||
return frappe.get_route()[3]; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
</script> |
|
@ -1,36 +0,0 @@ |
|||||
<template> |
|
||||
<div |
|
||||
class="marketplace-page" |
|
||||
:data-page-name="page_name" |
|
||||
> |
|
||||
<empty-state |
|
||||
:message="empty_state_message" |
|
||||
:height="500" |
|
||||
:action="action" |
|
||||
> |
|
||||
</empty-state> |
|
||||
|
|
||||
</div> |
|
||||
</template> |
|
||||
|
|
||||
<script> |
|
||||
export default { |
|
||||
name: 'not-found-page', |
|
||||
data() { |
|
||||
return { |
|
||||
page_name: 'not-found', |
|
||||
action: { |
|
||||
label: __('Back to Home'), |
|
||||
on_click: () => { |
|
||||
frappe.set_route(`marketplace/home`); |
|
||||
} |
|
||||
}, |
|
||||
|
|
||||
// Constants |
|
||||
empty_state_message: __('Sorry! We could not find what you were looking for.') |
|
||||
}; |
|
||||
}, |
|
||||
} |
|
||||
</script> |
|
||||
|
|
||||
<style scoped></style> |
|
@ -1,212 +0,0 @@ |
|||||
<template> |
|
||||
<div |
|
||||
class="marketplace-page" |
|
||||
:data-page-name="page_name" |
|
||||
> |
|
||||
<notification-message |
|
||||
v-if="last_sync_message" |
|
||||
:message="last_sync_message" |
|
||||
@remove-message="clear_last_sync_message" |
|
||||
></notification-message> |
|
||||
|
|
||||
<div class="flex justify-between align-flex-end margin-bottom"> |
|
||||
<h5>{{ page_title }}</h5> |
|
||||
|
|
||||
<button class="btn btn-primary btn-sm publish-items" |
|
||||
:disabled="no_selected_items" |
|
||||
@click="publish_selected_items" |
|
||||
> |
|
||||
<span>{{ publish_button_text }}</span> |
|
||||
</button> |
|
||||
</div> |
|
||||
|
|
||||
<item-cards-container |
|
||||
:container_name="page_title" |
|
||||
:items="selected_items" |
|
||||
:item_id_fieldname="item_id_fieldname" |
|
||||
:is_local="true" |
|
||||
:editable="true" |
|
||||
@remove-item="remove_item_from_selection" |
|
||||
|
|
||||
:empty_state_message="empty_state_message" |
|
||||
:empty_state_bordered="true" |
|
||||
:empty_state_height="80" |
|
||||
> |
|
||||
</item-cards-container> |
|
||||
|
|
||||
<p class="text-muted">{{ valid_items_instruction }}</p> |
|
||||
|
|
||||
<search-input |
|
||||
:placeholder="search_placeholder" |
|
||||
:on_search="get_valid_items" |
|
||||
v-model="search_value" |
|
||||
> |
|
||||
</search-input> |
|
||||
|
|
||||
<item-cards-container |
|
||||
:items="valid_items" |
|
||||
:item_id_fieldname="item_id_fieldname" |
|
||||
:is_local="true" |
|
||||
:on_click="show_publishing_dialog_for_item" |
|
||||
> |
|
||||
</item-cards-container> |
|
||||
</div> |
|
||||
</template> |
|
||||
|
|
||||
<script> |
|
||||
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], |
|
||||
valid_items: [], |
|
||||
selected_items: [], |
|
||||
items_data_to_publish: {}, |
|
||||
search_value: '', |
|
||||
item_id_fieldname: 'item_code', |
|
||||
|
|
||||
// Constants |
|
||||
// TODO: multiline translations don't work |
|
||||
page_title: __('Publish Items'), |
|
||||
search_placeholder: __('Search Items ...'), |
|
||||
empty_state_message: __('No Items selected yet. Browse and click on items below to publish.'), |
|
||||
valid_items_instruction: __('Only items with an image and description can be published. Please update them if an item in your inventory does not appear.'), |
|
||||
last_sync_message: (hub.settings.last_sync_datetime) |
|
||||
? __('Last sync was {0}.', [`<a href="#marketplace/profile">${comment_when(hub.settings.last_sync_datetime)}</a>`]) + |
|
||||
` <a href="#marketplace/published-items">${__('See your Published Items.')}</a>` |
|
||||
: '' |
|
||||
}; |
|
||||
}, |
|
||||
computed: { |
|
||||
no_selected_items() { |
|
||||
return this.selected_items.length === 0; |
|
||||
}, |
|
||||
|
|
||||
publish_button_text() { |
|
||||
const number = this.selected_items.length; |
|
||||
let text = __('Publish'); |
|
||||
if(number === 1) { |
|
||||
text = __('Publish 1 Item'); |
|
||||
} |
|
||||
if(number > 1) { |
|
||||
text = __('Publish {0} Items', [number]); |
|
||||
} |
|
||||
return text; |
|
||||
}, |
|
||||
|
|
||||
items_dict() { |
|
||||
let items_dict = {}; |
|
||||
this.valid_items.map(item => { |
|
||||
items_dict[item[this.item_id_fieldname]] = item |
|
||||
}) |
|
||||
|
|
||||
return items_dict; |
|
||||
}, |
|
||||
}, |
|
||||
created() { |
|
||||
this.get_valid_items(); |
|
||||
this.make_publishing_dialog(); |
|
||||
}, |
|
||||
methods: { |
|
||||
get_valid_items() { |
|
||||
frappe.call( |
|
||||
'erpnext.hub_node.api.get_valid_items', |
|
||||
{ |
|
||||
search_value: this.search_value |
|
||||
} |
|
||||
) |
|
||||
.then((r) => { |
|
||||
this.valid_items = r.message; |
|
||||
}) |
|
||||
}, |
|
||||
|
|
||||
publish_selected_items() { |
|
||||
frappe.call( |
|
||||
'erpnext.hub_node.api.publish_selected_items', |
|
||||
{ |
|
||||
items_to_publish: this.selected_items |
|
||||
} |
|
||||
) |
|
||||
.then((r) => { |
|
||||
this.selected_items = []; |
|
||||
return frappe.db.get_doc('Marketplace Settings'); |
|
||||
}) |
|
||||
.then(doc => { |
|
||||
hub.settings = doc; |
|
||||
this.add_last_sync_message(); |
|
||||
}); |
|
||||
}, |
|
||||
|
|
||||
add_last_sync_message() { |
|
||||
this.last_sync_message = __('Last sync was {0}.', |
|
||||
[`<a href="#marketplace/profile">${comment_when(hub.settings.last_sync_datetime)}</a>`] |
|
||||
) + `<a href="#marketplace/published-items">${__('See your Published Items')}</a>.`; |
|
||||
}, |
|
||||
|
|
||||
clear_last_sync_message() { |
|
||||
this.last_sync_message = ''; |
|
||||
}, |
|
||||
|
|
||||
remove_item_from_selection(item_code) { |
|
||||
this.selected_items = this.selected_items |
|
||||
.filter(item => item.item_code !== item_code); |
|
||||
}, |
|
||||
|
|
||||
make_publishing_dialog() { |
|
||||
this.item_publish_dialog = ItemPublishDialog( |
|
||||
{ |
|
||||
fn: (values) => { |
|
||||
this.add_item_to_publish(values); |
|
||||
this.item_publish_dialog.hide(); |
|
||||
} |
|
||||
}, |
|
||||
{ |
|
||||
fn: () => { |
|
||||
const values = this.item_publish_dialog.get_values(true); |
|
||||
this.update_items_data_to_publish(values); |
|
||||
} |
|
||||
} |
|
||||
); |
|
||||
}, |
|
||||
|
|
||||
add_item_to_publish(values) { |
|
||||
this.update_items_data_to_publish(values); |
|
||||
|
|
||||
const item_code = values.item_code; |
|
||||
let item_doc = this.items_dict[item_code]; |
|
||||
|
|
||||
const item_to_publish = Object.assign({}, item_doc, values); |
|
||||
this.selected_items.push(item_to_publish); |
|
||||
}, |
|
||||
|
|
||||
update_items_data_to_publish(values) { |
|
||||
this.items_data_to_publish[values.item_code] = values; |
|
||||
}, |
|
||||
|
|
||||
show_publishing_dialog_for_item(item_code) { |
|
||||
let item_data = this.items_data_to_publish[item_code]; |
|
||||
if(!item_data) { item_data = { item_code }; }; |
|
||||
|
|
||||
this.item_publish_dialog.clear(); |
|
||||
|
|
||||
const item_doc = this.items_dict[item_code]; |
|
||||
if(item_doc) { |
|
||||
this.item_publish_dialog.fields_dict.image_list.set_data( |
|
||||
item_doc.attachments.map(attachment => attachment.file_url) |
|
||||
); |
|
||||
} |
|
||||
|
|
||||
this.item_publish_dialog.set_values(item_data); |
|
||||
this.item_publish_dialog.show(); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
</script> |
|
||||
|
|
||||
<style scoped></style> |
|
@ -1,74 +0,0 @@ |
|||||
<template> |
|
||||
<div |
|
||||
class="marketplace-page" |
|
||||
:data-page-name="page_name" |
|
||||
> |
|
||||
<section-header> |
|
||||
<div> |
|
||||
<h5>{{ __('Published Items') }}</h5> |
|
||||
<p v-if="items.length" |
|
||||
class="text-muted margin-bottom"> |
|
||||
{{ __('You can publish upto 200 items.') }} |
|
||||
</p> |
|
||||
</div> |
|
||||
|
|
||||
<button v-if="items.length" |
|
||||
class="btn btn-default btn-xs publish-items" |
|
||||
v-route="'marketplace/publish'" |
|
||||
> |
|
||||
<span>{{ __('Publish More Items') }}</span> |
|
||||
</button> |
|
||||
|
|
||||
</section-header> |
|
||||
|
|
||||
<item-cards-container |
|
||||
:container_name="__('Published Items')" |
|
||||
:items="items" |
|
||||
:item_id_fieldname="item_id_fieldname" |
|
||||
:on_click="go_to_item_details_page" |
|
||||
:empty_state_message="__('You haven\'t published any items yet.')" |
|
||||
:empty_state_action="publish_page_action" |
|
||||
> |
|
||||
</item-cards-container> |
|
||||
</div> |
|
||||
</template> |
|
||||
|
|
||||
<script> |
|
||||
export default { |
|
||||
data() { |
|
||||
return { |
|
||||
page_name: frappe.get_route()[1], |
|
||||
items: [], |
|
||||
item_id_fieldname: 'name', |
|
||||
|
|
||||
publish_page_action: { |
|
||||
label: __('Publish Your First Items'), |
|
||||
on_click: () => { |
|
||||
frappe.set_route(`marketplace/publish`); |
|
||||
} |
|
||||
} |
|
||||
}; |
|
||||
}, |
|
||||
created() { |
|
||||
this.get_items(); |
|
||||
}, |
|
||||
methods: { |
|
||||
get_items() { |
|
||||
hub.call('get_items', { |
|
||||
filters: { |
|
||||
hub_seller: hub.settings.hub_seller_name |
|
||||
} |
|
||||
}) |
|
||||
.then((items) => { |
|
||||
this.items = items; |
|
||||
}) |
|
||||
}, |
|
||||
|
|
||||
go_to_item_details_page(hub_item_name) { |
|
||||
frappe.set_route(`marketplace/item/${hub_item_name}`); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
</script> |
|
||||
|
|
||||
<style scoped></style> |
|
@ -1,116 +0,0 @@ |
|||||
<template> |
|
||||
<div |
|
||||
class="marketplace-page" |
|
||||
:data-page-name="page_name" |
|
||||
> |
|
||||
<h5>{{ page_title }}</h5> |
|
||||
|
|
||||
<item-cards-container |
|
||||
:container_name="page_title" |
|
||||
:items="items" |
|
||||
:item_id_fieldname="item_id_fieldname" |
|
||||
:on_click="go_to_item_details_page" |
|
||||
:editable="true" |
|
||||
@remove-item="on_item_remove" |
|
||||
:empty_state_message="empty_state_message" |
|
||||
> |
|
||||
</item-cards-container> |
|
||||
</div> |
|
||||
</template> |
|
||||
|
|
||||
<script> |
|
||||
export default { |
|
||||
name: 'saved-items-page', |
|
||||
data() { |
|
||||
return { |
|
||||
page_name: frappe.get_route()[1], |
|
||||
items: [], |
|
||||
item_id_fieldname: 'name', |
|
||||
|
|
||||
// Constants |
|
||||
page_title: __('Saved Items'), |
|
||||
empty_state_message: __('You have not saved any items yet.') |
|
||||
}; |
|
||||
}, |
|
||||
created() { |
|
||||
this.get_items(); |
|
||||
}, |
|
||||
methods: { |
|
||||
get_items() { |
|
||||
hub.call( |
|
||||
'get_saved_items_of_user', {}, |
|
||||
'action:item_save' |
|
||||
) |
|
||||
.then((items) => { |
|
||||
this.items = items; |
|
||||
}) |
|
||||
}, |
|
||||
|
|
||||
go_to_item_details_page(hub_item_name) { |
|
||||
frappe.set_route(`marketplace/item/${hub_item_name}`); |
|
||||
}, |
|
||||
|
|
||||
on_item_remove(hub_item_name) { |
|
||||
const grace_period = 5000; |
|
||||
let reverted = false; |
|
||||
let alert; |
|
||||
|
|
||||
const undo_remove = () => { |
|
||||
this.toggle_item(hub_item_name);; |
|
||||
reverted = true; |
|
||||
alert.hide(); |
|
||||
return false; |
|
||||
} |
|
||||
|
|
||||
const item_name = this.items.filter(item => item.hub_item_name === hub_item_name); |
|
||||
|
|
||||
alert = frappe.show_alert(` |
|
||||
<span> |
|
||||
${__('{0} removed.', [item_name], 'A specific Item has been removed.')} |
|
||||
<a href="#" data-action="undo-remove"> |
|
||||
<b>${__('Undo', None, 'Undo removal of item.')}</b> |
|
||||
</a> |
|
||||
</span>`, |
|
||||
grace_period/1000, |
|
||||
{ |
|
||||
'undo-remove': undo_remove.bind(this) |
|
||||
} |
|
||||
); |
|
||||
|
|
||||
this.toggle_item(hub_item_name, false); |
|
||||
|
|
||||
setTimeout(() => { |
|
||||
if(!reverted) { |
|
||||
this.remove_item_from_saved_items(hub_item_name); |
|
||||
} |
|
||||
}, grace_period); |
|
||||
}, |
|
||||
|
|
||||
remove_item_from_saved_items(hub_item_name) { |
|
||||
erpnext.hub.trigger('action:item_save'); |
|
||||
hub.call('remove_item_from_user_saved_items', { |
|
||||
hub_item_name, |
|
||||
hub_user: frappe.session.user |
|
||||
}) |
|
||||
.then(() => { |
|
||||
this.get_items(); |
|
||||
}) |
|
||||
.catch(e => { |
|
||||
console.log(e); |
|
||||
}); |
|
||||
}, |
|
||||
|
|
||||
// By default show |
|
||||
toggle_item(hub_item_name, show=true) { |
|
||||
this.items = this.items.map(item => { |
|
||||
if(item.name === hub_item_name) { |
|
||||
item.seen = show; |
|
||||
} |
|
||||
return item; |
|
||||
}); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
</script> |
|
||||
|
|
||||
<style scoped></style> |
|
@ -1,81 +0,0 @@ |
|||||
<template> |
|
||||
<div |
|
||||
class="marketplace-page" |
|
||||
:data-page-name="page_name" |
|
||||
> |
|
||||
<search-input |
|
||||
:placeholder="search_placeholder" |
|
||||
:on_search="set_route_and_get_items" |
|
||||
v-model="search_value" |
|
||||
> |
|
||||
</search-input> |
|
||||
|
|
||||
<h5>{{ page_title }}</h5> |
|
||||
|
|
||||
<item-cards-container |
|
||||
container_name="Search" |
|
||||
:items="items" |
|
||||
:item_id_fieldname="item_id_fieldname" |
|
||||
:on_click="go_to_item_details_page" |
|
||||
:empty_state_message="empty_state_message" |
|
||||
> |
|
||||
</item-cards-container> |
|
||||
</div> |
|
||||
</template> |
|
||||
|
|
||||
<script> |
|
||||
export default { |
|
||||
data() { |
|
||||
return { |
|
||||
page_name: frappe.get_route()[1], |
|
||||
items: [], |
|
||||
category: frappe.get_route()[2], |
|
||||
search_value: frappe.get_route()[3], |
|
||||
item_id_fieldname: 'name', |
|
||||
filters: {}, |
|
||||
|
|
||||
// Constants |
|
||||
search_placeholder: __('Search for anything ...'), |
|
||||
empty_state_message: __('') |
|
||||
}; |
|
||||
}, |
|
||||
computed: { |
|
||||
page_title() { |
|
||||
return this.items.length |
|
||||
? __('Results for "{0}" {1}', [ |
|
||||
this.search_value, |
|
||||
this.category !== 'All' ? __('in category {0}', [this.category]) : '' |
|
||||
]) |
|
||||
: __('No Items found.'); |
|
||||
} |
|
||||
}, |
|
||||
created() { |
|
||||
this.get_items(); |
|
||||
}, |
|
||||
methods: { |
|
||||
get_items() { |
|
||||
if (this.category !== 'All') { |
|
||||
this.filters['hub_category'] = this.category; |
|
||||
} |
|
||||
hub.call('get_items', { |
|
||||
keyword: this.search_value, |
|
||||
filters: this.filters |
|
||||
}) |
|
||||
.then((items) => { |
|
||||
this.items = items; |
|
||||
}) |
|
||||
}, |
|
||||
|
|
||||
set_route_and_get_items() { |
|
||||
frappe.set_route('marketplace', 'search', this.category, this.search_value); |
|
||||
this.get_items(); |
|
||||
}, |
|
||||
|
|
||||
go_to_item_details_page(hub_item_name) { |
|
||||
frappe.set_route(`marketplace/item/${hub_item_name}`); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
</script> |
|
||||
|
|
||||
<style scoped></style> |
|
@ -1,201 +0,0 @@ |
|||||
<template> |
|
||||
<div |
|
||||
class="marketplace-page" |
|
||||
:data-page-name="page_name" |
|
||||
v-if="init || profile" |
|
||||
> |
|
||||
<detail-view |
|
||||
:title="title" |
|
||||
:image="image" |
|
||||
:sections="sections" |
|
||||
:show_skeleton="init" |
|
||||
> |
|
||||
<detail-header-item slot="detail-header-item" |
|
||||
:value="country" |
|
||||
></detail-header-item> |
|
||||
<detail-header-item slot="detail-header-item" |
|
||||
:value="site_name" |
|
||||
></detail-header-item> |
|
||||
<detail-header-item slot="detail-header-item" |
|
||||
:value="joined_when" |
|
||||
></detail-header-item> |
|
||||
|
|
||||
</detail-view> |
|
||||
|
|
||||
<div v-if="items.length"> |
|
||||
<h5> |
|
||||
{{ item_container_heading }} |
|
||||
<small v-if="is_user_registered() && is_own_company"> |
|
||||
<a class="pull-right" href="#marketplace/featured-items">Customize your Featured Items</a> |
|
||||
</small> |
|
||||
</h5> |
|
||||
<item-cards-container |
|
||||
:container_name="item_container_heading" |
|
||||
:items="items" |
|
||||
:item_id_fieldname="item_id_fieldname" |
|
||||
:on_click="go_to_item_details_page" |
|
||||
> |
|
||||
</item-cards-container> |
|
||||
<a class="pull-right" @click="go_to_seller_items_page(seller_company)">Show all items</a> |
|
||||
</div> |
|
||||
|
|
||||
<div v-if="recent_seller_reviews.length"> |
|
||||
<h5>Customer Reviews</h5> |
|
||||
<div class="container" v-for="review in recent_seller_reviews" :key="review.name"> |
|
||||
<br> |
|
||||
<span class="text-muted"> |
|
||||
<rating :rating="review.rating" :max_rating="5"></rating> |
|
||||
</span> |
|
||||
<i class="octicon octicon-quote hidden-xs fa-fw"></i> |
|
||||
<span class="bold">{{ review.subject }}</span> |
|
||||
<i class="octicon octicon-quote hidden-xs fa-fw fa-rotate-180"></i> |
|
||||
<div class="container"> |
|
||||
by {{ review.username }} |
|
||||
<a class="text-muted"> |
|
||||
<span class="text-muted hidden-xs">–</span> |
|
||||
<span class="hidden-xs" v-html="comment_when(review.timestamp)"></span> |
|
||||
</a> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
|
|
||||
<div v-if="seller_product_view_stats.length"> |
|
||||
<h5>Stats</h5> |
|
||||
<div id="seller_traffic_chart"></div> |
|
||||
</div> |
|
||||
|
|
||||
|
|
||||
|
|
||||
</div> |
|
||||
</template> |
|
||||
|
|
||||
<script> |
|
||||
import Rating from '../components/Rating.vue'; |
|
||||
|
|
||||
|
|
||||
export default { |
|
||||
name: 'seller-page', |
|
||||
components: { |
|
||||
Rating |
|
||||
}, |
|
||||
data() { |
|
||||
return { |
|
||||
page_name: frappe.get_route()[1], |
|
||||
seller_company: frappe.get_route()[2], |
|
||||
hub_seller: null, |
|
||||
|
|
||||
init: true, |
|
||||
|
|
||||
profile: null, |
|
||||
items:[], |
|
||||
recent_seller_reviews: [], |
|
||||
seller_product_view_stats: [], |
|
||||
seller_traffic_chart: null, |
|
||||
item_id_fieldname: 'name', |
|
||||
item_container_heading: 'Items', |
|
||||
|
|
||||
title: null, |
|
||||
image: null, |
|
||||
sections: [], |
|
||||
|
|
||||
country: '', |
|
||||
site_name: '', |
|
||||
joined_when: '', |
|
||||
}; |
|
||||
}, |
|
||||
created() { |
|
||||
this.get_seller_profile_and_items(); |
|
||||
}, |
|
||||
computed: { |
|
||||
is_own_company() { |
|
||||
let is_own_company = false; |
|
||||
if(this.hub_seller) { |
|
||||
if(this.hub_seller === hub.settings.hub_seller_name) { |
|
||||
is_own_company = true; |
|
||||
} |
|
||||
} |
|
||||
return is_own_company; |
|
||||
}, |
|
||||
}, |
|
||||
methods: { |
|
||||
comment_when(timestamp){ |
|
||||
return comment_when(timestamp) |
|
||||
}, |
|
||||
is_user_registered(){ |
|
||||
return hub.is_user_registered() |
|
||||
}, |
|
||||
get_seller_profile_and_items() { |
|
||||
let post_data = {company: this.seller_company} |
|
||||
if (this.page_name == 'profile'){ |
|
||||
this.seller_company = null; |
|
||||
this.hub_seller = hub.settings.hub_seller_name |
|
||||
post_data = {hub_seller: this.hub_seller} |
|
||||
} |
|
||||
hub.call('get_hub_seller_page_info', post_data) |
|
||||
.then(data => { |
|
||||
this.init = false; |
|
||||
this.profile = data.profile; |
|
||||
this.items = data.items; |
|
||||
this.item_container_heading = data.is_featured_item ? __('Featured Items') : __('Popular Items'); |
|
||||
this.hub_seller = this.items[0].hub_seller; |
|
||||
this.recent_seller_reviews = data.recent_seller_reviews; |
|
||||
this.seller_product_view_stats = data.seller_product_view_stats; |
|
||||
|
|
||||
const profile = this.profile; |
|
||||
|
|
||||
this.title = profile.company; |
|
||||
|
|
||||
this.country = __(profile.country); |
|
||||
this.site_name = __(profile.site_name); |
|
||||
this.joined_when = __('Joined {0}', [comment_when(profile.creation)]); |
|
||||
|
|
||||
this.image = profile.logo; |
|
||||
this.sections = [ |
|
||||
{ |
|
||||
title: __('About the Company'), |
|
||||
content: profile.company_description |
|
||||
? __(profile.company_description) |
|
||||
: __('No description') |
|
||||
} |
|
||||
]; |
|
||||
|
|
||||
setTimeout(() => this.init_seller_traffic_chart(), 1); |
|
||||
|
|
||||
}); |
|
||||
}, |
|
||||
|
|
||||
go_to_item_details_page(hub_item_name) { |
|
||||
frappe.set_route(`marketplace/item/${hub_item_name}`); |
|
||||
}, |
|
||||
go_to_seller_items_page(hub_seller) { |
|
||||
frappe.set_route(`marketplace/seller/${hub_seller}/items`); |
|
||||
}, |
|
||||
init_seller_traffic_chart() { |
|
||||
let lables = [] |
|
||||
let tooltip_lables = {} |
|
||||
let datasets = [{name:"Product Views",chartType: 'line',values: []}] |
|
||||
this.seller_product_view_stats.map((stat) => { |
|
||||
lables.push(stat.date.substring(5)); |
|
||||
tooltip_lables[stat.date.substring(5)] = new Date(stat.date).toDateString(); |
|
||||
datasets[0].values.push(stat.view_count); |
|
||||
}); |
|
||||
let data = {labels: lables, datasets:datasets, tooltip_lables:tooltip_lables} |
|
||||
this.seller_traffic_chart = new Chart( "#seller_traffic_chart", { // or DOM element |
|
||||
data: data, |
|
||||
|
|
||||
title: "Daily Product Views", |
|
||||
type: 'axis-mixed', // or 'bar', 'line', 'pie', 'percentage' |
|
||||
height: 300, |
|
||||
colors: ['purple', '#ffa3ef', 'light-blue'], |
|
||||
|
|
||||
tooltipOptions: { |
|
||||
formatTooltipX: d => this.seller_traffic_chart.data.tooltip_lables[d], |
|
||||
formatTooltipY: d => d + ' Views', |
|
||||
} |
|
||||
}); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
</script> |
|
||||
|
|
||||
<style scoped></style> |
|
@ -1,57 +0,0 @@ |
|||||
<template> |
|
||||
<div |
|
||||
class="marketplace-page" |
|
||||
:data-page-name="page_name" |
|
||||
v-if="init || items.length" |
|
||||
> |
|
||||
<h5>{{ item_container_heading }}</h5> |
|
||||
<item-cards-container |
|
||||
:container_name="item_container_heading" |
|
||||
:items="items" |
|
||||
:item_id_fieldname="item_id_fieldname" |
|
||||
:on_click="go_to_item_details_page" |
|
||||
> |
|
||||
</item-cards-container> |
|
||||
</div> |
|
||||
</template> |
|
||||
|
|
||||
<script> |
|
||||
export default { |
|
||||
name: 'seller-items-page', |
|
||||
data() { |
|
||||
return { |
|
||||
page_name: frappe.get_route()[1], |
|
||||
seller_company: frappe.get_route()[2], |
|
||||
|
|
||||
init: true, |
|
||||
items:[], |
|
||||
item_id_fieldname: 'name', |
|
||||
}; |
|
||||
}, |
|
||||
created() { |
|
||||
this.get_seller_and_items(); |
|
||||
}, |
|
||||
computed: { |
|
||||
item_container_heading() { |
|
||||
return __('Items by ' + this.seller_company); |
|
||||
} |
|
||||
}, |
|
||||
methods: { |
|
||||
get_seller_and_items() { |
|
||||
hub.call( |
|
||||
'get_items', |
|
||||
{ company: this.seller_company } |
|
||||
).then(data => { |
|
||||
this.init = false; |
|
||||
this.items = data; |
|
||||
}); |
|
||||
}, |
|
||||
|
|
||||
go_to_item_details_page(hub_item_name) { |
|
||||
frappe.set_route(`marketplace/item/${hub_item_name}`); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
</script> |
|
||||
|
|
||||
<style scoped></style> |
|
@ -1,66 +0,0 @@ |
|||||
<template> |
|
||||
<div> |
|
||||
<section-header> |
|
||||
<h4>{{ __('Selling') }}</h4> |
|
||||
</section-header> |
|
||||
<div class="row" v-if="items && items.length"> |
|
||||
<div class="col-md-7" |
|
||||
style="margin-bottom: 30px;" |
|
||||
v-for="item of items" |
|
||||
:key="item.name" |
|
||||
> |
|
||||
<item-list-card |
|
||||
:item="item" |
|
||||
> |
|
||||
<div slot="subtitle"> |
|
||||
<span class="text-muted">{{ __('{0} conversations', [item.received_messages.length]) }}</span> |
|
||||
</div> |
|
||||
</item-list-card> |
|
||||
<div class="hub-list-item" v-for="(message, index) in item.received_messages" :key="index" |
|
||||
v-route="'marketplace/selling/' + message.buyer + '/' + item.name" |
|
||||
> |
|
||||
<div class="hub-list-left"> |
|
||||
<div class="hub-list-body"> |
|
||||
<div class="hub-list-title"> |
|
||||
{{ message.buyer_name }} |
|
||||
</div> |
|
||||
<div class="hub-list-subtitle"> |
|
||||
{{ message.sender }}: {{ message.message | striphtml }} |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
<empty-state v-else :message="__('This page keeps track of your items in which buyers have showed some interest.')" :centered="false" /> |
|
||||
</div> |
|
||||
</template> |
|
||||
<script> |
|
||||
import EmptyState from '../components/EmptyState.vue'; |
|
||||
import SectionHeader from '../components/SectionHeader.vue'; |
|
||||
import ItemListCard from '../components/ItemListCard.vue'; |
|
||||
|
|
||||
export default { |
|
||||
components: { |
|
||||
SectionHeader, |
|
||||
ItemListCard, |
|
||||
EmptyState |
|
||||
}, |
|
||||
data() { |
|
||||
return { |
|
||||
items: null |
|
||||
} |
|
||||
}, |
|
||||
created() { |
|
||||
this.get_items_for_messages() |
|
||||
.then(items => { |
|
||||
this.items = items; |
|
||||
}); |
|
||||
}, |
|
||||
methods: { |
|
||||
get_items_for_messages() { |
|
||||
return hub.call('get_selling_items_for_messages'); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
</script> |
|
@ -1,58 +0,0 @@ |
|||||
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'; |
|
||||
import Image from './components/Image.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.component('base-image', Image); |
|
||||
|
|
||||
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) { |
|
||||
el.classList.remove('cursor-pointer'); |
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
const handleImage = (el, src) => { |
|
||||
let img = new Image(); |
|
||||
// add loading class
|
|
||||
el.src = ''; |
|
||||
el.classList.add('img-loading'); |
|
||||
|
|
||||
img.onload = () => { |
|
||||
// image loaded, remove loading class
|
|
||||
el.classList.remove('img-loading'); |
|
||||
// set src
|
|
||||
el.src = src; |
|
||||
} |
|
||||
img.onerror = () => { |
|
||||
el.classList.remove('img-loading'); |
|
||||
el.classList.add('no-image'); |
|
||||
el.src = null; |
|
||||
} |
|
||||
img.src = src; |
|
||||
} |
|
||||
|
|
||||
Vue.filter('striphtml', function (text) { |
|
||||
return strip_html(text || ''); |
|
||||
}); |
|
Loading…
Reference in new issue