¨4.0.1¨
This commit is contained in:
25
Modules/Variation/Admin/VariationTable.php
Normal file
25
Modules/Variation/Admin/VariationTable.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Variation\Admin;
|
||||
|
||||
use Modules\Admin\Ui\AdminTable;
|
||||
use Yajra\DataTables\EloquentDataTable;
|
||||
use Yajra\DataTables\Exceptions\Exception;
|
||||
|
||||
class VariationTable extends AdminTable
|
||||
{
|
||||
/**
|
||||
* Make table response for the resource.
|
||||
*
|
||||
* @return EloquentDataTable
|
||||
* @throws Exception
|
||||
*/
|
||||
public function make(): EloquentDataTable
|
||||
{
|
||||
return $this->newTable()
|
||||
->editColumn('type', function ($variation) {
|
||||
return trans("variation::variations.form.variation_types.{$variation->type}");
|
||||
})
|
||||
->removeColumn('values');
|
||||
}
|
||||
}
|
||||
10
Modules/Variation/Config/permissions.php
Normal file
10
Modules/Variation/Config/permissions.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'admin.variations' => [
|
||||
'index' => 'variation::permissions.variations.index',
|
||||
'create' => 'variation::permissions.variations.create',
|
||||
'edit' => 'variation::permissions.variations.edit',
|
||||
'destroy' => 'variation::permissions.variations.destroy',
|
||||
],
|
||||
];
|
||||
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class CreateVariationsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('variations', function (Blueprint $table) {
|
||||
$table->increments('id');
|
||||
$table->string('uid')->unique();
|
||||
$table->string('type');
|
||||
$table->boolean('is_global')->default(true);
|
||||
$table->integer('position')->unsigned()->nullable();
|
||||
$table->softDeletes();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('variations');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class CreateVariationTranslationsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('variation_translations', function (Blueprint $table) {
|
||||
$table->increments('id');
|
||||
$table->integer('variation_id')->unsigned();
|
||||
$table->string('locale');
|
||||
$table->string('name');
|
||||
|
||||
$table->unique(['variation_id', 'locale']);
|
||||
$table->foreign('variation_id')->references('id')->on('variations')->onDelete('cascade');
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('variation_translations');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class CreateVariationValuesTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('variation_values', function (Blueprint $table) {
|
||||
$table->increments('id');
|
||||
$table->string('uid')->unique();
|
||||
$table->integer('variation_id')->unsigned()->index();
|
||||
$table->string('value')->nullable();
|
||||
$table->integer('position')->unsigned()->nullable();
|
||||
$table->timestamps();
|
||||
|
||||
$table->foreign('variation_id')->references('id')->on('variations')->onDelete('cascade');
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('variation_values');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class CreateVariationValueTranslationsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('variation_value_translations', function (Blueprint $table) {
|
||||
$table->increments('id');
|
||||
$table->integer('variation_value_id')->unsigned();
|
||||
$table->string('locale');
|
||||
$table->string('label');
|
||||
|
||||
$table->unique(['variation_value_id', 'locale']);
|
||||
$table->foreign('variation_value_id')->references('id')->on('variation_values')->onDelete('cascade');
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('variation_value_translations');
|
||||
}
|
||||
}
|
||||
174
Modules/Variation/Entities/Variation.php
Normal file
174
Modules/Variation/Entities/Variation.php
Normal file
@@ -0,0 +1,174 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Variation\Entities;
|
||||
|
||||
use Illuminate\Support\Collection;
|
||||
use Modules\Support\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Modules\Support\Eloquent\Translatable;
|
||||
use Modules\Variation\Admin\VariationTable;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
|
||||
class Variation extends Model
|
||||
{
|
||||
use Translatable, SoftDeletes;
|
||||
|
||||
/**
|
||||
* Available variation types.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
const TYPES = ['text', 'color', 'image'];
|
||||
|
||||
/**
|
||||
* The relations to eager load on every query.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $with = ['translations', 'values'];
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fillable = ['uid', 'type', 'is_global', 'position'];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast to native types.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $casts = [
|
||||
'is_global' => 'boolean',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be mutated to dates.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $dates = ['deleted_at'];
|
||||
|
||||
/**
|
||||
* The attributes that are translatable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected array $translatedAttributes = ['name'];
|
||||
|
||||
|
||||
/**
|
||||
* Perform any actions required after the model boots.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected static function booted(): void
|
||||
{
|
||||
static::saved(function ($variation) {
|
||||
if (request()->routeIs('admin.variations.*')) {
|
||||
$variation->saveValuesForGlobal();
|
||||
}
|
||||
|
||||
if (request()->routeIs('admin.products.*')) {
|
||||
$variation->saveValuesForLocal();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Save values for the variation.
|
||||
*
|
||||
* @param array $values
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function saveValues(array $values = []): void
|
||||
{
|
||||
$ids = $this->getDeleteCandidates($values);
|
||||
|
||||
if ($ids->isNotEmpty()) {
|
||||
$this->values()
|
||||
->whereIn('id', $ids)
|
||||
->delete();
|
||||
}
|
||||
|
||||
$counter = 0;
|
||||
|
||||
foreach (array_reset_index($values) as $attributes) {
|
||||
$attributes += ['position' => ++$counter];
|
||||
$attributes += ['value' => $attributes['color'] ?? ''];
|
||||
|
||||
$this->values()->updateOrCreate(
|
||||
[
|
||||
'id' => array_get($attributes, 'id'),
|
||||
],
|
||||
$attributes,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the values for the variation.
|
||||
*
|
||||
* @return HasMany
|
||||
*/
|
||||
public function values(): HasMany
|
||||
{
|
||||
return $this->hasMany(VariationValue::class);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Scope a query to only include global variations.
|
||||
*
|
||||
* @param Builder $query
|
||||
*
|
||||
* @return Builder
|
||||
*/
|
||||
public function scopeGlobals(Builder $query): Builder
|
||||
{
|
||||
return $query->where('is_global', true);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get table data for the resource
|
||||
*
|
||||
* @return VariationTable
|
||||
*/
|
||||
public function table(): VariationTable
|
||||
{
|
||||
return new VariationTable($this->newQuery()->globals());
|
||||
}
|
||||
|
||||
|
||||
protected function saveValuesForGlobal()
|
||||
{
|
||||
$this->saveValues(request('values', []));
|
||||
}
|
||||
|
||||
|
||||
protected function saveValuesForLocal()
|
||||
{
|
||||
$this->saveValues(
|
||||
request('variations.' . $this->uid . '.values', [])
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $values
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
private function getDeleteCandidates($values): Collection
|
||||
{
|
||||
return $this->values()
|
||||
->pluck('id')
|
||||
->diff(array_pluck($values, 'id'));
|
||||
}
|
||||
}
|
||||
15
Modules/Variation/Entities/VariationTranslation.php
Normal file
15
Modules/Variation/Entities/VariationTranslation.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Variation\Entities;
|
||||
|
||||
use Modules\Support\Eloquent\TranslationModel;
|
||||
|
||||
class VariationTranslation extends TranslationModel
|
||||
{
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fillable = ['name'];
|
||||
}
|
||||
99
Modules/Variation/Entities/VariationValue.php
Normal file
99
Modules/Variation/Entities/VariationValue.php
Normal file
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Variation\Entities;
|
||||
|
||||
use Modules\Support\Eloquent\Model;
|
||||
use Modules\Media\Eloquent\HasMedia;
|
||||
use Modules\Support\Eloquent\Translatable;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
|
||||
class VariationValue extends Model
|
||||
{
|
||||
use Translatable, HasMedia;
|
||||
|
||||
/**
|
||||
* The relations to eager load on every query.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $with = ['translations'];
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fillable = ['uid', 'value', 'position'];
|
||||
|
||||
/**
|
||||
* The attributes that are translatable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected array $translatedAttributes = ['label'];
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
protected $appends = ['color', 'image'];
|
||||
|
||||
|
||||
/**
|
||||
* @return mixed|null
|
||||
*/
|
||||
public function getColorAttribute(): mixed
|
||||
{
|
||||
return $this->value ?? null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return mixed|null
|
||||
*/
|
||||
public function getImageAttribute(): mixed
|
||||
{
|
||||
return $this->files()->first() ?? null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function variation(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Variation::class);
|
||||
}
|
||||
|
||||
|
||||
protected function extractMediaFromRequest()
|
||||
{
|
||||
if (request()->routeIs('admin.variations.*')) {
|
||||
return $this->extractMediaForGlobal();
|
||||
}
|
||||
|
||||
if (request()->routeIs('admin.products.*')) {
|
||||
return $this->extractMediaForLocal();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected function extractMediaForGlobal()
|
||||
{
|
||||
if (request('type') === 'image') {
|
||||
return [
|
||||
'media' => [request('values.' . $this->uid . '.image')],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected function extractMediaForLocal()
|
||||
{
|
||||
if ($this->variation->type === 'image') {
|
||||
return [
|
||||
'media' => [request('variations.' . $this->variation->uid . '.values.' . $this->uid . '.image')],
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
15
Modules/Variation/Entities/VariationValueTranslation.php
Normal file
15
Modules/Variation/Entities/VariationValueTranslation.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Variation\Entities;
|
||||
|
||||
use Modules\Support\Eloquent\TranslationModel;
|
||||
|
||||
class VariationValueTranslation extends TranslationModel
|
||||
{
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fillable = ['label'];
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Variation\Http\Controllers\Admin;
|
||||
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Illuminate\Contracts\View\Factory;
|
||||
use Modules\Admin\Traits\HasCrudActions;
|
||||
use Modules\Variation\Entities\Variation;
|
||||
use Illuminate\Contracts\Foundation\Application;
|
||||
use Modules\Variation\Transformers\VariationResource;
|
||||
use Modules\Variation\Http\Requests\SaveVariationRequest;
|
||||
|
||||
class VariationController
|
||||
{
|
||||
use HasCrudActions;
|
||||
|
||||
/**
|
||||
* Model for the resource.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected string $model = Variation::class;
|
||||
|
||||
/**
|
||||
* Label of the resource.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected string $label = 'variation::variations.variation';
|
||||
|
||||
/**
|
||||
* View path of the resource.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected string $viewPath = 'variation::admin.variations';
|
||||
|
||||
/**
|
||||
* Form requests for the resource.
|
||||
*
|
||||
* @var array|string
|
||||
*/
|
||||
protected string|array $validation = SaveVariationRequest::class;
|
||||
|
||||
|
||||
public function show($id): VariationResource
|
||||
{
|
||||
$entity = $this->getEntity($id);
|
||||
|
||||
return new VariationResource($entity);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Show the form for editing the specified resource.
|
||||
*
|
||||
* @param int $id
|
||||
*
|
||||
* @return Application|Factory|View
|
||||
*/
|
||||
public function edit($id): View|Factory|Application
|
||||
{
|
||||
$entity = $this->getEntity($id);
|
||||
$variationResource = new VariationResource($entity);
|
||||
|
||||
return view("{$this->viewPath}.edit",
|
||||
[
|
||||
'variation' => $entity,
|
||||
'variation_resource' => $variationResource->response()->content(),
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
57
Modules/Variation/Http/Requests/SaveVariationRequest.php
Normal file
57
Modules/Variation/Http/Requests/SaveVariationRequest.php
Normal file
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Variation\Http\Requests;
|
||||
|
||||
use Illuminate\Validation\Rule;
|
||||
use Modules\Core\Http\Requests\Request;
|
||||
use Modules\Variation\Entities\Variation;
|
||||
|
||||
class SaveVariationRequest extends Request
|
||||
{
|
||||
/**
|
||||
* Available attributes.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $availableAttributes = 'variation::attributes';
|
||||
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'name' => 'required',
|
||||
'type' => ['required', Rule::in(Variation::TYPES)],
|
||||
'values' => 'array|min:1',
|
||||
'values.*.label' => 'required|distinct',
|
||||
'values.*.color' => 'required_if:type,color|regex:/^#(?:[0-9a-fA-F]{3}){1,2}$/',
|
||||
'values.*.image' => 'required_if:type,image',
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
public function __validationData(): array
|
||||
{
|
||||
return request()
|
||||
->merge([
|
||||
'values' => $this->filter($this->values ?? []),
|
||||
])
|
||||
->all();
|
||||
}
|
||||
|
||||
|
||||
private function filter($values = [])
|
||||
{
|
||||
return array_filter($values, function ($value) {
|
||||
if (!array_has($value, 'label')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return !is_null($value['label']);
|
||||
});
|
||||
}
|
||||
}
|
||||
19
Modules/Variation/Providers/VariationServiceProvider.php
Normal file
19
Modules/Variation/Providers/VariationServiceProvider.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Variation\Providers;
|
||||
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class VariationServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Bootstrap the application services.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
JsonResource::withoutWrapping();
|
||||
}
|
||||
}
|
||||
68
Modules/Variation/Resources/assets/admin/js/create.js
Normal file
68
Modules/Variation/Resources/assets/admin/js/create.js
Normal file
@@ -0,0 +1,68 @@
|
||||
import Vue from "vue";
|
||||
import VariationMixin from "./mixins/VariationMixin";
|
||||
import { toaster } from "@admin/js/Toaster";
|
||||
|
||||
new Vue({
|
||||
el: "#app",
|
||||
|
||||
mixins: [VariationMixin],
|
||||
|
||||
created() {
|
||||
this.setFormDefaultData();
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.focusInitialField();
|
||||
},
|
||||
|
||||
methods: {
|
||||
setFormDefaultData() {
|
||||
this.form = {
|
||||
uid: this.uid(),
|
||||
type: "",
|
||||
values: [
|
||||
{
|
||||
uid: this.uid(),
|
||||
image: {
|
||||
id: null,
|
||||
path: null,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
|
||||
focusInitialField() {
|
||||
this.$nextTick(() => {
|
||||
$("#name").trigger("focus");
|
||||
});
|
||||
},
|
||||
|
||||
submit() {
|
||||
this.formSubmitting = true;
|
||||
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: route("admin.variations.store"),
|
||||
data: this.transformData(this.form),
|
||||
dataType: "json",
|
||||
success: (response) => {
|
||||
toaster(response.message, {
|
||||
type: "success",
|
||||
});
|
||||
|
||||
this.resetForm();
|
||||
this.errors.reset();
|
||||
},
|
||||
})
|
||||
.catch((error) => {
|
||||
this.errors.reset();
|
||||
this.errors.record(error.responseJSON.errors);
|
||||
this.scrollToFirstErrorField(this.$refs.form.elements);
|
||||
})
|
||||
.always(() => {
|
||||
this.formSubmitting = false;
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
55
Modules/Variation/Resources/assets/admin/js/edit.js
Normal file
55
Modules/Variation/Resources/assets/admin/js/edit.js
Normal file
@@ -0,0 +1,55 @@
|
||||
import Vue from "vue";
|
||||
import VariationMixin from "./mixins/VariationMixin";
|
||||
import { toaster } from "@admin/js/Toaster";
|
||||
|
||||
new Vue({
|
||||
el: "#app",
|
||||
|
||||
mixins: [VariationMixin],
|
||||
|
||||
created() {
|
||||
this.form = this.prepareFormData(FleetCart.data["variation"]);
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.initColorPicker();
|
||||
},
|
||||
|
||||
methods: {
|
||||
prepareFormData(formData) {
|
||||
formData.uid = this.uid();
|
||||
|
||||
formData.values.forEach((value) => {
|
||||
value.uid = this.uid();
|
||||
});
|
||||
|
||||
return formData;
|
||||
},
|
||||
|
||||
submit() {
|
||||
this.formSubmitting = true;
|
||||
|
||||
$.ajax({
|
||||
type: "PUT",
|
||||
url: route("admin.variations.update", this.form.id),
|
||||
data: this.transformData(this.form),
|
||||
dataType: "json",
|
||||
success: (response) => {
|
||||
toaster(response.message, {
|
||||
type: "success",
|
||||
});
|
||||
|
||||
this.errors.reset();
|
||||
},
|
||||
})
|
||||
.catch((error) => {
|
||||
this.errors.reset();
|
||||
this.errors.record(error.responseJSON.errors);
|
||||
this.scrollToFirstErrorField(this.$refs.form.elements);
|
||||
})
|
||||
.always(() => {
|
||||
this.formSubmitting = false;
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,242 @@
|
||||
import draggable from "vuedraggable";
|
||||
import Errors from "@admin/js/Errors";
|
||||
import Coloris from "@melloware/coloris";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
draggable,
|
||||
},
|
||||
|
||||
data: {
|
||||
formSubmitting: false,
|
||||
form: {},
|
||||
errors: new Errors(),
|
||||
},
|
||||
|
||||
computed: {
|
||||
isEmptyVariationType() {
|
||||
return this.form.type === "";
|
||||
},
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.hideColorPicker();
|
||||
},
|
||||
|
||||
methods: {
|
||||
uid() {
|
||||
return Math.random().toString(36).slice(3);
|
||||
},
|
||||
|
||||
changeVariationType(value) {
|
||||
const values = this.form.values;
|
||||
|
||||
if (value !== "" && values.length === 1) {
|
||||
this.$nextTick(() => {
|
||||
$(`#values-${values[0].uid}-label`).trigger("focus");
|
||||
});
|
||||
}
|
||||
|
||||
if (value === "text") {
|
||||
values.forEach((value) => {
|
||||
this.errors.clear(`values.${value.uid}.color`);
|
||||
this.errors.clear(`values.${value.uid}.image`);
|
||||
});
|
||||
} else if (value === "color") {
|
||||
values.forEach((value) => {
|
||||
this.errors.clear(`values.${value.uid}.image`);
|
||||
});
|
||||
|
||||
this.$nextTick(() => {
|
||||
this.initColorPicker();
|
||||
});
|
||||
} else if (value === "image") {
|
||||
values.forEach((value, index) => {
|
||||
if (!value.image) {
|
||||
this.$set(values[index], "image", {
|
||||
id: null,
|
||||
path: null,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
values.forEach((value) => {
|
||||
this.errors.clear(`values.${value.uid}.color`);
|
||||
});
|
||||
} else {
|
||||
this.clearValueErrors();
|
||||
}
|
||||
},
|
||||
|
||||
addRow() {
|
||||
const values = this.form.values;
|
||||
const uid = this.uid();
|
||||
|
||||
values.push({
|
||||
uid,
|
||||
image: {
|
||||
id: null,
|
||||
path: null,
|
||||
},
|
||||
});
|
||||
|
||||
this.$nextTick(() => {
|
||||
$(`#values-${uid}-label`).trigger("focus");
|
||||
|
||||
if (this.form.type === "color") {
|
||||
this.initColorPicker();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
addRowOnPressEnter(event, index) {
|
||||
const values = this.form.values;
|
||||
|
||||
if (event.target.value === "") return;
|
||||
|
||||
if (values.length - 1 === index) {
|
||||
this.addRow();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (index < values.length - 1) {
|
||||
$(`#values-${values[index + 1].uid}-label`).trigger("focus");
|
||||
}
|
||||
},
|
||||
|
||||
deleteRow(index, uid) {
|
||||
const values = this.form.values;
|
||||
|
||||
values.splice(index, 1);
|
||||
|
||||
if (values.length === 0) {
|
||||
this.addRow();
|
||||
}
|
||||
|
||||
this.clearValueRowErrors(uid);
|
||||
this.updateColorThumbnails();
|
||||
},
|
||||
|
||||
updateColorThumbnails() {
|
||||
if (this.form.type !== "color") return;
|
||||
|
||||
const elements = document.querySelectorAll(".clr-field");
|
||||
|
||||
this.form.values.forEach((value, index) => {
|
||||
elements[index].style.color = value.color || "";
|
||||
});
|
||||
},
|
||||
|
||||
initColorPicker() {
|
||||
Coloris.init();
|
||||
|
||||
Coloris({
|
||||
el: ".color-picker",
|
||||
alpha: false,
|
||||
rtl: FleetCart.rtl,
|
||||
theme: "large",
|
||||
wrap: true,
|
||||
format: "hex",
|
||||
selectInput: true,
|
||||
swatches: [
|
||||
"#D01C1F",
|
||||
"#3AA845",
|
||||
"#118257",
|
||||
"#0A33AE",
|
||||
"#0D46A0",
|
||||
"#000000",
|
||||
"#5F4C3A",
|
||||
"#726E6E",
|
||||
"#F6D100",
|
||||
"#C0E506",
|
||||
"#FF540A",
|
||||
"#C5A996",
|
||||
"#4B80BE",
|
||||
"#A1C3DA",
|
||||
"#C8BFC2",
|
||||
"#A9A270",
|
||||
],
|
||||
});
|
||||
},
|
||||
|
||||
hideColorPicker() {
|
||||
$(document).on("click", "#clr-swatches button", (e) => {
|
||||
$(e.currentTarget)
|
||||
.parents("#clr-picker")
|
||||
.removeClass("clr-open");
|
||||
});
|
||||
},
|
||||
|
||||
chooseImage(index, uid) {
|
||||
let picker = new MediaPicker({ type: "image" });
|
||||
|
||||
picker.on("select", ({ id, path }) => {
|
||||
this.errors.clear(`values.${uid}.image`);
|
||||
|
||||
this.form.values[index].image = {
|
||||
id: +id,
|
||||
path,
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
resetForm() {
|
||||
this.setFormDefaultData();
|
||||
this.focusInitialField();
|
||||
},
|
||||
|
||||
clearValueErrors() {
|
||||
Object.keys(this.errors.errors).forEach((key) => {
|
||||
if (key.startsWith("values")) {
|
||||
this.errors.clear(key);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
clearValueRowErrors(uid) {
|
||||
Object.keys(this.errors.errors).forEach((key) => {
|
||||
if (key.startsWith(`values.${uid}`)) {
|
||||
this.errors.clear(key);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
scrollToFirstErrorField(elements) {
|
||||
this.$nextTick(() => {
|
||||
[...elements]
|
||||
.find(
|
||||
(el) => el.name === Object.keys(this.errors.errors)[0]
|
||||
)
|
||||
.focus();
|
||||
});
|
||||
},
|
||||
|
||||
transformData(data) {
|
||||
const formData = JSON.parse(JSON.stringify(data));
|
||||
const PATHS = {
|
||||
text: ["id", "uid", "label"],
|
||||
color: ["id", "uid", "label", "color"],
|
||||
image: ["id", "uid", "label", "image"],
|
||||
};
|
||||
|
||||
if (formData.type === "") {
|
||||
formData.values = [];
|
||||
|
||||
return formData;
|
||||
}
|
||||
|
||||
formData.values = formData.values.reduce((accumulator, value) => {
|
||||
value = _.pick(value, PATHS[formData.type]);
|
||||
|
||||
if (formData.type === "image") {
|
||||
value.image = value.image.id;
|
||||
}
|
||||
|
||||
return { ...accumulator, [value.uid]: value };
|
||||
}, {});
|
||||
|
||||
return formData;
|
||||
},
|
||||
},
|
||||
};
|
||||
127
Modules/Variation/Resources/assets/admin/sass/main.scss
Normal file
127
Modules/Variation/Resources/assets/admin/sass/main.scss
Normal file
@@ -0,0 +1,127 @@
|
||||
/*rtl:begin:ignore*/
|
||||
@import "@melloware/coloris/dist/coloris.css";
|
||||
/*rtl:end:ignore*/
|
||||
|
||||
.form {
|
||||
.has-variation-type {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.clr-field {
|
||||
width: 100%;
|
||||
|
||||
button {
|
||||
top: 4px;
|
||||
right: 4px;
|
||||
bottom: 4px;
|
||||
height: auto;
|
||||
width: 28px;
|
||||
border-radius: 3px;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
input {
|
||||
padding-right: 40px;
|
||||
}
|
||||
}
|
||||
|
||||
.variations-group {
|
||||
.variation-values {
|
||||
margin-top: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
.variation-values {
|
||||
.table-responsive {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.options {
|
||||
&.type-text {
|
||||
.option-row {
|
||||
td {
|
||||
&:nth-child(2) {
|
||||
width: 100%;
|
||||
min-width: 180px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.options {
|
||||
&.type-color,
|
||||
&.type-image {
|
||||
.option-row {
|
||||
td {
|
||||
&:nth-child(2) {
|
||||
width: 75%;
|
||||
min-width: 160px;
|
||||
}
|
||||
|
||||
&:nth-child(3) {
|
||||
width: 25%;
|
||||
min-width: 135px;
|
||||
|
||||
.image-holder {
|
||||
height: 36px;
|
||||
width: 36px;
|
||||
margin: 0;
|
||||
|
||||
> i {
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 1200px) {
|
||||
.variation-values {
|
||||
.options {
|
||||
&.type-color {
|
||||
.option-row {
|
||||
td:nth-child(3) {
|
||||
min-width: 200px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 767px) {
|
||||
.box-body {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.form {
|
||||
.has-variation-type {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
> .row {
|
||||
> div {
|
||||
&:first-child {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
> div {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.variations-group {
|
||||
.variation-values {
|
||||
margin-top: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
10
Modules/Variation/Resources/lang/en/attributes.php
Normal file
10
Modules/Variation/Resources/lang/en/attributes.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'name' => 'Name',
|
||||
'type' => 'Type',
|
||||
'values' => 'Values',
|
||||
'values.*.label' => 'Label',
|
||||
'values.*.color' => 'Color',
|
||||
'values.*.image' => 'Image',
|
||||
];
|
||||
10
Modules/Variation/Resources/lang/en/permissions.php
Normal file
10
Modules/Variation/Resources/lang/en/permissions.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'variations' => [
|
||||
'index' => 'Index Variations',
|
||||
'create' => 'Create Variations',
|
||||
'edit' => 'Edit Variations',
|
||||
'destroy' => 'Delete Variations',
|
||||
],
|
||||
];
|
||||
5
Modules/Variation/Resources/lang/en/sidebar.php
Normal file
5
Modules/Variation/Resources/lang/en/sidebar.php
Normal file
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'variations' => 'Variations',
|
||||
];
|
||||
32
Modules/Variation/Resources/lang/en/variations.php
Normal file
32
Modules/Variation/Resources/lang/en/variations.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'variation' => 'Variation',
|
||||
'variations' => 'Variations',
|
||||
|
||||
'table' => [
|
||||
'name' => 'Name',
|
||||
'type' => 'Type',
|
||||
],
|
||||
|
||||
'group' => [
|
||||
'general' => 'General',
|
||||
'values' => 'Values',
|
||||
],
|
||||
|
||||
'form' => [
|
||||
'name' => 'Name',
|
||||
'type' => 'Type',
|
||||
'values' => 'Values',
|
||||
'label' => 'Label',
|
||||
'color' => 'Color',
|
||||
'image' => 'Image',
|
||||
'variation_types' => [
|
||||
'please_select' => 'Please Select',
|
||||
'text' => 'Text',
|
||||
'color' => 'Color',
|
||||
'image' => 'Image',
|
||||
],
|
||||
'add_row' => 'Add Row',
|
||||
],
|
||||
];
|
||||
@@ -0,0 +1,38 @@
|
||||
@extends('admin::layout')
|
||||
|
||||
@component('admin::components.page.header')
|
||||
@slot('title', trans('admin::resource.create', ['resource' => trans('variation::variations.variation')]))
|
||||
|
||||
<li><a href="{{ route('admin.variations.index') }}">{{ trans('variation::variations.variations') }}</a></li>
|
||||
<li class="active">{{ trans('admin::resource.create', ['resource' => trans('variation::variations.variation')]) }}</li>
|
||||
@endcomponent
|
||||
|
||||
@section('content')
|
||||
<div class="box">
|
||||
<div class="box-body">
|
||||
<div id="app">
|
||||
<form
|
||||
class="form"
|
||||
@input="errors.clear($event.target.name)"
|
||||
@submit.prevent
|
||||
ref="form"
|
||||
>
|
||||
@include('variation::admin.variations.partials.general')
|
||||
@include('variation::admin.variations.partials.values')
|
||||
@include('variation::admin.variations.partials.submit')
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@include('variation::admin.variations.partials.scripts')
|
||||
|
||||
@push('globals')
|
||||
@vite([
|
||||
'Modules/Variation/Resources/assets/admin/sass/main.scss',
|
||||
'Modules/Variation/Resources/assets/admin/js/create.js',
|
||||
'Modules/Media/Resources/assets/admin/sass/main.scss',
|
||||
'Modules/Media/Resources/assets/admin/js/main.js',
|
||||
])
|
||||
@endpush
|
||||
@@ -0,0 +1,43 @@
|
||||
@extends('admin::layout')
|
||||
|
||||
@component('admin::components.page.header')
|
||||
@slot('title', trans('admin::resource.edit', ['resource' => trans('variation::variations.variation')]))
|
||||
@slot('subtitle', $variation->name)
|
||||
|
||||
<li><a href="{{ route('admin.variations.index') }}">{{ trans('variation::variations.variations') }}</a></li>
|
||||
<li class="active">{{ trans('admin::resource.edit', ['resource' => trans('variation::variations.variation')]) }}</li>
|
||||
@endcomponent
|
||||
|
||||
@section('content')
|
||||
<div class="box">
|
||||
<div class="box-body">
|
||||
<div id="app">
|
||||
<form
|
||||
class="form"
|
||||
@input="errors.clear($event.target.name)"
|
||||
@submit.prevent
|
||||
ref="form"
|
||||
>
|
||||
@include('variation::admin.variations.partials.general')
|
||||
@include('variation::admin.variations.partials.values')
|
||||
@include('variation::admin.variations.partials.submit')
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@include('variation::admin.variations.partials.scripts')
|
||||
|
||||
@push('globals')
|
||||
<script type="module">
|
||||
FleetCart.data['variation'] = {!! $variation_resource !!};
|
||||
</script>
|
||||
|
||||
@vite([
|
||||
'Modules/Variation/Resources/assets/admin/sass/main.scss',
|
||||
'Modules/Variation/Resources/assets/admin/js/edit.js',
|
||||
'Modules/Media/Resources/assets/admin/sass/main.scss',
|
||||
'Modules/Media/Resources/assets/admin/js/main.js',
|
||||
])
|
||||
@endpush
|
||||
@@ -0,0 +1,38 @@
|
||||
@extends('admin::layout')
|
||||
|
||||
@component('admin::components.page.header')
|
||||
@slot('title', trans('variation::variations.variations'))
|
||||
|
||||
<li class="active">{{ trans('variation::variations.variations') }}</li>
|
||||
@endcomponent
|
||||
|
||||
@component('admin::components.page.index_table')
|
||||
@slot('buttons', ['create'])
|
||||
@slot('resource', 'variations')
|
||||
@slot('name', trans('variation::variations.variation'))
|
||||
|
||||
@slot('thead')
|
||||
<tr>
|
||||
@include('admin::partials.table.select_all')
|
||||
|
||||
<th>{{ trans('admin::admin.table.id') }}</th>
|
||||
<th>{{ trans('variation::variations.table.name') }}</th>
|
||||
<th>{{ trans('variation::variations.table.type') }}</th>
|
||||
<th data-sort>{{ trans('admin::admin.table.updated') }}</th>
|
||||
</tr>
|
||||
@endslot
|
||||
@endcomponent
|
||||
|
||||
@push('scripts')
|
||||
<script type="module">
|
||||
new DataTable('#variations-table .table', {
|
||||
columns: [
|
||||
{ data: 'checkbox', orderable: false, searchable: false, width: '3%' },
|
||||
{ data: 'id', width: '5%' },
|
||||
{ data: 'name', name: 'translations.name', orderable: false, defaultContent: '' },
|
||||
{ data: 'type', name: 'type' },
|
||||
{ data: 'updated', name: 'updated_at' },
|
||||
],
|
||||
});
|
||||
</script>
|
||||
@endpush
|
||||
@@ -0,0 +1,57 @@
|
||||
<div class="row" :class="{ 'has-variation-type': !isEmptyVariationType }">
|
||||
<div class="col-lg-2 col-sm-2">
|
||||
<h5>{{ trans('variation::variations.group.general') }}</h5>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-7 col-sm-10">
|
||||
<div class="row">
|
||||
<div class="col-sm-6">
|
||||
<div class="form-group">
|
||||
<label for="name">
|
||||
{{ trans('variation::attributes.name') }}
|
||||
<span class="text-red">*</span>
|
||||
</label>
|
||||
|
||||
<input type="text" name="name" id="name" class="form-control" v-model="form.name">
|
||||
|
||||
<span class="help-block text-red" v-if="errors.has('name')" v-text="errors.get('name')"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-6">
|
||||
<div class="form-group">
|
||||
<label for="type">
|
||||
{{ trans('variation::attributes.type') }}
|
||||
<span class="text-red">*</span>
|
||||
</label>
|
||||
|
||||
<select
|
||||
name="type"
|
||||
id="type"
|
||||
class="form-control custom-select-black"
|
||||
@change="changeVariationType($event.target.value)"
|
||||
v-model="form.type"
|
||||
>
|
||||
<option value="">
|
||||
{{ trans('variation::variations.form.variation_types.please_select') }}
|
||||
</option>
|
||||
|
||||
<option value="text">
|
||||
{{ trans('variation::variations.form.variation_types.text') }}
|
||||
</option>
|
||||
|
||||
<option value="color">
|
||||
{{ trans('variation::variations.form.variation_types.color') }}
|
||||
</option>
|
||||
|
||||
<option value="image">
|
||||
{{ trans('variation::variations.form.variation_types.image') }}
|
||||
</option>
|
||||
</select>
|
||||
|
||||
<span class="help-block text-red" v-if="errors.has('type')" v-text="errors.get('type')"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,15 @@
|
||||
@push('shortcuts')
|
||||
<dl class="dl-horizontal">
|
||||
<dt><code>b</code></dt>
|
||||
<dd>{{ trans('admin::admin.shortcuts.back_to_index', ['name' => trans('variation::variations.variation')]) }}</dd>
|
||||
</dl>
|
||||
@endpush
|
||||
|
||||
@push('scripts')
|
||||
<script type="module">
|
||||
keypressAction([{
|
||||
key: 'b',
|
||||
route: "{{ route('admin.variations.index') }}"
|
||||
}, ]);
|
||||
</script>
|
||||
@endpush
|
||||
@@ -0,0 +1,15 @@
|
||||
<div class="row">
|
||||
<div class="col-lg-7 col-lg-offset-2 col-md-12 text-right">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-primary"
|
||||
:class="{
|
||||
'btn-loading': formSubmitting
|
||||
}"
|
||||
:disabled="formSubmitting"
|
||||
@click="submit"
|
||||
>
|
||||
{{ trans('admin::admin.buttons.save') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,123 @@
|
||||
<div v-cloak class="row" v-if="!isEmptyVariationType">
|
||||
<div class="col-lg-2 col-sm-2">
|
||||
<h5>{{ trans('variation::variations.group.values') }}</h5>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-7 col-sm-10">
|
||||
<div class="variation-values clearfix">
|
||||
<div class="table-responsive">
|
||||
<table
|
||||
class="options table table-bordered table-striped"
|
||||
:class="form.type !== '' ? `type-${form.type}` : ''"
|
||||
>
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>
|
||||
{{ trans('variation::variations.form.label') }}
|
||||
<span class="text-red">*</span>
|
||||
</th>
|
||||
<th v-if="form.type === 'color'">
|
||||
{{ trans('variation::variations.form.color') }}
|
||||
<span class="text-red">*</span>
|
||||
</th>
|
||||
<th v-else-if="form.type === 'image'">
|
||||
{{ trans('variation::variations.form.image') }}
|
||||
<span class="text-red">*</span>
|
||||
</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody
|
||||
is="draggable"
|
||||
tag="tbody"
|
||||
handle=".drag-handle"
|
||||
animation="150"
|
||||
:list="form.values"
|
||||
@end="updateColorThumbnails"
|
||||
>
|
||||
<tr v-for="(value, index) in form.values" class="option-row" :key="index">
|
||||
<td class="text-center">
|
||||
<span class="drag-handle">
|
||||
<i class="fa"></i>
|
||||
<i class="fa"></i>
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<input
|
||||
type="text"
|
||||
:name="`values.${value.uid}.label`"
|
||||
:id="`values-${value.uid}-label`"
|
||||
class="form-control"
|
||||
@keyup.enter="addRowOnPressEnter($event, index)"
|
||||
v-model="value.label"
|
||||
>
|
||||
|
||||
<span
|
||||
class="help-block text-red"
|
||||
v-if="errors.has(`values.${value.uid}.label`)"
|
||||
v-text="errors.get(`values.${value.uid}.label`)"
|
||||
>
|
||||
</span>
|
||||
</td>
|
||||
<td v-if="form.type === 'color'">
|
||||
<div>
|
||||
<input
|
||||
type="text"
|
||||
:name="`values.${value.uid}.color`"
|
||||
:id="`values-${value.uid}-color`"
|
||||
class="form-control color-picker"
|
||||
v-model="value.color"
|
||||
>
|
||||
</div>
|
||||
|
||||
<span
|
||||
class="help-block text-red"
|
||||
v-if="errors.has(`values.${value.uid}.color`)"
|
||||
v-text="errors.get(`values.${value.uid}.color`)"
|
||||
>
|
||||
</span>
|
||||
</td>
|
||||
<td v-else-if="form.type === 'image'">
|
||||
<div class="d-flex">
|
||||
<div
|
||||
class="image-holder"
|
||||
@click="chooseImage(index, value.uid)"
|
||||
>
|
||||
<template v-if="value.image.id">
|
||||
<img :src="value.image.path" alt="variation image">
|
||||
</template>
|
||||
|
||||
<img v-else src="{{ asset('build/assets/placeholder_image.png') }}" class="placeholder-image" alt="Placeholder image">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<span
|
||||
class="help-block text-red"
|
||||
v-if="errors.has(`values.${value.uid}.image`)"
|
||||
v-text="errors.get(`values.${value.uid}.image`)"
|
||||
>
|
||||
</span>
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<button
|
||||
type="button"
|
||||
tabindex="-1"
|
||||
class="btn btn-default delete-row"
|
||||
@click="deleteRow(index, value.uid)"
|
||||
>
|
||||
<i class="fa fa-trash"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<button type="button" class="btn btn-default" @click="addRow">
|
||||
{{ trans('variation::variations.form.add_row') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
51
Modules/Variation/Routes/admin.php
Normal file
51
Modules/Variation/Routes/admin.php
Normal file
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
Route::get('variations', [
|
||||
'as' => 'admin.variations.index',
|
||||
'uses' => 'VariationController@index',
|
||||
'middleware' => 'can:admin.variations.index',
|
||||
]);
|
||||
|
||||
Route::get('variations/create', [
|
||||
'as' => 'admin.variations.create',
|
||||
'uses' => 'VariationController@create',
|
||||
'middleware' => 'can:admin.variations.create',
|
||||
]);
|
||||
|
||||
Route::post('variations', [
|
||||
'as' => 'admin.variations.store',
|
||||
'uses' => 'VariationController@store',
|
||||
'middleware' => 'can:admin.variations.create',
|
||||
]);
|
||||
|
||||
Route::get('variations/{id}', [
|
||||
'as' => 'admin.variations.show',
|
||||
'uses' => 'VariationController@show',
|
||||
'middleware' => 'can:admin.variations.index',
|
||||
]);
|
||||
|
||||
Route::get('variations/{id}/edit', [
|
||||
'as' => 'admin.variations.edit',
|
||||
'uses' => 'VariationController@edit',
|
||||
'middleware' => 'can:admin.variations.edit',
|
||||
]);
|
||||
|
||||
Route::put('variations/{id}', [
|
||||
'as' => 'admin.variations.update',
|
||||
'uses' => 'VariationController@update',
|
||||
'middleware' => 'can:admin.variations.edit',
|
||||
]);
|
||||
|
||||
Route::delete('variations/{ids}', [
|
||||
'as' => 'admin.variations.destroy',
|
||||
'uses' => 'VariationController@destroy',
|
||||
'middleware' => 'can:admin.variations.destroy',
|
||||
]);
|
||||
|
||||
Route::get('variations/index/table', [
|
||||
'as' => 'admin.variations.table',
|
||||
'uses' => 'VariationController@table',
|
||||
'middleware' => 'can:admin.variations.index',
|
||||
]);
|
||||
26
Modules/Variation/Sidebar/SidebarExtender.php
Normal file
26
Modules/Variation/Sidebar/SidebarExtender.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Variation\Sidebar;
|
||||
|
||||
use Maatwebsite\Sidebar\Item;
|
||||
use Maatwebsite\Sidebar\Menu;
|
||||
use Maatwebsite\Sidebar\Group;
|
||||
use Modules\Admin\Sidebar\BaseSidebarExtender;
|
||||
|
||||
class SidebarExtender extends BaseSidebarExtender
|
||||
{
|
||||
public function extend(Menu $menu)
|
||||
{
|
||||
$menu->group(trans('admin::sidebar.content'), function (Group $group) {
|
||||
$group->item(trans('product::sidebar.products'), function (Item $item) {
|
||||
$item->item(trans('variation::sidebar.variations'), function (Item $item) {
|
||||
$item->weight(25);
|
||||
$item->route('admin.variations.index');
|
||||
$item->authorize(
|
||||
$this->auth->hasAccess('admin.variations.index')
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
28
Modules/Variation/Transformers/VariationResource.php
Normal file
28
Modules/Variation/Transformers/VariationResource.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Variation\Transformers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class VariationResource extends JsonResource
|
||||
{
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @param Request $request
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray($request): array
|
||||
{
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'uid' => $this->uid,
|
||||
'name' => $this->name,
|
||||
'type' => $this->type,
|
||||
'is_global' => $this->is_global,
|
||||
'values' => VariationValueResource::collection($this->values->sortBy('position')),
|
||||
];
|
||||
}
|
||||
}
|
||||
36
Modules/Variation/Transformers/VariationValueResource.php
Normal file
36
Modules/Variation/Transformers/VariationValueResource.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Variation\Transformers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class VariationValueResource extends JsonResource
|
||||
{
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @param Request $request
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray($request): array
|
||||
{
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'uid' => $this->uid,
|
||||
'label' => $this->label,
|
||||
'image' => $this->when(
|
||||
condition: $this->variation->type === 'image',
|
||||
value: fn () => [
|
||||
'id' => $this->image?->id,
|
||||
'path' => $this->image?->path,
|
||||
]
|
||||
),
|
||||
'color' => $this->when(
|
||||
condition: $this->variation->type === 'color',
|
||||
value: $this->color
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
||||
27
Modules/Variation/composer.json
Normal file
27
Modules/Variation/composer.json
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"name": "fleetcart/variation",
|
||||
"description": "The FleetCart Variation Module.",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Envay Soft",
|
||||
"email": "envaysoft@gmail.com"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": "^8.0.2"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Modules\\Variation\\": ""
|
||||
}
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.0.x-dev"
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"sort-packages": true
|
||||
},
|
||||
"minimum-stability": "dev"
|
||||
}
|
||||
9
Modules/Variation/module.json
Normal file
9
Modules/Variation/module.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"name": "Variation",
|
||||
"alias": "variation",
|
||||
"description": "The FleetCart Variation Module.",
|
||||
"priority": 90,
|
||||
"providers": [
|
||||
"Modules\\Variation\\Providers\\VariationServiceProvider"
|
||||
]
|
||||
}
|
||||
8
Modules/Variation/package.json
Normal file
8
Modules/Variation/package.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": "variation-module",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@melloware/coloris": "^0.21.1",
|
||||
"vuedraggable": "^2.24.3"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user