import for tax rate
This commit is contained in:
parent
16a7c87926
commit
fdcf420af9
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -1,4 +1,4 @@
|
||||||
{
|
{
|
||||||
"/js/admin.js": "/js/admin.js?id=da5ebef9c25a064e7ed6",
|
"/js/admin.js": "/js/admin.js",
|
||||||
"/css/admin.css": "/css/admin.css?id=7381eeebea31109e7088"
|
"/css/admin.css": "/css/admin.css"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -599,6 +599,10 @@ Route::group(['middleware' => ['web']], function () {
|
||||||
])->name('admin.tax-rates.update');
|
])->name('admin.tax-rates.update');
|
||||||
|
|
||||||
Route::get('/tax-rates/delete/{id}', 'Webkul\Tax\Http\Controllers\TaxRateController@destroy')->name('admin.tax-rates.delete');
|
Route::get('/tax-rates/delete/{id}', 'Webkul\Tax\Http\Controllers\TaxRateController@destroy')->name('admin.tax-rates.delete');
|
||||||
|
|
||||||
|
Route::post('/tax-rates/import', 'Webkul\Tax\Http\Controllers\TaxRateController@import')->defaults('_config', [
|
||||||
|
'redirect' => 'admin.tax-rates.index'
|
||||||
|
])->name('admin.tax-rates.import');
|
||||||
//tax rate ends
|
//tax rate ends
|
||||||
|
|
||||||
//DataGrid Export
|
//DataGrid Export
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Webkul\Admin\Imports;
|
||||||
|
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
use Maatwebsite\Excel\Concerns\ToCollection;
|
||||||
|
use Maatwebsite\Excel\Concerns\WithHeadingRow;
|
||||||
|
use Maatwebsite\Excel\Concerns\Importable;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DataGridImport class
|
||||||
|
*
|
||||||
|
* @author Rahul Shukla <rahulshukla.symfony517@webkul.com>
|
||||||
|
* @copyright 2018 Webkul Software Pvt Ltd (http://www.webkul.com)
|
||||||
|
*/
|
||||||
|
|
||||||
|
class DataGridImport implements ToCollection, WithHeadingRow
|
||||||
|
{
|
||||||
|
use Importable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $row
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function collection(Collection $rows)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -544,10 +544,10 @@ body {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.export {
|
.export-import {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
.export-icon {
|
.export-icon, .import-icon {
|
||||||
position: relative;
|
position: relative;
|
||||||
top: 10px;
|
top: 10px;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -747,11 +747,14 @@ return [
|
||||||
],
|
],
|
||||||
|
|
||||||
'export' => [
|
'export' => [
|
||||||
'export' => 'Export',
|
'export' => 'Export',
|
||||||
'format' => 'Select Format',
|
'import' => 'Import',
|
||||||
'download' => 'Download',
|
'format' => 'Select Format',
|
||||||
'csv' => 'CSV',
|
'download' => 'Download',
|
||||||
'xls' => 'XLS'
|
'upload' => 'Upload',
|
||||||
|
'csv' => 'CSV',
|
||||||
|
'xls' => 'XLS',
|
||||||
|
'file' => 'File'
|
||||||
],
|
],
|
||||||
|
|
||||||
'response' => [
|
'response' => [
|
||||||
|
|
@ -763,6 +766,7 @@ return [
|
||||||
'attribute-error' => ':name is used in configurable products.',
|
'attribute-error' => ':name is used in configurable products.',
|
||||||
'attribute-product-error' => ':name is used in products.',
|
'attribute-product-error' => ':name is used in products.',
|
||||||
'customer-associate' => ':name can not be deleted because customer is associated with this group.',
|
'customer-associate' => ':name can not be deleted because customer is associated with this group.',
|
||||||
'currency-delete-error' => 'This currency is set as channel base currency so it can not be deleted.'
|
'currency-delete-error' => 'This currency is set as channel base currency so it can not be deleted.',
|
||||||
|
'upload-success' => ':name uploaded successfully.'
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|
@ -12,7 +12,7 @@
|
||||||
<div class="page-title">
|
<div class="page-title">
|
||||||
<h1>
|
<h1>
|
||||||
<i class="icon angle-left-icon back-link" onclick="history.length > 1 ? history.go(-1) : window.location = '{{ url('/admin/dashboard') }}';"></i>
|
<i class="icon angle-left-icon back-link" onclick="history.length > 1 ? history.go(-1) : window.location = '{{ url('/admin/dashboard') }}';"></i>
|
||||||
|
|
||||||
{{ __('admin::app.catalog.families.add-title') }}
|
{{ __('admin::app.catalog.families.add-title') }}
|
||||||
</h1>
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -222,7 +222,7 @@
|
||||||
<script type="text/x-template" id="country-template">
|
<script type="text/x-template" id="country-template">
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<select type="text" v-validate="'required'" class="control" id="{{ $firstField }}[{{ $secondField }}][{{ $thirdField }}][{{ $field['name'] }}]" name="{{ $firstField }}[{{ $secondField }}][{{ $thirdField }}][{{ $field['name'] }}]" v-model="country" data-vv-as=""{{ __('admin::app.customers.customers.country') }}"" @change="someHandler">
|
<select type="text" v-validate="'required'" class="control" id="{{ $firstField }}[{{ $secondField }}][{{ $thirdField }}][{{ $field['name'] }}]" name="{{ $firstField }}[{{ $secondField }}][{{ $thirdField }}][country]" v-model="country" data-vv-as=""{{ __('admin::app.customers.customers.country') }}"" @change="someHandler">
|
||||||
<option value=""></option>
|
<option value=""></option>
|
||||||
|
|
||||||
@foreach (core()->countries() as $country)
|
@foreach (core()->countries() as $country)
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@
|
||||||
<h1>{{ __('admin::app.customers.customers.title') }}</h1>
|
<h1>{{ __('admin::app.customers.customers.title') }}</h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="page-action">
|
<div class="page-action">
|
||||||
<div class="export" @click="showModal('downloadDataGrid')">
|
<div class="export-import" @click="showModal('downloadDataGrid')">
|
||||||
<i class="export-icon"></i>
|
<i class="export-icon"></i>
|
||||||
<span >
|
<span >
|
||||||
{{ __('admin::app.export.export') }}
|
{{ __('admin::app.export.export') }}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="page-action">
|
<div class="page-action">
|
||||||
<div class="export" @click="showModal('downloadDataGrid')">
|
<div class="export-import" @click="showModal('downloadDataGrid')">
|
||||||
<i class="export-icon"></i>
|
<i class="export-icon"></i>
|
||||||
<span>
|
<span>
|
||||||
{{ __('admin::app.export.export') }}
|
{{ __('admin::app.export.export') }}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="page-action">
|
<div class="page-action">
|
||||||
<div class="export" @click="showModal('downloadDataGrid')">
|
<div class="export-import" @click="showModal('downloadDataGrid')">
|
||||||
<i class="export-icon"></i>
|
<i class="export-icon"></i>
|
||||||
<span>
|
<span>
|
||||||
{{ __('admin::app.export.export') }}
|
{{ __('admin::app.export.export') }}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="page-action">
|
<div class="page-action">
|
||||||
<div class="export" @click="showModal('downloadDataGrid')">
|
<div class="export-import" @click="showModal('downloadDataGrid')">
|
||||||
<i class="export-icon"></i>
|
<i class="export-icon"></i>
|
||||||
<span>
|
<span>
|
||||||
{{ __('admin::app.export.export') }}
|
{{ __('admin::app.export.export') }}
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,19 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="page-action">
|
<div class="page-action">
|
||||||
|
<div class="export-import" @click="showModal('uploadDataGrid')" style="margin-right: 20px;">
|
||||||
|
<i class="import-icon"></i>
|
||||||
|
<span>
|
||||||
|
{{ __('admin::app.export.import') }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="export-import" @click="showModal('downloadDataGrid')">
|
||||||
|
<i class="export-icon"></i>
|
||||||
|
<span>
|
||||||
|
{{ __('admin::app.export.export') }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
<a href="{{ route('admin.tax-rates.show') }}" class="btn btn-lg btn-primary">
|
<a href="{{ route('admin.tax-rates.show') }}" class="btn btn-lg btn-primary">
|
||||||
{{ __('admin::app.settings.tax-rates.add-title') }}
|
{{ __('admin::app.settings.tax-rates.add-title') }}
|
||||||
</a>
|
</a>
|
||||||
|
|
@ -23,4 +36,81 @@
|
||||||
{!! $taxrates->render() !!}
|
{!! $taxrates->render() !!}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@endsection
|
|
||||||
|
<modal id="downloadDataGrid" :is-open="modalIds.downloadDataGrid">
|
||||||
|
<h3 slot="header">{{ __('admin::app.export.download') }}</h3>
|
||||||
|
<div slot="body">
|
||||||
|
<export-form></export-form>
|
||||||
|
</div>
|
||||||
|
</modal>
|
||||||
|
|
||||||
|
<modal id="uploadDataGrid" :is-open="modalIds.uploadDataGrid">
|
||||||
|
<h3 slot="header">{{ __('admin::app.export.upload') }}</h3>
|
||||||
|
<div slot="body">
|
||||||
|
|
||||||
|
<form method="POST" action="{{ route('admin.tax-rates.import') }}" enctype="multipart/form-data" @submit.prevent="onSubmit">
|
||||||
|
@csrf()
|
||||||
|
<div class="control-group" :class="[errors.has('tax_rate') ? 'has-error' : '']">
|
||||||
|
<label for="tax_rate" class="required">{{ __('admin::app.export.file') }}</label>
|
||||||
|
<input v-validate="'required|mimes:doc,csv,xlsx,xls'" type="file" class="control" id="tax_rate" name="tax_rate" data-vv-as=""{{ __('admin::app.export.file') }}"" value="{{ old('tax_rate') }}"/ style="padding-top: 5px">
|
||||||
|
<span class="control-error" v-if="errors.has('tax_rate')">@{{ errors.first('tax_rate') }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button type="submit" class="btn btn-lg btn-primary">
|
||||||
|
{{ __('admin::app.export.import') }}
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</modal>
|
||||||
|
|
||||||
|
@endsection
|
||||||
|
|
||||||
|
@push('scripts')
|
||||||
|
|
||||||
|
<script type="text/x-template" id="export-form-template">
|
||||||
|
<form method="POST" action="{{ route('admin.datagrid.export') }}">
|
||||||
|
|
||||||
|
<div class="page-content">
|
||||||
|
<div class="form-container">
|
||||||
|
@csrf()
|
||||||
|
|
||||||
|
<?php
|
||||||
|
$data = json_encode((array) $taxrates);
|
||||||
|
?>
|
||||||
|
|
||||||
|
<input type="hidden" name="gridData" value="{{ $data }}">
|
||||||
|
<input type="hidden" name="file_name" value="Tax rates">
|
||||||
|
|
||||||
|
<div class="control-group">
|
||||||
|
<label for="format" class="required">
|
||||||
|
{{ __('admin::app.export.format') }}
|
||||||
|
</label>
|
||||||
|
<select name="format" class="control" v-validate="'required'">
|
||||||
|
<option value="xls">{{ __('admin::app.export.xls') }}</option>
|
||||||
|
<option value="csv">{{ __('admin::app.export.csv') }}</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button type="submit" class="btn btn-lg btn-primary" @click="closeModal">
|
||||||
|
{{ __('admin::app.export.export') }}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
</form>
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
Vue.component('export-form', {
|
||||||
|
template: '#export-form-template',
|
||||||
|
methods: {
|
||||||
|
closeModal () {
|
||||||
|
this.$parent.closeModal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
@endpush
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
const { mix } = require("laravel-mix");
|
const { mix } = require("laravel-mix");
|
||||||
require("laravel-mix-merge-manifest");
|
require("laravel-mix-merge-manifest");
|
||||||
|
|
||||||
var publicPath = 'publishable/assets';
|
// var publicPath = 'publishable/assets';
|
||||||
// var publicPath = "../../../public/vendor/webkul/admin/assets";
|
var publicPath = "../../../public/vendor/webkul/admin/assets";
|
||||||
|
|
||||||
mix.setPublicPath(publicPath).mergeManifest();
|
mix.setPublicPath(publicPath).mergeManifest();
|
||||||
mix.disableNotifications();
|
mix.disableNotifications();
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,9 @@ use Illuminate\Http\Request;
|
||||||
use Illuminate\Http\Response;
|
use Illuminate\Http\Response;
|
||||||
use Illuminate\Support\Facades\Event;
|
use Illuminate\Support\Facades\Event;
|
||||||
use Webkul\Tax\Repositories\TaxRateRepository as TaxRate;
|
use Webkul\Tax\Repositories\TaxRateRepository as TaxRate;
|
||||||
|
use Webkul\Admin\Imports\DataGridImport;
|
||||||
|
use Illuminate\Support\Facades\Validator;
|
||||||
|
use Excel;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tax controller
|
* Tax controller
|
||||||
|
|
@ -144,7 +146,7 @@ class TaxRateController extends Controller
|
||||||
return redirect()->route($this->_config['redirect']);
|
return redirect()->route($this->_config['redirect']);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove the specified resource from storage.
|
* Remove the specified resource from storage.
|
||||||
*
|
*
|
||||||
* @param int $id
|
* @param int $id
|
||||||
|
|
@ -167,4 +169,96 @@ class TaxRateController extends Controller
|
||||||
|
|
||||||
return redirect()->back();
|
return redirect()->back();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* import function for the upload
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Http\Response
|
||||||
|
*/
|
||||||
|
public function import() {
|
||||||
|
|
||||||
|
$excelData = (new DataGridImport)->toArray(request()->file('tax_rate'));
|
||||||
|
|
||||||
|
foreach ($excelData as $data) {
|
||||||
|
foreach ($data as $column => $uploadData) {
|
||||||
|
$validator = Validator::make($uploadData, [
|
||||||
|
'identifier' => 'required|string',
|
||||||
|
'state' => 'required|string',
|
||||||
|
'country' => 'required|string',
|
||||||
|
'tax_rate' => 'required|numeric'
|
||||||
|
]);
|
||||||
|
|
||||||
|
if ($validator->fails()) {
|
||||||
|
$failedRules[$column+1] = $validator->errors();
|
||||||
|
}
|
||||||
|
|
||||||
|
$identiFier[$column+1] = $uploadData['identifier'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$identiFierCount = array_count_values($identiFier);
|
||||||
|
|
||||||
|
$filtered = array_filter($identiFier, function ($value) use ($identiFierCount) {
|
||||||
|
return $identiFierCount[$value] > 1;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($filtered) {
|
||||||
|
foreach ($filtered as $position => $identifier) {
|
||||||
|
$message[] = 'Identifier must be unique, duplicate identifier' . ' ' . $identifier. ' at row' . ' ' . $position. '.';
|
||||||
|
}
|
||||||
|
$finalMsg = implode(" ", $message);
|
||||||
|
|
||||||
|
session()->flash('error', $finalMsg);
|
||||||
|
} else {
|
||||||
|
$errorMsg = [];
|
||||||
|
|
||||||
|
if (isset($failedRules)) {
|
||||||
|
foreach ($failedRules as $coulmn => $fail) {
|
||||||
|
if ($fail->first('identifier')){
|
||||||
|
$errorMsg[$coulmn] = $fail->first('identifier');
|
||||||
|
} else if ($fail->first('tax_rate')) {
|
||||||
|
$errorMsg[$coulmn] = $fail->first('tax_rate');
|
||||||
|
} else if ($fail->first('country')) {
|
||||||
|
$errorMsg[$coulmn] = $fail->first('country');
|
||||||
|
} else if ($fail->first('state')) {
|
||||||
|
$errorMsg[$coulmn] = $fail->first('state');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($errorMsg as $key => $msg) {
|
||||||
|
$msg = str_replace(".", "", $msg);
|
||||||
|
$message[] = $msg. ' at Row ' .$key . '.';
|
||||||
|
}
|
||||||
|
|
||||||
|
$finalMsg = implode(" ", $message);
|
||||||
|
|
||||||
|
session()->flash('error', $finalMsg);
|
||||||
|
} else {
|
||||||
|
$taxRate = $this->taxRate->get()->toArray();
|
||||||
|
|
||||||
|
foreach ($taxRate as $rate) {
|
||||||
|
$rateIdentifier[$rate['id']] = $rate['identifier'];
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($excelData as $data) {
|
||||||
|
foreach ($data as $column => $uploadData) {
|
||||||
|
if (isset($rateIdentifier)) {
|
||||||
|
$id = array_search($uploadData['identifier'], $rateIdentifier);
|
||||||
|
if ($id) {
|
||||||
|
$this->taxRate->update($uploadData, $id);
|
||||||
|
} else {
|
||||||
|
$this->taxRate->create($uploadData);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$this->taxRate->create($uploadData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
session()->flash('success', trans('admin::app.response.upload-success', ['name' => 'Tax Rate']));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return redirect()->route($this->_config['redirect']);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg width="32px" height="32px" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<!-- Generator: Sketch 50 (54983) - http://www.bohemiancoding.com/sketch -->
|
||||||
|
<title>Icon-Import</title>
|
||||||
|
<desc>Created with Sketch.</desc>
|
||||||
|
<defs></defs>
|
||||||
|
<g id="Icon-Import" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<g transform="translate(2.000000, 6.000000)" stroke="#979797" stroke-width="2">
|
||||||
|
<path d="M29,5.5 C29,2.78076172 29,0.947428385 29,0 L10,0 L10,20 L29,20 L29,14.5" id="Path-2" transform="translate(19.500000, 10.000000) scale(-1, 1) translate(-19.500000, -10.000000) "></path>
|
||||||
|
<path d="M0,10 L20.068125,10" id="Path-3"></path>
|
||||||
|
<polyline id="Path-4" points="17 5.21679688 22 10.1761378 17 15"></polyline>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 960 B |
|
|
@ -299,6 +299,11 @@
|
||||||
height: 32px;
|
height: 32px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.import-icon {
|
||||||
|
background-image: url("../images/Icon-Import.svg");
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
.star-blue-icon {
|
.star-blue-icon {
|
||||||
width: 17px;
|
width: 17px;
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
const { mix } = require("laravel-mix");
|
const { mix } = require("laravel-mix");
|
||||||
require("laravel-mix-merge-manifest");
|
require("laravel-mix-merge-manifest");
|
||||||
|
|
||||||
var publicPath = 'publishable/assets';
|
// var publicPath = 'publishable/assets';
|
||||||
// var publicPath = "../../../public/vendor/webkul/ui/assets";
|
var publicPath = "../../../public/vendor/webkul/ui/assets";
|
||||||
|
|
||||||
mix.setPublicPath(publicPath).mergeManifest();
|
mix.setPublicPath(publicPath).mergeManifest();
|
||||||
mix.disableNotifications();
|
mix.disableNotifications();
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@ class SessionController extends Controller
|
||||||
return redirect()->route('admin.dashboard.index');
|
return redirect()->route('admin.dashboard.index');
|
||||||
} else {
|
} else {
|
||||||
session()->put('url.intended', url()->previous());
|
session()->put('url.intended', url()->previous());
|
||||||
|
|
||||||
return view($this->_config['view']);
|
return view($this->_config['view']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="control-group" id="host_name">
|
<div class="control-group" id="host_name">
|
||||||
<label for="host_name" class="required">Databse Host</label>
|
<label for="host_name" class="required">Database Host</label>
|
||||||
<input type="text" name="host_name" class="control"
|
<input type="text" name="host_name" class="control"
|
||||||
placeholder="127.0.0.1"
|
placeholder="127.0.0.1"
|
||||||
data-validation="required length" data-validation-length="max50">
|
data-validation="required length" data-validation-length="max50">
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue