diff --git a/Model/Api/Model/Common/Seo.php b/Model/Api/Model/Common/Seo.php index 1b3e16d..24afbe7 100644 --- a/Model/Api/Model/Common/Seo.php +++ b/Model/Api/Model/Common/Seo.php @@ -4,8 +4,6 @@ use \Vuefront\Vuefront\Model\Api\System\Engine\Model; use Magefan\Blog\Model\Url; - - class Seo extends Model { private $_categoryFactory; diff --git a/Model/Api/Model/Common/Vuefront.php b/Model/Api/Model/Common/Vuefront.php index f95a3df..77acc14 100644 --- a/Model/Api/Model/Common/Vuefront.php +++ b/Model/Api/Model/Common/Vuefront.php @@ -26,7 +26,6 @@ public function editApp($name, $appSetting) { $appSetting['codename'] = $name; - $app = $this->_appsFactory->create()->getCollection(); $app->addFieldToSelect('*'); $app->addFieldToFilter('codename', ['like' => $name]); diff --git a/Model/Api/Model/Store/Cart.php b/Model/Api/Model/Store/Cart.php new file mode 100644 index 0000000..6d025f9 --- /dev/null +++ b/Model/Api/Model/Store/Cart.php @@ -0,0 +1,119 @@ +_currencyHelper = $currencyHelper; + $this->_cartModel = $cartModel; + $this->_linkRepository = $linkRepository; + $this->_sessionFactory = $sessionFactory; + $this->_quoteModel = $quoteFactory; + }//end __construct() + + public function prepareCart() + { + $cart = []; + $this->_cartModel->getQuote()->collectTotals(); + $cart = [ + 'products' => [], + 'total' => $this->currency->format($this->_cartModel->getQuote()->getGrandTotal()), + ]; + + $results = $this->_cartModel->getItems(); + + foreach ($results as $value) { + /* + @var \Magento\Catalog\Model\Product $product + */ + $product = $value->getProduct(); + if (!$value->isDeleted() && !$value->getParentItemId() && !$value->getParentItem()) { + $price = ""; + if ($product->getTypeId() != "simple") { + $price = $this->_currencyHelper->currency($product->getFinalPrice(), true, false); + } else { + $price = $this->_currencyHelper->currency($product->getPrice(), true, false); + } + $cart['products'][] = [ + 'key' => $value->getId(), + 'product' => [ + 'product_id' => $product->getId(), + 'price' => $price + ], + 'quantity' => $value->getQty(), + 'option' => $this->getCartOptions($product, $value), + 'total' => $this->currency->format($value->getPrice() * $value->getQty()), + ]; + } + } + + return $cart; + } + + public function getProduct($key) + { + $product = null; + + foreach ($this->cart->getProducts() as $value) { + if ($value['cart_id'] == $key) { + $product = $value; + break; + } + } + if ($product === null) { + return null; + } + + return $product; + } + + /** + * @param $product \Magento\Catalog\Model\Product + * @param $value mixed + * @return array + */ + public function getCartOptions($product, $value) + { + $options = []; + $result_options = $product->getTypeInstance(true)->getOrderOptions($product); + + if (!empty($result_options['attributes_info'])) { + foreach ($result_options['attributes_info'] as $option) { + $options[] = [ + 'option_id' => (int)$option['option_id'], + 'option_value_id' => (int)$option['option_value'], + ]; + } + } + return $options; + } +} diff --git a/Model/Api/Model/Store/Category.php b/Model/Api/Model/Store/Category.php index 420bec8..3ef5c13 100755 --- a/Model/Api/Model/Store/Category.php +++ b/Model/Api/Model/Store/Category.php @@ -33,7 +33,7 @@ public function getCategories($data = []) } if ($data['top']) { - $collection->addAttributeToFilter('include_in_menu', array('eq' => $data['top'] ? 1 : 0)); + $collection->addAttributeToFilter('include_in_menu', ['eq' => $data['top'] ? 1 : 0]); } if ($data['size'] != '-1') { diff --git a/Model/Api/Resolver/Store/Cart.php b/Model/Api/Resolver/Store/Cart.php index 96dea85..52351b6 100755 --- a/Model/Api/Resolver/Store/Cart.php +++ b/Model/Api/Resolver/Store/Cart.php @@ -54,7 +54,6 @@ public function __construct( $this->_linkRepository = $linkRepository; $this->_sessionFactory = $sessionFactory; $this->_quoteModel = $quoteFactory; - }//end __construct() public function add($args) diff --git a/Model/ResourceModel/Settings.php b/Model/ResourceModel/Settings.php new file mode 100644 index 0000000..ca3d692 --- /dev/null +++ b/Model/ResourceModel/Settings.php @@ -0,0 +1,12 @@ +_init('vuefront_settings', 'setting_id'); + } +} diff --git a/Model/ResourceModel/Settings/Collection.php b/Model/ResourceModel/Settings/Collection.php new file mode 100644 index 0000000..3259ece --- /dev/null +++ b/Model/ResourceModel/Settings/Collection.php @@ -0,0 +1,15 @@ +_init( + \Vuefront\Vuefront\Model\Settings::class, + \Vuefront\Vuefront\Model\ResourceModel\Settings::class + ); + } +} diff --git a/Model/Settings.php b/Model/Settings.php new file mode 100644 index 0000000..f693ae9 --- /dev/null +++ b/Model/Settings.php @@ -0,0 +1,11 @@ +_init(\Vuefront\Vuefront\Model\ResourceModel\Settings::class); + } +} diff --git a/Setup/UpgradeSchema.php b/Setup/UpgradeSchema.php new file mode 100644 index 0000000..53eaded --- /dev/null +++ b/Setup/UpgradeSchema.php @@ -0,0 +1,74 @@ +getConnection(); + + $installer->startSetup(); + if (version_compare($context->getVersion(), '1.0.0', '<')) { + $table = $setup->getTable('vuefront_apps'); + $connection->addColumn( + $setup->getTable($table), + 'eventUrl', + [ + 'type' => \Magento\Framework\DB\Ddl\Table::TYPE_TEXT, + 'length' => 255, + 'nullable' => true, + 'comment' => 'Url for events' + ] + ); + $connection->addColumn( + $setup->getTable($table), + 'authUrl', + [ + 'type' => \Magento\Framework\DB\Ddl\Table::TYPE_TEXT, + 'length' => 255, + ['nullable' => true], + 'comment' => 'Url for auth' + ] + ); + + if ($connection->isTableExists('vuefront_settings') == false) { + + /** + * Create table 'vuefront_settings' + */ + $table = $installer->getConnection()->newTable( + $installer->getTable('vuefront_settings') + )->addColumn( + 'setting_id', + \Magento\Framework\DB\Ddl\Table::TYPE_INTEGER, + null, + ['identity' => true, 'nullable' => false, 'primary' => true], + 'SETTING ID' + )->addColumn( + 'key', + \Magento\Framework\DB\Ddl\Table::TYPE_TEXT, + 255, + ['nullable' => true], + 'SETTING key' + )->addColumn( + 'value', + \Magento\Framework\DB\Ddl\Table::TYPE_TEXT, + '64k', + ['nullable' => true], + 'SETTING value' + ); + $installer->getConnection()->createTable($table); + } + } + + $installer->endSetup(); + } +} diff --git a/etc/schemaAdmin.graphql b/etc/schemaAdmin.graphql new file mode 100644 index 0000000..8d11184 --- /dev/null +++ b/etc/schemaAdmin.graphql @@ -0,0 +1,51 @@ +type CustomerResult { + content: [Customer] + first: Boolean + last: Boolean + number: Int + numberOfElements: Int + size: Int + totalPages: Int + totalElements: Int +} +type OptionResult { + content: [Option] + first: Boolean + last: Boolean + number: Int + numberOfElements: Int + size: Int + totalPages: Int + totalElements: Int +} + +type Option { + id: String + name: String + type: String + sort_order: Int + values: [OptionValue] +} + +input InputAppSetting { + eventUrl: String + jwt: String + authUrl: String +} + +type AppSetting { + codename: String + authUrl: String + eventUrl: String + jwt: String +} + +type RootQueryType { + customersList(page: Int = 1, size: Int = 10, search: String, sort: String = "email", order: String = "ASC"): CustomerResult + customer(id: String): Customer + option(id: String): Option + optionsList(page: Int = 1, size: Int = 10, search: String, sort: String = "sort_order", order: String = "ASC"): OptionResult +} +type RootMutationType { + updateApp(name: String, settings: InputAppSetting): AppSetting +} diff --git a/view/adminhtml/web/js/pax/components/access.vue b/view/adminhtml/web/js/pax/components/access.vue new file mode 100644 index 0000000..ddb70a0 --- /dev/null +++ b/view/adminhtml/web/js/pax/components/access.vue @@ -0,0 +1,121 @@ + + + + Access key + + + You can specify Access Key to access the hidden graphl API. + + + + + + {{ saved ? $t('buttonSaved') :$t('buttonSave') }} + + + + + + + +{ + "buttonSave": "Save", + "buttonSaved": "Saved" +} + + diff --git a/view/adminhtml/web/js/pax/components/editApp.vue b/view/adminhtml/web/js/pax/components/editApp.vue new file mode 100755 index 0000000..f17e548 --- /dev/null +++ b/view/adminhtml/web/js/pax/components/editApp.vue @@ -0,0 +1,114 @@ + + modal(:value="edit" class="edit-app" btn-close @cancel="handleClose") + div(class="edit-app__title") {{$t('text_title')}} + validation-observer(v-slot="{validate}") + b-form(class="edit-app__form" @submit.stop.prevent="validate().then(onSubmit)") + validation-provider(v-slot="{errors, valid}" :name="$t('text_url')" rules="required") + b-form-group(:label="$t('text_callback_url')" label-for="input-callback-url") + b-form-input(id="input-callback-url" v-model="form.eventUrl") + b-form-group(:label="$t('text_auth_url')" label-for="input-auth-url") + b-form-input(id="input-auth-url" v-model="form.authUrl") + b-form-group(:label="$t('text_jwt')" label-for="input-jwt") + b-form-input(id="input-jwt" v-model="form.jwt") + div(class="edit-app__submit_button") + b-button(type="submit" size="lg" variant="success") {{$t('text_save')}} + + + +{ + "text_title": "Edit app", + "text_url": "URL Site", + "text_callback_url": "Url for event", + "text_access_key": "Key for access to admin api", + "text_auth_url": "Auth url", + "text_jwt": "App JWT", + "text_save": "Save" +} + + diff --git a/view/adminhtml/web/js/pax/store/settings.js b/view/adminhtml/web/js/pax/store/settings.js new file mode 100644 index 0000000..f093fef --- /dev/null +++ b/view/adminhtml/web/js/pax/store/settings.js @@ -0,0 +1,52 @@ +import gql from 'graphql-tag' +import _ from 'lodash' +export const state = () => ({ + setting: {}, + edit: false +}) + +export const mutations = { + setSetting(state, payload) { + if (_.isArray(payload) && _.isEmpty(payload)) { + state.setting = {} + } else { + state.setting = payload + } + }, + setEdit(state, payload) { + state.edit = payload + } +} + +export const getters = { + get(state) { + return state.setting + }, + edit(state) { + return state.edit + } +} + +export const actions = { + + async load({commit}) { + try { + commit('setResponseError', false, {root: true}) + const {data} = await this.$axios.get('/api/vf_settings') + + commit('setSetting', data) + } catch (e) { + commit('setResponseError', e, {root: true}) + } + }, + async edit({commit, getters}, payload) { + try { + const setting = _.merge(_.isArray(getters.get) ? Object.fromEntries(getters.get) : getters.get, payload) + commit('setResponseError', false, {root: true}) + await this.$axios.post('/api/vf_settings_edit', {setting}) + + } catch (e) { + commit('setResponseError', e, {root: true}) + } + } +} diff --git a/view/adminhtml/web/js/pax/webpack-manifest.js b/view/adminhtml/web/js/pax/webpack-manifest.js new file mode 100755 index 0000000..7c81010 --- /dev/null +++ b/view/adminhtml/web/js/pax/webpack-manifest.js @@ -0,0 +1,152 @@ +const urljoin = require('url-join'); +const path = require('path'); +const mkdirp = require('mkdirp'); +const fs = require('fs'); +const toposort = require('toposort'); + +class AssetsManifest { + constructor(options) { + this.options = { + path: undefined, + filename: 'assets-manifest.json', + extensions: ['js', 'css'], + prettyPrint: true, + metadata: undefined, + ...options, + }; + } + + attachAfterEmitHook(compiler, callback) { + // Backwards compatible version of: compiler.plugin.afterEmit.tapAsync() + if (compiler.hooks) { + compiler.hooks.afterEmit.tapAsync('webpack-manifest', callback); + } else { + compiler.plugin('after-emit', callback); + } + } + + apply(compiler) { + this.attachAfterEmitHook(compiler, (compilation, callback) => { + const opts = this.options; + const conf = compilation.options; + const base = conf.output.publicPath || ''; + + const { chunks } = compilation.getStats().toJson(); + + let sortedChunks = {}; + + if (compilation.chunkGroups) { + sortedChunks = this.sortChunkGroups(chunks, compilation.chunkGroups); + } else { + sortedChunks = this.sortChunks(chunks); + } + + const manifest = this.mapChunksToManifest(sortedChunks, { publicPath: base }); + + const dest = opts.path || conf.output.path; + const file = path.join(dest, opts.filename); + + const writeFile = (data) => { + const content = JSON.stringify(data, null, opts.prettyPrint ? 2 : null); + fs.writeFile(file, content, (err) => { + if (err) throw err; + callback(); + }); + }; + + mkdirp(dest, () => { + if (opts.metadata) { + writeFile({ files: manifest, metadata: opts.metadata }); + } else { + writeFile({ files: manifest }); + } + }); + }); + } + + sortChunkGroups(chunks, chunkGroups) { + const nodeMap = {}; + chunks.forEach((chunk) => { + nodeMap[chunk.id] = chunk; + }); + + const edges = chunkGroups.reduce((result, chunkGroup) => result.concat(Array.from(chunkGroup.parentsIterable, parentGroup => [parentGroup, chunkGroup])), []); + const sortedGroups = toposort.array(chunkGroups, edges); + // flatten chunkGroup into chunks + const sortedChunks = sortedGroups + .reduce((result, chunkGroup) => result.concat(chunkGroup.chunks), []) + .map(chunk => nodeMap[chunk.id]) + .filter((chunk, index, self) => { + // make sure exists (ie excluded chunks not in nodeMap) + const exists = !!chunk; + // make sure we have a unique list + const unique = self.indexOf(chunk) === index; + return exists && unique; + }); + return sortedChunks; + } + + sortChunks(chunks) { + const nodes = {}; + + chunks.forEach((chunk) => { + nodes[chunk.id] = chunk; + }); + + // Next, we add an edge for each parent relationship into the graph + const edges = []; + + chunks.forEach((chunk) => { + if (chunk.parents) { + // Add an edge for each parent (parent -> child) + chunk.parents.forEach((parentId) => { + // webpack2 chunk.parents are chunks instead of string id(s) + const parentChunk = nodes[parentId]; + // If the parent chunk does not exist (e.g. because of an excluded chunk) + // we ignore that parent + if (parentChunk) { + edges.push([parentChunk, chunk]); + } + }); + } + }); + + // We now perform a topological sorting on the input chunks and built edges + return toposort.array(chunks, edges); + } + + mapChunksToManifest(chunks, manifestOptions = {}) { + const options = { + publicPath: '', + ...manifestOptions, + }; + + + const nextElement = ([head, ...tail]) => { + if (!head) { + return null; + } + + return { + id: head.id, + next: nextElement(tail), + ...this.options.extensions.reduce((acc, extension) => ({ + ...acc, + ...this.filterChunkFilesByExtension(head.files, extension, options.publicPath), + }), {}), + }; + }; + + return nextElement(chunks); + } + + filterChunkFilesByExtension(files, extension, publicPath) { + return { + [extension]: files + .filter(file => file.endsWith(extension)) + .map(file => (publicPath.length === 0 ? file : urljoin(publicPath, file))), + }; + } +} + +module.exports = AssetsManifest;