first upload all files
This commit is contained in:
114
Modules/Option/Resources/assets/admin/js/BaseOption.js
Normal file
114
Modules/Option/Resources/assets/admin/js/BaseOption.js
Normal file
@@ -0,0 +1,114 @@
|
||||
export default class {
|
||||
addOptionsErrors(errors) {
|
||||
for (let key in errors) {
|
||||
let inputField = this.getInputFieldForErrorKey(key);
|
||||
|
||||
inputField.closest('.option').addClass('option-has-errors');
|
||||
|
||||
let parent = inputField.parent();
|
||||
|
||||
parent.append(`<span class="help-block">${errors[key][0]}</span>`);
|
||||
}
|
||||
}
|
||||
|
||||
getRowTemplate(data) {
|
||||
let template = _.template($('#option-select-values-template').html());
|
||||
|
||||
return $(template(data));
|
||||
}
|
||||
|
||||
changeOptionType({ optionId, type, values = [] }) {
|
||||
let optionValuesElement = this.getOptionValuesElement(optionId);
|
||||
let templateType = this.getTemplateType(type, optionValuesElement);
|
||||
let optionValuesData = { optionId, value: { id: '', label: '', price: '', price_type: 'fixed' } };
|
||||
|
||||
if (this.shouldNotChangeTemplate(templateType, optionValuesElement)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (values.length !== 0 && templateType === 'text') {
|
||||
optionValuesData.value = values[0];
|
||||
}
|
||||
|
||||
let template = _.template($(`#option-${templateType}-template`).html());
|
||||
|
||||
optionValuesElement.html(template(optionValuesData));
|
||||
|
||||
if (templateType === 'select') {
|
||||
this.addOptionRowEventListener(optionId);
|
||||
|
||||
this.addOptionRows({ optionId, values });
|
||||
|
||||
if (values.length === 0) {
|
||||
this.getAddNewRowButton(optionId).trigger('click');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
addOptionRows({ optionId, values }) {
|
||||
for (let [index, value] of values.entries()) {
|
||||
this.addOptionRow({
|
||||
optionId,
|
||||
valueId: index,
|
||||
value,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
getTemplateType(type) {
|
||||
if (this.templateTypeIsText(type)) {
|
||||
return 'text';
|
||||
}
|
||||
|
||||
if (this.templateTypeIsSelect(type)) {
|
||||
return 'select';
|
||||
}
|
||||
}
|
||||
|
||||
templateTypeIsText(type) {
|
||||
return ['field', 'textarea', 'date', 'date_time', 'time'].includes(type);
|
||||
}
|
||||
|
||||
templateTypeIsSelect(type) {
|
||||
return ['dropdown', 'checkbox', 'checkbox_custom', 'radio', 'radio_custom', 'multiple_select'].includes(type);
|
||||
}
|
||||
|
||||
shouldNotChangeTemplate(templateType, optionValuesElement) {
|
||||
return templateType === undefined || this.alreadyHasCurrentTemplate(templateType, optionValuesElement);
|
||||
}
|
||||
|
||||
alreadyHasCurrentTemplate(templateType, optionValuesElement) {
|
||||
if (templateType === 'text') {
|
||||
return optionValuesElement.children().hasClass('option-text');
|
||||
}
|
||||
|
||||
if (templateType === 'select') {
|
||||
return optionValuesElement.children().hasClass('option-select');
|
||||
}
|
||||
}
|
||||
|
||||
initOptionRow(template, selectValues) {
|
||||
if (selectValues.length !== 0 && ! selectValues.is('.sortable')) {
|
||||
this.makeSortable(selectValues[0]);
|
||||
|
||||
selectValues.addClass('sortable');
|
||||
}
|
||||
|
||||
this.deleteOptionRowEventListener(template);
|
||||
|
||||
window.admin.tooltip();
|
||||
}
|
||||
|
||||
deleteOptionRowEventListener(row) {
|
||||
row.find('.delete-row').on('click', (e) => {
|
||||
$(e.currentTarget).closest('.option-row').remove();
|
||||
});
|
||||
}
|
||||
|
||||
makeSortable(el) {
|
||||
Sortable.create(el, {
|
||||
handle: '.drag-icon',
|
||||
animation: 150,
|
||||
});
|
||||
}
|
||||
}
|
||||
53
Modules/Option/Resources/assets/admin/js/Option.js
Normal file
53
Modules/Option/Resources/assets/admin/js/Option.js
Normal file
@@ -0,0 +1,53 @@
|
||||
import BaseOption from './BaseOption';
|
||||
|
||||
export default class extends BaseOption {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
let values = FleetCart.data['option.values'];
|
||||
|
||||
$('#type').on('change', (e) => {
|
||||
super.changeOptionType({ type: e.currentTarget.value, values });
|
||||
super.addOptionsErrors(FleetCart.errors['option.values']);
|
||||
});
|
||||
|
||||
$('#type').trigger('change');
|
||||
|
||||
window.admin.removeSubmitButtonOffsetOn('#values');
|
||||
}
|
||||
|
||||
addOptionRow({ valueId, value = { label: '', price: '', price_type: 'fixed' } }) {
|
||||
let template = this.getRowTemplate({ optionId: undefined, valueId, value });
|
||||
|
||||
let selectValues = $('#select-values').append(template);
|
||||
|
||||
super.initOptionRow(template, selectValues);
|
||||
}
|
||||
|
||||
addOptionRowEventListener() {
|
||||
$('#add-new-row').on('click', () => {
|
||||
let valueId = $('#option-values .option-row').length;
|
||||
|
||||
this.addOptionRow({ valueId });
|
||||
});
|
||||
}
|
||||
|
||||
getOptionValuesElement() {
|
||||
return $('#option-values');
|
||||
}
|
||||
|
||||
getAddNewRowButton() {
|
||||
return $('#add-new-row');
|
||||
}
|
||||
|
||||
getInputFieldForErrorKey(key) {
|
||||
let keyParts = key.split('.');
|
||||
|
||||
// Replace all "_" to "-".
|
||||
keyParts = keyParts.map(k => {
|
||||
return k.split('_').join('-');
|
||||
});
|
||||
|
||||
return $(`#${keyParts.join('-')}`);
|
||||
}
|
||||
}
|
||||
161
Modules/Option/Resources/assets/admin/js/ProductOption.js
Normal file
161
Modules/Option/Resources/assets/admin/js/ProductOption.js
Normal file
@@ -0,0 +1,161 @@
|
||||
import BaseOption from './BaseOption';
|
||||
|
||||
export default class extends BaseOption {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.optionsCount = 0;
|
||||
|
||||
this.addOptions(FleetCart.data['product.options']);
|
||||
|
||||
if (this.optionsCount === 0) {
|
||||
this.addOption();
|
||||
}
|
||||
|
||||
if (this.optionsCount > 3) {
|
||||
this.collapseOptions();
|
||||
}
|
||||
|
||||
super.addOptionsErrors(FleetCart.errors['product.options']);
|
||||
|
||||
$('#add-new-option').on('click', () => this.addOption());
|
||||
$('#add-global-option').on('click', () => this.addGlobalOption());
|
||||
}
|
||||
|
||||
addOptions(options) {
|
||||
for (let option of options) {
|
||||
this.addOption(option);
|
||||
}
|
||||
}
|
||||
|
||||
collapseOptions() {
|
||||
let options = $('.option:not(.option-has-errors)');
|
||||
|
||||
for (let option of options) {
|
||||
$(option).find('[data-toggle=collapse]').trigger('click');
|
||||
}
|
||||
}
|
||||
|
||||
addGlobalOption() {
|
||||
let globalOptionId = $('#global-option').val();
|
||||
|
||||
if (globalOptionId === '') {
|
||||
return window.admin.stopButtonLoading($('#add-global-option'));
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
type: 'GET',
|
||||
url: route('admin.options.show', globalOptionId),
|
||||
dataType: 'json',
|
||||
success: option => {
|
||||
this.addOption(option);
|
||||
|
||||
window.admin.stopButtonLoading($('#add-global-option'));
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
addOption(option = { id: null, name: null, type: null, is_required: false, values: [] }) {
|
||||
// Cast "is_required" field to boolean.
|
||||
option.is_required = !! JSON.parse(option.is_required);
|
||||
|
||||
let optionId = this.optionsCount;
|
||||
|
||||
let template = _.template($('#option-template').html());
|
||||
let html = $(template({ option, optionId }));
|
||||
|
||||
if (option.name !== null) {
|
||||
setTimeout(() => {
|
||||
$(`#option-${optionId}`).find('#option-name').text(option.name);
|
||||
});
|
||||
}
|
||||
|
||||
let optionGroup = $('#options-group').append(html);
|
||||
|
||||
if (! optionGroup.is('.sortable')) {
|
||||
super.makeSortable(optionGroup[0]);
|
||||
|
||||
optionGroup.addClass('sortable');
|
||||
}
|
||||
|
||||
this.deleteOptionEventListener(html);
|
||||
this.updateOptionNameEventListener(optionId);
|
||||
this.updateTemplateEventListener(optionId, option['values']);
|
||||
|
||||
window.admin.tooltip();
|
||||
|
||||
this.optionsCount++;
|
||||
}
|
||||
|
||||
deleteOptionEventListener(option) {
|
||||
option.find('.delete-option').on('click', (e) => $(e.currentTarget).closest('.option').remove());
|
||||
}
|
||||
|
||||
updateOptionNameEventListener(optionId) {
|
||||
let option = $(`#option-${optionId}`);
|
||||
let old = option.find('#option-name').text();
|
||||
|
||||
$(option).find('.option-name-field').on('input', (e) => {
|
||||
let name = e.currentTarget.value !== '' ? e.currentTarget.value : old;
|
||||
|
||||
option.find('#option-name').text(name);
|
||||
});
|
||||
}
|
||||
|
||||
updateTemplateEventListener(optionId, values = []) {
|
||||
let optionTypeElement = $(`#option-${optionId}-type`);
|
||||
|
||||
optionTypeElement.on('change', (e) => {
|
||||
let type = e.currentTarget.value;
|
||||
|
||||
if (type === '') {
|
||||
return this.getOptionValuesElement(optionId).html('');
|
||||
}
|
||||
|
||||
super.changeOptionType({ optionId, type, values });
|
||||
});
|
||||
|
||||
// Trigger the "change" event on option type after attaching the listener
|
||||
// this will automatically take effect of the current option which is
|
||||
// maybe loaded from the old input values or from the product data.
|
||||
optionTypeElement.trigger('change');
|
||||
}
|
||||
|
||||
addOptionRow({ optionId, valueId, value = { label: '', price: '', price_type: 'fixed' } }) {
|
||||
let template = this.getRowTemplate({ optionId, valueId, value });
|
||||
|
||||
let selectValues = $(`#option-${optionId}-select-values`).append(template);
|
||||
|
||||
super.initOptionRow(template, selectValues);
|
||||
}
|
||||
|
||||
addOptionRowEventListener(optionId) {
|
||||
$(`#option-${optionId}-add-new-row`).on('click', () => {
|
||||
let valueId = $(`#option-${optionId}-values .option-row`).length;
|
||||
|
||||
this.addOptionRow({ optionId, valueId });
|
||||
});
|
||||
}
|
||||
|
||||
getOptionValuesElement(optionId) {
|
||||
return $(`#option-${optionId}-values`);
|
||||
}
|
||||
|
||||
getAddNewRowButton(optionId) {
|
||||
return $(`#option-${optionId}-add-new-row`);
|
||||
}
|
||||
|
||||
getInputFieldForErrorKey(key) {
|
||||
let keyParts = key.split('.');
|
||||
|
||||
// Remove the first element which is "option".
|
||||
keyParts.shift();
|
||||
|
||||
// Replace all "_" to "-".
|
||||
keyParts = keyParts.map(k => {
|
||||
return k.split('_').join('-');
|
||||
});
|
||||
|
||||
return $(`#option-${keyParts.join('-')}`);
|
||||
}
|
||||
}
|
||||
10
Modules/Option/Resources/assets/admin/js/main.js
Normal file
10
Modules/Option/Resources/assets/admin/js/main.js
Normal file
@@ -0,0 +1,10 @@
|
||||
import Option from './Option';
|
||||
import ProductOption from './ProductOption';
|
||||
|
||||
if ($('#option-create-form, #option-edit-form').length !== 0) {
|
||||
new Option();
|
||||
}
|
||||
|
||||
if ($('#product-create-form, #product-edit-form').length !== 0) {
|
||||
new ProductOption();
|
||||
}
|
||||
127
Modules/Option/Resources/assets/admin/sass/main.scss
Normal file
127
Modules/Option/Resources/assets/admin/sass/main.scss
Normal file
@@ -0,0 +1,127 @@
|
||||
.new-option {
|
||||
.checkbox {
|
||||
margin: 30px 0 0;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.delete-option {
|
||||
margin-top: 25px;
|
||||
padding: 10px 15px;
|
||||
}
|
||||
}
|
||||
|
||||
.options-group-wrapper {
|
||||
.panel-title a {
|
||||
position: relative;
|
||||
text-decoration: none;
|
||||
overflow: hidden;
|
||||
|
||||
> .drag-icon {
|
||||
font-size: 16px;
|
||||
color: #737881;
|
||||
cursor: move;
|
||||
vertical-align: top;
|
||||
margin: 4px 10px 0 0;
|
||||
white-space: nowrap;
|
||||
display: inline-block;
|
||||
i {
|
||||
float: left;
|
||||
|
||||
&:nth-child(2) {
|
||||
margin-left: 1px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.options {
|
||||
.form-group {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
thead th {
|
||||
&:first-child {
|
||||
width: 40px;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
width: 60px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.option-select {
|
||||
.options {
|
||||
.option-row {
|
||||
td {
|
||||
&:nth-child(2) {
|
||||
min-width: 180px;
|
||||
}
|
||||
|
||||
&:nth-child(3) {
|
||||
min-width: 120px;
|
||||
}
|
||||
|
||||
&:nth-child(4) {
|
||||
min-width: 110px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.add-global-option {
|
||||
.form-group {
|
||||
margin-left: 0 !important;
|
||||
margin-right: 10px !important;
|
||||
}
|
||||
|
||||
> button {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@media screen and (max-width: 1199px) {
|
||||
.add-global-option {
|
||||
float: none !important;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.new-option {
|
||||
.checkbox {
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.delete-option {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.p-l-0 {
|
||||
padding-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.options-group-wrapper {
|
||||
.option-values {
|
||||
.option-text,
|
||||
.option-select {
|
||||
margin-top: 15px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 991px) {
|
||||
.new-option {
|
||||
.checkbox {
|
||||
padding-top: 7px;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user