¨4.0.1¨

This commit is contained in:
¨NW¨
2023-12-03 14:07:47 +00:00
parent c08b36d1b6
commit f35052522d
1112 changed files with 43019 additions and 24987 deletions

View File

@@ -1,3 +1,4 @@
#Application
APP_ENV=local APP_ENV=local
APP_DEBUG=true APP_DEBUG=true
APP_CACHE=false APP_CACHE=false
@@ -5,10 +6,13 @@ APP_INSTALLED=false
APP_KEY= APP_KEY=
APP_URL=http://localhost APP_URL=http://localhost
#Ignition
IGNITION_EDITOR=vscode IGNITION_EDITOR=vscode
#Logging
LOG_CHANNEL=stack LOG_CHANNEL=stack
#Database
DB_CONNECTION=mysql DB_CONNECTION=mysql
DB_HOST=127.0.0.1 DB_HOST=127.0.0.1
DB_PORT=3306 DB_PORT=3306
@@ -16,15 +20,24 @@ DB_DATABASE=homestead
DB_USERNAME=homestead DB_USERNAME=homestead
DB_PASSWORD=secret DB_PASSWORD=secret
#Queue
QUEUE_DRIVER=sync QUEUE_DRIVER=sync
#Cache
CACHE_DRIVER=file CACHE_DRIVER=file
#Session
SESSION_DRIVER=file SESSION_DRIVER=file
#File System
FILESYSTEM_DRIVER=public_storage FILESYSTEM_DRIVER=public_storage
#Redis
REDIS_HOST=127.0.0.1 REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null REDIS_PASSWORD=null
REDIS_PORT=6379 REDIS_PORT=6379
#Mail
MAIL_DRIVER=smtp MAIL_DRIVER=smtp
MAIL_HOST=smtp.mailtrap.io MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=2525 MAIL_PORT=2525
@@ -34,15 +47,23 @@ MAIL_FROM_ADDRESS=null
MAIL_FROM_NAME=null MAIL_FROM_NAME=null
MAIL_ENCRYPTION=null MAIL_ENCRYPTION=null
#Mailchimp
MAILCHIMP_APIKEY=
#AWS
AWS_ACCESS_KEY_ID= AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY= AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=us-east-1 AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET= AWS_BUCKET=
#Scout
SCOUT_QUEUE=false SCOUT_QUEUE=false
MAILCHIMP_APIKEY= #Laravel DataTables (yajra/laravel-datatables)
DATATABLES_ERROR=null DATATABLES_ERROR=null
#Debugbar for Laravel (barryvdh/laravel-debugbar)
DEBUGBAR_ENABLED=false DEBUGBAR_ENABLED=false
#Laravel Query Detector (beyondcode/laravel-query-detector)
QUERY_DETECTOR_ENABLED=false QUERY_DETECTOR_ENABLED=false

48
.gitignore vendored
View File

@@ -1,21 +1,47 @@
#Meta
.meta/
.idea/ .idea/
.phpintel/ .phpintel/
*.sublime-project *.sublime-project
.DS_Store .DS_Store
.vscode
#Env
.env
#Dependencies
node_modules/ node_modules/
/vendor/ vendor/
/public/storage/
/public/modules/ #Assets
/public/themes/ public/images/
/storage/logs/* public/modules/
public/themes/
public/build/
public/pwa/
Modules/*/Assets/
Themes/*/assets/
#Fonts
Themes/Storefront/resources/assets/public/fonts/
#Storage
public/storage/
#Logs
public/phplog public/phplog
/Modules/*/Assets/ storage/logs/*
/Themes/*/assets/ Modules/*/yarn-error.log
/tags
/.meta
mix-manifest.json
npm-debug.log npm-debug.log
yarn-error.log yarn-error.log
*.log
#Others
tools/
*.map *.map
.env tags/
public/hot
mix-manifest.json
public/sitemaps/*
public/sitemap.xml
.php-cs-fixer.cache

View File

@@ -18,16 +18,18 @@ class AccountAddressController extends Controller
]); ]);
} }
public function store(SaveAddressRequest $request) public function store(SaveAddressRequest $request)
{ {
$address = auth()->user()->addresses()->create($request->all()); $address = auth()->user()->addresses()->create($request->all());
return response()->json([ return response()->json([
'address' => $address, 'address' => $address,
'message' => trans('account::messages.address_saved'), 'message' => trans('account::messages.address_created'),
]); ]);
} }
public function update(SaveAddressRequest $request, $id) public function update(SaveAddressRequest $request, $id)
{ {
$address = Address::find($id); $address = Address::find($id);
@@ -35,10 +37,11 @@ class AccountAddressController extends Controller
return response()->json([ return response()->json([
'address' => $address, 'address' => $address,
'message' => trans('account::messages.address_saved'), 'message' => trans('account::messages.address_updated'),
]); ]);
} }
public function destroy($id) public function destroy($id)
{ {
auth()->user()->addresses()->find($id)->delete(); auth()->user()->addresses()->find($id)->delete();

View File

@@ -2,12 +2,14 @@
namespace Modules\Account\Http\Controllers; namespace Modules\Account\Http\Controllers;
use Illuminate\Http\Response;
class AccountDashboardController class AccountDashboardController
{ {
/** /**
* Display a listing of the resource. * Display a listing of the resource.
* *
* @return \Illuminate\Http\Response * @return Response
*/ */
public function index() public function index()
{ {

View File

@@ -3,6 +3,7 @@
namespace Modules\Account\Http\Controllers; namespace Modules\Account\Http\Controllers;
use Illuminate\Routing\Controller; use Illuminate\Routing\Controller;
use Illuminate\Contracts\Support\Renderable;
use Modules\Address\Entities\DefaultAddress; use Modules\Address\Entities\DefaultAddress;
class AccountDefaultAddressController extends Controller class AccountDefaultAddressController extends Controller
@@ -10,7 +11,7 @@ class AccountDefaultAddressController extends Controller
/** /**
* Update the specified resource in storage. * Update the specified resource in storage.
* *
* @return \Illuminate\Contracts\Support\Renderable * @return Renderable
*/ */
public function update() public function update()
{ {

View File

@@ -2,6 +2,7 @@
namespace Modules\Account\Http\Controllers; namespace Modules\Account\Http\Controllers;
use Illuminate\Http\Response;
use Modules\Order\Entities\Order; use Modules\Order\Entities\Order;
class AccountDownloadsController class AccountDownloadsController
@@ -9,7 +10,7 @@ class AccountDownloadsController
/** /**
* Display a listing of the resource. * Display a listing of the resource.
* *
* @return \Illuminate\Http\Response * @return Response
*/ */
public function index() public function index()
{ {
@@ -18,23 +19,26 @@ class AccountDownloadsController
]); ]);
} }
/** /**
* Display the specified resource. * Display the specified resource.
* *
* @param int $id * @param int $id
* @return \Illuminate\Http\Response *
* @return Response
*/ */
public function show($id) public function show($id)
{ {
$file = $this->getDownloads()->firstWhere('id', decrypt($id)); $file = $this->getDownloads()->firstWhere('id', decrypt($id));
if (is_null($file) || ! file_exists($file->realPath())) { if (is_null($file) || !file_exists($file->realPath())) {
return back()->with('error', trans('storefront::account.downloads.no_file_found')); return back()->with('error', trans('storefront::account.downloads.no_file_found'));
} }
return response()->download($file->realPath(), $file->filename); return response()->download($file->realPath(), $file->filename);
} }
private function getDownloads() private function getDownloads()
{ {
return auth()->user() return auth()->user()

View File

@@ -2,12 +2,14 @@
namespace Modules\Account\Http\Controllers; namespace Modules\Account\Http\Controllers;
use Illuminate\Http\Response;
class AccountOrdersController class AccountOrdersController
{ {
/** /**
* Display a listing of the resource. * Display a listing of the resource.
* *
* @return \Illuminate\Http\Response * @return Response
*/ */
public function index() public function index()
{ {
@@ -19,11 +21,13 @@ class AccountOrdersController
return view('public.account.orders.index', compact('orders')); return view('public.account.orders.index', compact('orders'));
} }
/** /**
* Display the specified resource. * Display the specified resource.
* *
* @param int $id * @param int $id
* @return \Illuminate\Http\Response *
* @return Response
*/ */
public function show($id) public function show($id)
{ {

View File

@@ -2,6 +2,7 @@
namespace Modules\Account\Http\Controllers; namespace Modules\Account\Http\Controllers;
use Illuminate\Http\Response;
use Modules\User\Http\Requests\UpdateProfileRequest; use Modules\User\Http\Requests\UpdateProfileRequest;
class AccountProfileController class AccountProfileController
@@ -9,7 +10,7 @@ class AccountProfileController
/** /**
* Show the form for editing the specified resource. * Show the form for editing the specified resource.
* *
* @return \Illuminate\Http\Response * @return Response
*/ */
public function edit() public function edit()
{ {
@@ -18,15 +19,17 @@ class AccountProfileController
]); ]);
} }
/** /**
* Update the specified resource in storage. * Update the specified resource in storage.
* *
* @param \Modules\User\Http\Requests\UpdateProfileRequest $request * @param UpdateProfileRequest $request
* @return \Illuminate\Http\Response *
* @return Response
*/ */
public function update(UpdateProfileRequest $request) public function update(UpdateProfileRequest $request)
{ {
$request->bcryptPassword($request); $request->bcryptPassword();
auth()->user()->update($request->all()); auth()->user()->update($request->all());

View File

@@ -2,22 +2,19 @@
namespace Modules\Account\Http\Controllers; namespace Modules\Account\Http\Controllers;
use Illuminate\Contracts\View\View;
use Illuminate\Contracts\View\Factory;
use Illuminate\Contracts\Foundation\Application;
class AccountReviewController class AccountReviewController
{ {
/** /**
* Display a listing of the resource. * Display a listing of the resource.
* *
* @return \Illuminate\Http\Response * @return Application|Factory|View
*/ */
public function index() public function index(): View|Factory|Application
{ {
$reviews = auth()->user() return view('public.account.reviews.index');
->reviews()
->withoutGlobalScope('approved')
->with('product.files')
->whereHas('product')
->paginate(20);
return view('public.account.reviews.index', compact('reviews'));
} }
} }

View File

@@ -2,12 +2,14 @@
namespace Modules\Account\Http\Controllers; namespace Modules\Account\Http\Controllers;
use Illuminate\Http\Response;
class AccountWishlistController class AccountWishlistController
{ {
/** /**
* Display a listing of the resource. * Display a listing of the resource.
* *
* @return \Illuminate\Http\Response * @return Response
*/ */
public function index() public function index()
{ {

View File

@@ -13,6 +13,7 @@ class SaveAddressRequest extends Request
*/ */
protected $availableAttributes = 'account::attributes.addresses'; protected $availableAttributes = 'account::attributes.addresses';
/** /**
* Get the validation rules that apply to the request. * Get the validation rules that apply to the request.
* *

View File

@@ -1,8 +1,9 @@
<?php <?php
return [ return [
'profile_updated' => 'Your profile has been updated.', 'profile_updated' => 'Your profile has been updated',
'default_address_updated' => 'The default address has been updated.', 'default_address_updated' => 'The default address has been updated',
'address_saved' => 'The address has been saved.', 'address_created' => 'The address has been created',
'address_deleted' => 'The address has been deleted.', 'address_updated' => 'The address has been updated',
'address_deleted' => 'The address has been deleted',
]; ];

View File

@@ -30,6 +30,7 @@ class CreateAddressesTable extends Migration
}); });
} }
/** /**
* Reverse the migrations. * Reverse the migrations.
* *

View File

@@ -23,6 +23,7 @@ class CreateDefaultAddressesTable extends Migration
}); });
} }
/** /**
* Reverse the migrations. * Reverse the migrations.
* *

View File

@@ -13,21 +13,25 @@ class Address extends Model
protected $appends = ['full_name', 'state_name', 'country_name']; protected $appends = ['full_name', 'state_name', 'country_name'];
public function customer() public function customer()
{ {
return $this->belongsTo(User::class); return $this->belongsTo(User::class);
} }
public function getFullNameAttribute() public function getFullNameAttribute()
{ {
return $this->first_name . ' ' . $this->last_name; return $this->first_name . ' ' . $this->last_name;
} }
public function getStateNameAttribute() public function getStateNameAttribute()
{ {
return State::name($this->country, $this->state); return State::name($this->country, $this->state);
} }
public function getCountryNameAttribute() public function getCountryNameAttribute()
{ {
return Country::name($this->country); return Country::name($this->country);

View File

@@ -6,42 +6,47 @@ use Illuminate\Database\Eloquent\Model;
class DefaultAddress extends Model class DefaultAddress extends Model
{ {
public $timestamps = false;
protected $with = ['address']; protected $with = ['address'];
protected $fillable = ['customer_id', 'address_id']; protected $fillable = ['customer_id', 'address_id'];
public $timestamps = false;
public function address() public function address()
{ {
return $this->belongsTo(Address::class); return $this->belongsTo(Address::class);
} }
public function getAddress1Attribute() public function getAddress1Attribute()
{ {
return $this->address->address_1; return $this->address->address_1;
} }
public function getAddress2Attribute() public function getAddress2Attribute()
{ {
return $this->address->address_1; return $this->address->address_1;
} }
public function getCityAttribute() public function getCityAttribute()
{ {
return $this->address->city; return $this->address->city;
} }
public function getStateAttribute() public function getStateAttribute()
{ {
return $this->address->state; return $this->address->state;
} }
public function getZipAttribute() public function getZipAttribute()
{ {
return $this->address->zip; return $this->address->zip;
} }
public function getCountryAttribute() public function getCountryAttribute()
{ {
return $this->address->country; return $this->address->country;

View File

@@ -2,18 +2,20 @@
namespace Modules\Admin\Http\Controllers\Admin; namespace Modules\Admin\Http\Controllers\Admin;
use Illuminate\Http\Response;
use Modules\User\Entities\User; use Modules\User\Entities\User;
use Modules\Order\Entities\Order; use Modules\Order\Entities\Order;
use Modules\Review\Entities\Review; use Modules\Review\Entities\Review;
use Modules\Product\Entities\Product; use Modules\Product\Entities\Product;
use Modules\Product\Entities\SearchTerm; use Modules\Product\Entities\SearchTerm;
use Illuminate\Database\Eloquent\Collection;
class DashboardController class DashboardController
{ {
/** /**
* Display the dashboard with its widgets. * Display the dashboard with its widgets.
* *
* @return \Illuminate\Http\Response * @return Response
*/ */
public function index() public function index()
{ {
@@ -28,15 +30,17 @@ class DashboardController
]); ]);
} }
private function getLatestSearchTerms() private function getLatestSearchTerms()
{ {
return SearchTerm::latest('updated_at')->take(5)->get(); return SearchTerm::latest('updated_at')->take(5)->get();
} }
/** /**
* Get latest five orders. * Get latest five orders.
* *
* @return \Illuminate\Database\Eloquent\Collection * @return Collection
*/ */
private function getLatestOrders() private function getLatestOrders()
{ {
@@ -50,10 +54,11 @@ class DashboardController
])->latest()->take(5)->get(); ])->latest()->take(5)->get();
} }
/** /**
* Get latest five reviews. * Get latest five reviews.
* *
* @return \Illuminate\Database\Eloquent\Collection * @return Collection
*/ */
private function getLatestReviews() private function getLatestReviews()
{ {

View File

@@ -2,6 +2,7 @@
namespace Modules\Admin\Http\Controllers\Admin; namespace Modules\Admin\Http\Controllers\Admin;
use Illuminate\Http\Response;
use Modules\Order\Entities\Order; use Modules\Order\Entities\Order;
class SalesAnalyticsController class SalesAnalyticsController
@@ -9,8 +10,9 @@ class SalesAnalyticsController
/** /**
* Display a listing of the resource. * Display a listing of the resource.
* *
* @param \Modules\Order\Entities\Order $order * @param Order $order
* @return \Illuminate\Http\Response *
* @return Response
*/ */
public function index(Order $order) public function index(Order $order)
{ {

View File

@@ -9,18 +9,19 @@ use Maatwebsite\Sidebar\Presentation\SidebarRenderer;
class AdminSidebarCreator class AdminSidebarCreator
{ {
/** /**
* @var \Modules\Admin\Sidebar\AdminSidebar * @var AdminSidebar
*/ */
protected $sidebar; protected $sidebar;
/** /**
* @var \Maatwebsite\Sidebar\Presentation\SidebarRenderer * @var SidebarRenderer
*/ */
protected $renderer; protected $renderer;
/** /**
* @param \Modules\Admin\Sidebar\AdminSidebar $sidebar * @param AdminSidebar $sidebar
* @param \Maatwebsite\Sidebar\Presentation\SidebarRenderer $renderer * @param SidebarRenderer $renderer
*/ */
public function __construct(AdminSidebar $sidebar, SidebarRenderer $renderer) public function __construct(AdminSidebar $sidebar, SidebarRenderer $renderer)
{ {
@@ -28,6 +29,7 @@ class AdminSidebarCreator
$this->renderer = $renderer; $this->renderer = $renderer;
} }
public function create(View $view) public function create(View $view)
{ {
$view->sidebar = $this->renderer->render($this->sidebar); $view->sidebar = $this->renderer->render($this->sidebar);

View File

@@ -4,16 +4,11 @@ namespace Modules\Admin\Providers;
use Modules\Admin\Ui\Facades\Form; use Modules\Admin\Ui\Facades\Form;
use Illuminate\Pagination\Paginator; use Illuminate\Pagination\Paginator;
use Illuminate\Support\Facades\View;
use Modules\Support\Traits\AddsAsset;
use Illuminate\Foundation\AliasLoader; use Illuminate\Foundation\AliasLoader;
use Illuminate\Support\ServiceProvider; use Illuminate\Support\ServiceProvider;
use Modules\Admin\Http\ViewComposers\AssetsComposer;
class AdminServiceProvider extends ServiceProvider class AdminServiceProvider extends ServiceProvider
{ {
use AddsAsset;
/** /**
* Bootstrap any application services. * Bootstrap any application services.
* *
@@ -21,12 +16,10 @@ class AdminServiceProvider extends ServiceProvider
*/ */
public function boot() public function boot()
{ {
View::composer('admin::layout', AssetsComposer::class);
Paginator::defaultSimpleView('admin::pagination.simple'); Paginator::defaultSimpleView('admin::pagination.simple');
$this->addAdminAssets('admin.dashboard.index', ['admin.dashboard.css', 'admin.dashboard.js']);
} }
/** /**
* Register the service provider. * Register the service provider.
* *

View File

@@ -17,7 +17,7 @@ class SidebarServiceProvider extends ServiceProvider
*/ */
public function boot(SidebarManager $manager) public function boot(SidebarManager $manager)
{ {
if (! config('app.installed')) { if (!config('app.installed')) {
return; return;
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

View File

@@ -1,4 +1,4 @@
import NProgress from 'nprogress'; import NProgress from "nprogress";
export default class { export default class {
constructor() { constructor() {
@@ -14,43 +14,52 @@ export default class {
} }
selectize() { selectize() {
let selects = $('select.selectize').removeClass('form-control custom-select-black'); let selects = $("select.selectize").removeClass(
"form-control custom-select-black"
);
let options = _.merge({ let options = _.merge(
valueField: 'id', {
labelField: 'name', valueField: "id",
searchField: 'name', labelField: "name",
delimiter: ',', searchField: "name",
persist: true, delimiter: ",",
selectOnTab: true, persist: true,
hideSelected: false, selectOnTab: true,
allowEmptyOption: true, hideSelected: true,
onItemAdd(value) { allowEmptyOption: true,
this.getItem(value)[0].innerHTML = this.getItem(value)[0].innerHTML.replace(/¦––\s/g, ''); onItemAdd(value) {
}, this.getItem(value)[0].innerHTML = this.getItem(
onInitialize() { value
for (let index in this.options) { )[0].innerHTML.replace(/¦––\s/g, "");
let label = this.options[index].name; },
let value = this.options[index].id; onInitialize() {
for (let index in this.options) {
let label = this.options[index].name;
let value = this.options[index].id;
this.$control.find(`.item[data-value="${value}"]`).html( this.$control
label.replace(/¦––\s/g, '') + .find(`.item[data-value="${value}"]`)
'<a href="javascript:void(0)" class="remove" tabindex="-1">×</a>' .html(
); label.replace(/¦––\s/g, "") +
} '<a href="javascript:void(0)" class="remove" tabindex="-1">×</a>'
);
}
},
}, },
}, ...FleetCart.selectize); ...FleetCart.selectize
);
for (let select of selects) { for (let select of selects) {
select = $(select); select = $(select);
let create = true; let create = true;
let plugins = ['remove_button', 'restore_on_backspace']; let plugins = ["remove_button", "restore_on_backspace"];
if (select.hasClass('prevent-creation')) { if (select.hasClass("prevent-creation")) {
create = false; create = false;
plugins.remove('restore_on_backspace'); plugins.remove("restore_on_backspace");
} }
select.selectize(_.merge(options, { create, plugins })); select.selectize(_.merge(options, { create, plugins }));
@@ -58,33 +67,33 @@ export default class {
} }
dateTimePicker(elements) { dateTimePicker(elements) {
elements = elements || $('.datetime-picker'); elements = elements || $(".datetime-picker");
elements = elements instanceof jQuery ? elements : $(elements); elements = elements instanceof jQuery ? elements : $(elements);
for (let el of elements) { for (let el of elements) {
$(el).flatpickr({ $(el).flatpickr({
mode: el.hasAttribute('data-range') ? 'range' : 'single', mode: el.hasAttribute("data-range") ? "range" : "single",
enableTime: el.hasAttribute('data-time'), enableTime: el.hasAttribute("data-time"),
noCalender: el.hasAttribute('data-no-calender'), noCalender: el.hasAttribute("data-no-calender"),
altInput: true, altInput: true,
}); });
} }
} }
changeAccordionTabState() { changeAccordionTabState() {
$('.accordion-box [data-toggle="tab"]').on('click', (e) => { $('.accordion-box [data-toggle="tab"]').on("click", (e) => {
if (! $(e.currentTarget).parent().hasClass('active')) { if (!$(e.currentTarget).parent().hasClass("active")) {
$('.accordion-tab li.active').removeClass('active'); $(".accordion-tab li.active").removeClass("active");
} }
}); });
} }
preventChangingCurrentTab() { preventChangingCurrentTab() {
$('[data-toggle="tab"]').on('click', (e) => { $('[data-toggle="tab"]').on("click", (e) => {
let targetElement = $(e.currentTarget); let targetElement = $(e.currentTarget);
if (targetElement.parent().hasClass('active')) { if (targetElement.parent().hasClass("active")) {
return false; return false;
} }
}); });
@@ -93,76 +102,85 @@ export default class {
removeSubmitButtonOffsetOn(tabs, tabsSelector = null) { removeSubmitButtonOffsetOn(tabs, tabsSelector = null) {
tabs = Array.isArray(tabs) ? tabs : [tabs]; tabs = Array.isArray(tabs) ? tabs : [tabs];
$(tabsSelector || '.accordion-tab li > a').on('click', (e) => { $(tabsSelector || ".accordion-tab li > a").on("click", (e) => {
if (tabs.includes(e.currentTarget.getAttribute('href'))) { if (tabs.includes(e.currentTarget.getAttribute("href"))) {
setTimeout(() => { setTimeout(() => {
$('button[type=submit]').parent().removeClass('col-md-offset-2'); $("button[type=submit]")
.parent()
.removeClass("col-md-offset-2");
}, 150); }, 150);
} else { } else {
setTimeout(() => { setTimeout(() => {
$('button[type=submit]').parent().addClass('col-md-offset-2'); $("button[type=submit]")
.parent()
.addClass("col-md-offset-2");
}, 150); }, 150);
} }
}); });
} }
buttonLoading() { buttonLoading() {
$(document).on('click', '[data-loading]', (e) => { $(document).on("click", "[data-loading]", (e) => {
let button = $(e.currentTarget); let button = $(e.currentTarget);
button.data('loading-text', button.html()) button
.addClass('btn-loading') .data("loading-text", button.html())
.button('loading'); .addClass("btn-loading")
.button("loading");
}); });
} }
stopButtonLoading(button) { stopButtonLoading(button) {
button = button instanceof jQuery ? button : $(button); button = button instanceof jQuery ? button : $(button);
button.data('loading-text', button.html()) button
.removeClass('btn-loading') .data("loading-text", button.html())
.button('reset'); .removeClass("btn-loading")
.button("reset");
} }
confirmationModal() { confirmationModal() {
let confirmationModal = $('#confirmation-modal'); let confirmationModal = $("#confirmation-modal");
$('[data-confirm]').on('click', () => { $("[data-confirm]").on("click", () => {
confirmationModal.modal('show'); confirmationModal.modal("show");
}); });
confirmationModal.find('form').on('submit', () => { confirmationModal.find("form").on("submit", () => {
confirmationModal.find('button.delete').prop('disabled', true); confirmationModal.find("button.delete").prop("disabled", true);
}); });
confirmationModal.on('hidden.bs.modal', () => { confirmationModal.on("hidden.bs.modal", () => {
confirmationModal.find('button.delete').prop('disabled', false); confirmationModal.find("button.delete").prop("disabled", false);
}); });
confirmationModal.on('shown.bs.modal', () => { confirmationModal.on("shown.bs.modal", () => {
confirmationModal.find('button.delete').focus(); confirmationModal.find("button.delete").focus();
}); });
} }
tooltip() { tooltip() {
$('[data-toggle="tooltip"]').tooltip({ trigger : 'hover' }) $('[data-toggle="tooltip"]')
.on('click', (e) => { .tooltip({ trigger: "hover" })
$(e.currentTarget).tooltip('hide'); .on("click", (e) => {
$(e.currentTarget).tooltip("hide");
}); });
} }
shortcuts() { shortcuts() {
Mousetrap.bind('f1', () => { Mousetrap.bind("f1", () => {
window.open(`http://envaysoft.com/fleetcart/docs/${FleetCart.version}`, '_blank'); window.open("https://docs.envaysoft.com/", "_blank");
}); });
Mousetrap.bind('?', () => { Mousetrap.bind("?", () => {
$('#keyboard-shortcuts-modal').modal(); $("#keyboard-shortcuts-modal").modal();
}); });
} }
nprogress() { nprogress() {
let inMobile = /iphone|ipod|android|ie|blackberry|fennec/i.test(window.navigator.userAgent); let inMobile = /iphone|ipod|android|ie|blackberry|fennec/i.test(
window.navigator.userAgent
);
if (inMobile) { if (inMobile) {
return; return;

View File

@@ -1,3 +1,6 @@
import "datatables.net";
import "datatables.net-bs";
// Initialize state holders. // Initialize state holders.
FleetCart.dataTable = { routes: {}, selected: {} }; FleetCart.dataTable = { routes: {}, selected: {} };
@@ -24,7 +27,7 @@ export default class {
{ {
serverSide: true, serverSide: true,
processing: true, processing: true,
ajax: this.route("index", { table: true }), ajax: this.route("table", { table: true }),
stateSave: true, stateSave: true,
sort: true, sort: true,
info: true, info: true,
@@ -34,9 +37,6 @@ export default class {
autoWidth: false, autoWidth: false,
pageLength: 20, pageLength: 20,
lengthMenu: [10, 20, 50, 100, 200], lengthMenu: [10, 20, 50, 100, 200],
language: {
processing: '<i class="fa fa-refresh fa-spin"></i>',
},
order: [ order: [
sortColumn.index() !== -1 ? sortColumn.index() : 1, sortColumn.index() !== -1 ? sortColumn.index() : 1,
sortColumn.data("sort") || "desc", sortColumn.data("sort") || "desc",
@@ -74,7 +74,6 @@ export default class {
}); });
}, },
stateSaveParams(settings, data) { stateSaveParams(settings, data) {
delete data.start;
delete data.search; delete data.search;
}, },
}, },
@@ -136,7 +135,6 @@ export default class {
deleted = _.flatten(deleted.concat(ids)); deleted = _.flatten(deleted.concat(ids));
this.constructor.setSelectedIds(this.selector, []); this.constructor.setSelectedIds(this.selector, []);
this.constructor.reload(this.element); this.constructor.reload(this.element);
}, },
error: (xhr) => { error: (xhr) => {
@@ -145,7 +143,6 @@ export default class {
deleted = _.flatten(deleted.concat(ids)); deleted = _.flatten(deleted.concat(ids));
this.constructor.setSelectedIds(this.selector, []); this.constructor.setSelectedIds(this.selector, []);
this.constructor.reload(this.element); this.constructor.reload(this.element);
}, },
}); });
@@ -157,10 +154,6 @@ export default class {
let url = this.route(key, { id }); let url = this.route(key, { id });
$(row).addClass("clickable-row").data("href", url); $(row).addClass("clickable-row").data("href", url);
setTimeout(() => {
$(".clickable-row td:not(:first-child)").css("cursor", "pointer");
});
} }
onRowClick(handler) { onRowClick(handler) {

View File

@@ -0,0 +1,56 @@
import Vue from "vue";
export default class {
constructor() {
this.errors = {};
}
record(errors) {
this.errors = Object.assign({}, this.errors, errors);
}
any() {
return Object.keys(this.errors).length > 0;
}
has(key) {
return this.errors.hasOwnProperty(this.normalizeKey(key));
}
get(key) {
if (this.errors[this.normalizeKey(key)]) {
return this.errors[this.normalizeKey(key)][0];
}
}
set(errors = {}) {
this.errors = Object.assign({}, this.errors, errors);
}
clear(keys) {
if (keys === undefined) {
return;
}
keys = Array.isArray(keys) ? keys : [keys];
keys.forEach((key) => {
Vue.delete(this.errors, this.normalizeKey(key));
});
}
reset() {
this.errors = {};
}
normalizeKey(key) {
let keyParts = key.split("[");
// No need to normalize the key.
if (keyParts.length === 1) {
return key;
}
return keyParts.join(".").slice(0, -1).replace(/]/g, "");
}
}

View File

@@ -0,0 +1,245 @@
$.FleetCart = {};
/* ----------------------------------
- FleetCart Options -
---------------------------------- */
$.FleetCart.options = {
animationSpeed: 300,
// Sidebar push menu toggle button selector
sidebarToggleSelector: "[data-toggle='offcanvas']",
// Activate sidebar push menu
sidebarPushMenu: true,
// BoxRefresh Plugin
enableBoxRefresh: true,
// Bootstrap.js tooltip
enableBSToppltip: true,
BSTooltipSelector: "[data-toggle='tooltip']",
// Control Sidebar Tree views
enableControlTreeView: true,
// The standard screen sizes that bootstrap uses.
screenSizes: {
xs: 480,
sm: 768,
md: 992,
lg: 1200,
},
};
/* ----------------------------------
- Implementation -
---------------------------------- */
$(function () {
// Easy access to options
var o = $.FleetCart.options;
// Set up the object
_init();
// Activate layout
$.FleetCart.layout.activate();
// Enable sidebar tree view controls
if (o.enableControlTreeView) {
$.FleetCart.tree(".sidebar");
}
// Activate sidebar push menu
if (o.sidebarPushMenu) {
$.FleetCart.pushMenu.activate(o.sidebarToggleSelector);
}
// Activate Bootstrap tooltip
if (o.enableBSToppltip) {
$("body").tooltip({
selector: o.BSTooltipSelector,
container: "body",
});
}
});
/* ----------------------------------
- Initialize the FleetCart Object -
---------------------------------- */
function _init() {
// Layout
$.FleetCart.layout = {
activate: function () {
var _this = this;
_this.fix();
$(window, ".wrapper").resize(function () {
_this.fix();
});
},
fix: function () {
var window_height = $(window).height();
$(".wrapper").css("min-height", window_height + "px");
},
};
// PushMenu
$.FleetCart.pushMenu = {
activate: function (toggleBtn) {
var screenSizes = $.FleetCart.options.screenSizes;
$(document).on("click", toggleBtn, function (e) {
e.preventDefault();
if ($(window).outerWidth() > screenSizes.md - 1) {
if ($("body").hasClass("sidebar-collapse")) {
$("body")
.removeClass("sidebar-collapse")
.trigger("expanded.pushMenu");
return;
}
$("body")
.addClass("sidebar-collapse")
.trigger("collapsed.pushMenu");
return;
}
if ($("body").hasClass("sidebar-open")) {
$("body")
.removeClass("sidebar-open")
.removeClass("sidebar-collapse")
.trigger("collapsed.pushMenu");
return;
}
$("body").addClass("sidebar-open").trigger("expanded.pushMenu");
});
$(window).on("resize", function () {
if ($(window).outerWidth() > screenSizes.md - 1) {
return;
} else {
$("body").removeClass("sidebar-collapse");
}
});
$(".content-wrapper").click(function () {
if (
$(window).width() <= screenSizes.md - 1 &&
$("body").hasClass("sidebar-open")
) {
$("body").removeClass("sidebar-open");
}
});
},
};
// Tree
$.FleetCart.tree = function (menu) {
var animationSpeed = $.FleetCart.options.animationSpeed;
$(document)
.off("click", menu + " li a")
.on("click", menu + " li a", function (e) {
var self = $(this);
var checkElement = self.next();
var activeElement = self
.closest(".sidebar-menu")
.find(".active");
if (checkElement.is(".treeview-menu")) {
self.closest(".sidebar-menu")
.find(".selected")
.removeClass("selected");
e.preventDefault();
}
if (self.parent().is(".active")) {
activeElement.toggleClass("closed");
} else {
activeElement.addClass("closed");
}
if (
checkElement.is(".treeview-menu") &&
checkElement.is(":visible") &&
!$("body").hasClass("sidebar-collapse")
) {
self.parent().removeClass("selected");
checkElement.slideUp(animationSpeed);
} else if (
checkElement.is(".treeview-menu") &&
!checkElement.is(":visible")
) {
var ul = self
.parents("ul")
.first()
.find("ul:visible")
.slideUp(animationSpeed);
self.parent().addClass("selected");
checkElement.slideDown(animationSpeed);
}
});
};
}
/* ----------------------------------
- Box Refresh Button -
---------------------------------- */
(function ($) {
$.fn.boxRefresh = function (options) {
var settings = $.extend(
{
trigger: ".refresh-btn",
source: "",
onLoadStart: function (box) {
return box;
},
onLoadDone: function (box) {
return box;
},
},
options
);
var overlay = $(
'<div class="overlay"><div class="fa fa-refresh fa-spin"></div></div>'
);
return this.each(function () {
if (settings.source === "") {
if (window.console) {
window.console.log(
"Please specify a source first - boxRefresh()"
);
}
return;
}
var box = $(this);
var rBtn = box.find(settings.trigger).first();
rBtn.on("click", function (e) {
e.preventDefault();
start(box);
box.find(".box-body").load(settings.source, function () {
done(box);
});
});
});
function start(box) {
box.append(overlay);
settings.onLoadStart.call(box);
}
function done(box) {
box.find(overlay).remove();
settings.onLoadDone.call(box);
}
};
})(jQuery);

View File

@@ -0,0 +1,16 @@
import Vue from "vue";
import VueToast from "vue-toast-notification";
Vue.use(VueToast);
export function toaster(message, options = {}) {
Vue.$toast.open({
message,
type: options.type || "default",
duration: 5000,
dismissible: true,
position: "bottom-right",
pauseOnHover: true,
...options,
});
}

View File

@@ -1,215 +1,48 @@
$.FleetCart = {}; import "bootstrap";
import "selectize";
import "flatpickr";
import "mousetrap";
import "./FleetCart";
import "./jquery.keypressAction";
/* ---------------------------------- import Admin from "./Admin";
- FleetCart Options - import Form from "./Form";
---------------------------------- */ import DataTable from "./DataTable";
$.FleetCart.options = { import {
animationSpeed: 300, trans,
// Sidebar push menu toggle button selector keypressAction,
sidebarToggleSelector: '[data-toggle=\'offcanvas\']', notify,
// Activate sidebar push menu info,
sidebarPushMenu: true, success,
// BoxRefresh Plugin warning,
enableBoxRefresh: true, error,
// Bootstrap.js tooltip } from "./functions";
enableBSToppltip: true,
BSTooltipSelector: '[data-toggle=\'tooltip\']',
// Control Sidebar Tree views
enableControlTreeView: true,
// The standard screen sizes that bootstrap uses.
screenSizes: {
xs: 480,
sm: 768,
md: 992,
lg: 1200,
},
};
/* ---------------------------------- if (
- Implementation - !route().current("admin.products.create") &&
---------------------------------- */ !route().current("admin.products.edit")
$(function () { ) {
// Easy access to options window.admin = new Admin();
var o = $.FleetCart.options;
// Set up the object
_init();
// Activate layout
$.FleetCart.layout.activate();
// Enable sidebar tree view controls
if (o.enableControlTreeView) {
$.FleetCart.tree('.sidebar');
}
// Activate sidebar push menu
if (o.sidebarPushMenu) {
$.FleetCart.pushMenu.activate(o.sidebarToggleSelector);
}
// Activate Bootstrap tooltip
if (o.enableBSToppltip) {
$('body').tooltip({
selector: o.BSTooltipSelector,
container: 'body',
});
}
});
/* ----------------------------------
- Initialize the FleetCart Object -
---------------------------------- */
function _init() {
// Layout
$.FleetCart.layout = {
activate: function () {
var _this = this;
_this.fix();
$(window, '.wrapper').resize(function () {
_this.fix();
});
},
fix: function () {
var window_height = $(window).height();
$('.wrapper').css('min-height', window_height + 'px');
}
};
// PushMenu
$.FleetCart.pushMenu = {
activate: function (toggleBtn) {
var screenSizes = $.FleetCart.options.screenSizes;
$(document).on('click', toggleBtn, function (e) {
e.preventDefault();
if ($(window).outerWidth() > (screenSizes.md - 1)) {
if ($('body').hasClass('sidebar-collapse')) {
$('body').removeClass('sidebar-collapse').trigger('expanded.pushMenu');
return;
}
$('body').addClass('sidebar-collapse').trigger('collapsed.pushMenu');
return;
}
if ($('body').hasClass('sidebar-open')) {
$('body').removeClass('sidebar-open').removeClass('sidebar-collapse').trigger('collapsed.pushMenu');
return;
}
$('body').addClass('sidebar-open').trigger('expanded.pushMenu');
});
$(window).on('resize', function () {
if ($(window).outerWidth() > (screenSizes.md - 1)) {
return;
} else {
$('body').removeClass('sidebar-collapse');
}
});
$('.content-wrapper').click(function () {
if ($(window).width() <= (screenSizes.md - 1) && $('body').hasClass('sidebar-open')) {
$('body').removeClass('sidebar-open');
}
});
}
};
// Tree
$.FleetCart.tree = function (menu) {
var animationSpeed = $.FleetCart.options.animationSpeed;
$(document).off('click', menu + ' li a')
.on('click', menu + ' li a', function (e) {
var self = $(this);
var checkElement = self.next();
var activeElement = self.closest('.sidebar-menu').find('.active');
if (checkElement.is('.treeview-menu')) {
self.closest('.sidebar-menu').find('.selected').removeClass('selected');
e.preventDefault();
}
if (self.parent().is('.active')) {
activeElement.toggleClass('closed');
} else {
activeElement.addClass('closed');
}
if ((checkElement.is('.treeview-menu')) && (checkElement.is(':visible')) && (!$('body').hasClass('sidebar-collapse'))) {
self.parent().removeClass('selected');
checkElement.slideUp(animationSpeed);
}
else if ((checkElement.is('.treeview-menu')) && (!checkElement.is(':visible'))) {
var ul = self.parents('ul').first().find('ul:visible').slideUp(animationSpeed);
self.parent().addClass('selected');
checkElement.slideDown(animationSpeed);
}
});
};
} }
/* ---------------------------------- window.form = new Form();
- Box Refresh Button - window.DataTable = DataTable;
---------------------------------- */
(function ($) {
$.fn.boxRefresh = function (options) {
var settings = $.extend({
trigger: '.refresh-btn',
source: '',
onLoadStart: function (box) {
return box;
},
onLoadDone: function (box) {
return box;
},
}, options);
var overlay = $('<div class="overlay"><div class="fa fa-refresh fa-spin"></div></div>'); window.trans = trans;
window.keypressAction = keypressAction;
window.notify = notify;
window.info = info;
window.success = success;
window.warning = warning;
window.error = error;
return this.each(function () { $.ajaxSetup({
if (settings.source === '') { headers: {
if (window.console) { Authorization: FleetCart.apiToken,
window.console.log('Please specify a source first - boxRefresh()'); "X-CSRF-TOKEN": FleetCart.csrfToken,
} },
});
return; $(document).on("preInit.dt", () => {
} $(".dataTables_length select").addClass("custom-select-black");
});
var box = $(this);
var rBtn = box.find(settings.trigger).first();
rBtn.on('click', function (e) {
e.preventDefault();
start(box);
box.find('.box-body').load(settings.source, function () {
done(box);
});
});
});
function start(box) {
box.append(overlay);
settings.onLoadStart.call(box);
}
function done(box) {
box.find(overlay).remove();
settings.onLoadDone.call(box);
}
};
})(jQuery);

View File

@@ -1,11 +1,16 @@
import Chart from 'chart.js'; import Chart from "chart.js/auto";
$(function () { $(function () {
$.ajax({ $.ajax({
type: 'GET', type: "GET",
url: route('admin.sales_analytics.index'), url: route("admin.sales_analytics.index"),
success(response) { success(response) {
let data = { labels: response.labels, sales: [], formatted: [], totalOrders: [] }; let data = {
labels: response.labels,
sales: [],
formatted: [],
totalOrders: [],
};
for (let item of response.data) { for (let item of response.data) {
data.sales.push(item.total.amount); data.sales.push(item.total.amount);
@@ -19,45 +24,56 @@ $(function () {
}); });
function initSalesAnalyticsChart(data) { function initSalesAnalyticsChart(data) {
new Chart($('.sales-analytics .chart'), { new Chart($(".sales-analytics .chart"), {
type: 'bar', type: "bar",
data: { data: {
labels: data.labels, labels: data.labels,
datasets: [{ datasets: [
data: data.sales, {
backgroundColor: [ data: data.sales,
'rgba(255, 99, 132, 0.5)', backgroundColor: [
'rgba(54, 162, 235, 0.5)', "rgba(255, 99, 132, 0.5)",
'rgba(255, 206, 86, 0.5)', "rgba(54, 162, 235, 0.5)",
'rgba(75, 192, 192, 0.5)', "rgba(255, 206, 86, 0.5)",
'rgba(153, 102, 255, 0.5)', "rgba(75, 192, 192, 0.5)",
'rgba(255, 159, 64, 0.5)', "rgba(153, 102, 255, 0.5)",
], "rgba(255, 159, 64, 0.5)",
}], ],
},
],
}, },
barThickness: 1,
options: { options: {
responsive: true,
maintainAspectRatio: false, maintainAspectRatio: false,
legend: { plugins: {
display: false, legend: false,
}, tooltip: {
tooltips: { displayColors: false,
displayColors: false, callbacks: {
callbacks: { label(item) {
label(item) { let orders = `${trans(
let orders = `${trans('admin::dashboard.sales_analytics.orders')}: ${data.totalOrders[item.index]}`; "admin::dashboard.sales_analytics.orders"
let sales = `${trans('admin::dashboard.sales_analytics.sales')}: ${data.formatted[item.index]}`; )}: ${data.totalOrders[item.dataIndex]}`;
return [orders, sales]; let sales = `${trans(
"admin::dashboard.sales_analytics.sales"
)}: ${data.formatted[item.dataIndex]}`;
return [orders, sales];
},
}, },
}, },
}, },
scales: { scales: {
yAxes: [{ y: {
beginAtZero: true,
ticks: { ticks: {
beginAtZero: true, // Include the currency symbol in the ticks
callback: function (value) {
return data.formatted[0].charAt(0) + value;
},
}, },
}], },
}, },
}, },
}); });

View File

@@ -1,4 +1,4 @@
import { ohSnap } from './ohsnap'; import { ohSnap } from "./ohsnap";
export function trans(langKey, replace = {}) { export function trans(langKey, replace = {}) {
let line = window.FleetCart.langs[langKey]; let line = window.FleetCart.langs[langKey];
@@ -16,14 +16,14 @@ export function keypressAction(actions) {
export function notify(type, message, { duration = 5000, context = document }) { export function notify(type, message, { duration = 5000, context = document }) {
let types = { let types = {
'info': 'blue', info: "blue",
'success': 'green', success: "green",
'warning': 'yellow', warning: "yellow",
'error': 'red', error: "red",
}; };
ohSnap(message, { ohSnap(message, {
'container-id': 'notification-toast', "container-id": "notification-toast",
context, context,
color: types[type], color: types[type],
duration, duration,
@@ -31,27 +31,60 @@ export function notify(type, message, { duration = 5000, context = document }) {
} }
export function info(message, duration) { export function info(message, duration) {
notify('info', message, { duration }); notify("info", message, { duration });
} }
export function success(message, duration) { export function success(message, duration) {
notify('success', message, { duration }); notify("success", message, { duration });
} }
export function warning(message, duration) { export function warning(message, duration) {
notify('warning', message, { duration }); notify("warning", message, { duration });
} }
export function error(message, duration) { export function error(message, duration) {
notify('error', message, { duration }); notify("error", message, { duration });
}
export function generateSlug(name) {
let slug = "";
// Change to lower case
const nameLower = name.toLowerCase();
// Letter "e"
slug = nameLower.replace(/e|é|è|ẽ|ẻ|ẹ|ê|ế|ề|ễ|ể|ệ/gi, "e");
// Letter "a"
slug = slug.replace(/a|á|à|ã|ả|ạ|ă|ắ|ằ|ẵ|ẳ|ặ|â|ấ|ầ|ẫ|ẩ|ậ/gi, "a");
// Letter "o"
slug = slug.replace(/o|ó|ò|õ|ỏ|ọ|ô|ố|ồ|ỗ|ổ|ộ|ơ|ớ|ờ|ỡ|ở|ợ/gi, "o");
// Letter "u"
slug = slug.replace(/u|ú|ù|ũ|ủ|ụ|ư|ứ|ừ|ữ|ử|ự/gi, "u");
// Letter "c"
slug = slug.replace(/ć|ĉ|č|ċ|ç/gi, "c");
// Letter "i"
slug = slug.replace(/î|ï|í|ī|į|ì/gi, "i");
// Letter (/, ', ")
slug = slug.replace(/\/|'|"|||,|\?|\.|;|]|\[|\+|=|\$|%|&|<|>|:/g, " ");
// Letter "d"
slug = slug.replace(/đ/gi, "d");
// Trim the last whitespace
slug = slug.replace(/\s*$/g, "");
// Change whitespace to "-"
slug = slug.replace(/\s+/g, "-");
return slug;
} }
/** /**
* @see https://stackoverflow.com/a/3955096 * @see https://stackoverflow.com/a/3955096
*/ */
if (! Array.prototype.remove) { if (!Array.prototype.remove) {
Array.prototype.remove = function () { Array.prototype.remove = function () {
let what, a = arguments, L = a.length, ax; let what,
a = arguments,
L = a.length,
ax;
while (L && this.length) { while (L && this.length) {
what = a[--L]; what = a[--L];
@@ -68,10 +101,12 @@ if (! Array.prototype.remove) {
/** /**
* @see https://stackoverflow.com/a/4673436 * @see https://stackoverflow.com/a/4673436
*/ */
if (! String.prototype.format) { if (!String.prototype.format) {
String.prototype.format = function () { String.prototype.format = function () {
return this.replace(/%(\d+)%/g, (match, number) => { return this.replace(/%(\d+)%/g, (match, number) => {
return typeof arguments[number] !== 'undefined' ? arguments[number] : match; return typeof arguments[number] !== "undefined"
? arguments[number]
: match;
}); });
}; };
} }

View File

@@ -1,43 +1,7 @@
window._ = require('lodash'); import jQuery from "jquery";
window.Sortable = require('sortablejs'); import _ from "lodash";
window.$ = window.jQuery = require('jquery'); import Sortable from "sortablejs";
require('bootstrap'); window.$ = window.jQuery = jQuery;
require('selectize'); window._ = _;
require('flatpickr'); window.Sortable = Sortable;
require('jquery-slimscroll');
require('mousetrap');
require('datatables.net');
require('datatables.net-bs');
require('./app');
require('./wysiwyg');
require('./jquery.keypressAction');
import Admin from './Admin';
import Form from './Form';
import DataTable from './DataTable';
import { trans, keypressAction, notify, info, success, warning, error } from './functions';
window.admin = new Admin();
window.form = new Form();
window.DataTable = DataTable;
window.trans = trans;
window.keypressAction = keypressAction;
window.notify = notify;
window.info = info;
window.success = success;
window.warning = warning;
window.error = error;
$.ajaxSetup({
headers: {
'Authorization': FleetCart.apiToken,
'X-CSRF-TOKEN': FleetCart.csrfToken,
},
});
$(document).on('preInit.dt', () => {
$('.dataTables_length select').addClass('custom-select-black');
});

View File

@@ -1,38 +1,46 @@
import tinyMCE from 'tinymce'; import tinyMCE from "tinymce";
tinyMCE.baseURL = `${FleetCart.baseUrl}/modules/admin/js/wysiwyg`; export default function () {
tinyMCE.baseURL = `${FleetCart.baseUrl}/build/assets/tinymce`;
tinyMCE.init({ tinyMCE.init({
selector: '.wysiwyg', selector: ".wysiwyg",
theme: 'silver', theme: "silver",
mobile: { theme: 'mobile' }, height: 350,
height: 350, menubar: false,
menubar: false, branding: false,
branding: false, image_advtab: true,
image_advtab: true, automatic_uploads: true,
automatic_uploads: true, media_alt_source: false,
media_alt_source: false, media_poster: false,
media_poster: false, relative_urls: false,
relative_urls: false, toolbar_mode: "sliding", // Possible values: floating, sliding, scrolling, wrap
directionality: FleetCart.rtl ? 'rtl' : 'ltr', directionality: FleetCart.rtl ? "rtl" : "ltr",
cache_suffix: `?v=${FleetCart.version}`, cache_suffix: `?v=${FleetCart.version}`,
plugins: 'lists, link, table, image, media, paste, autosave, autolink, wordcount, code, fullscreen', plugins:
toolbar: 'styleselect bold italic underline | bullist numlist | alignleft aligncenter alignright | outdent indent | image media link table | code fullscreen', "lists, link, table, image, media, paste, autosave, autolink,quickbars, wordcount, code, fullscreen",
toolbar:
"styleselect | bold italic underline strikethrough blockquote | bullist numlist | alignleft aligncenter alignright alignjustify | outdent indent | forecolor removeformat | table | image media link | code fullscreen",
quickbars_selection_toolbar:
"bold italic | quicklink h2 h3 blockquote quickimage quicktable",
images_upload_handler(blobInfo, success, failure) {
let formData = new FormData();
images_upload_handler(blobInfo, success, failure) { formData.append("file", blobInfo.blob(), blobInfo.filename());
let formData = new FormData();
formData.append('file', blobInfo.blob(), blobInfo.filename());
$.ajax({ $.ajax({
method: 'POST', method: "POST",
url: route('admin.media.store'), url: route("admin.media.store"),
data: formData, data: formData,
processData: false, processData: false,
contentType: false, contentType: false,
}).then((file) => { })
success(file.path); .then((file) => {
}).catch((xhr) => { success(file.path);
failure(xhr.responseJSON.message); })
}); .catch((xhr) => {
}, failure(xhr.responseJSON.message);
}); });
},
});
}

View File

@@ -1,6 +1,6 @@
.accordion-content { .accordion-content {
background: #ffffff; background: #ffffff;
padding: 20px 0px; padding: 20px 5px;
border-radius: 3px; border-radius: 3px;
box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.1); box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.1);
} }
@@ -57,7 +57,7 @@
top: 12px; top: 12px;
font-size: 20px; font-size: 20px;
line-height: 25px; line-height: 25px;
color: #000000; color: #333333;
transform: rotateX(180deg); transform: rotateX(180deg);
transition: all 200ms ease-in-out; transition: all 200ms ease-in-out;
} }
@@ -68,14 +68,13 @@
position: absolute; position: absolute;
font-family: FontAwesome; font-family: FontAwesome;
content: "\f107"; content: "\f107";
right: 10px; top: 15px;
top: 12px; right: 15px;
font-size: 20px; font-size: 20px;
line-height: 25px; line-height: 25px;
color: #000000; color: #333333;
transform: rotateX(180deg); transform: rotateX(180deg);
transition: all 200ms ease-in-out; transition: all 200ms ease-in-out;
top: 15px;
} }
.accordion-box .panel-heading [data-toggle="collapse"].collapsed:after, .accordion-box .panel-heading [data-toggle="collapse"].collapsed:after,
@@ -85,12 +84,12 @@
} }
.accordion-box .panel-heading [data-toggle="collapse"].collapsed:hover:after { .accordion-box .panel-heading [data-toggle="collapse"].collapsed:hover:after {
color: #000000; color: #333333;
} }
.accordion-box-content { .accordion-box-content {
.panel-heading [data-toggle="collapse"].collapsed:hover:after { .panel-heading [data-toggle="collapse"].collapsed:hover:after {
color: #000000; color: #333333;
} }
.panel-group .panel + .panel { .panel-group .panel + .panel {
@@ -102,19 +101,20 @@
.accordion-box { .accordion-box {
.panel-title a { .panel-title a {
position: relative; position: relative;
padding: 12px 15px; padding: 10px 15px;
display: block; display: block;
text-decoration: none; text-decoration: none;
outline: none; outline: none;
&.has-error.collapsed { &.has-error {
border-left: 3px solid #ff3366; border-left: 2px solid #ff3366;
} }
} }
.panel-body a { .panel-body a {
color: #333333; color: #333333;
display: block; display: block;
padding: 14px 15px; padding: 12px 15px;
transition: 200ms ease-in-out; transition: 200ms ease-in-out;
&:hover { &:hover {
@@ -175,7 +175,7 @@
border-bottom: none; border-bottom: none;
> li { > li {
float: none; float: none !important;
z-index: 0; z-index: 0;
> a { > a {
@@ -193,7 +193,7 @@
} }
&.has-error > a { &.has-error > a {
border-left: 3px solid #ff3366; border-left: 2px solid #ff3366;
} }
&.active > a { &.active > a {
@@ -206,9 +206,12 @@
&:focus { &:focus {
border-left: 3px solid #0068e1; border-left: 3px solid #0068e1;
border-bottom-color: #e9e9e9; border-bottom-color: #e9e9e9;
border-right: 0;
} }
} }
&.active.has-error > a {
border-left: 2px solid #ff3366;
}
} }
&.nav-tabs > li.active > a { &.nav-tabs > li.active > a {
@@ -223,19 +226,22 @@
.content-accordion { .content-accordion {
&.panel-group { &.panel-group {
margin-bottom: 15px; margin-bottom: 12px;
} }
.panel { .panel {
border: 1px solid #ebebed;
border-radius: 3px; border-radius: 3px;
border: none; box-shadow: none;
border: 1px solid #e9e9e9;
} }
.panel-heading { .panel-heading {
background: #f6f6f7; background: #f6f6f7;
padding: 0; padding: 0;
border-radius: 0; border-radius: 0;
} }
.panel-title a {
.panel-title [data-toggle="collapse"] {
display: block; display: block;
padding: 15px; padding: 15px;
@@ -244,9 +250,19 @@
&:focus { &:focus {
color: #333333; color: #333333;
} }
&.has-error {
border-left: 2px solid #ff3366;
border-radius: 2px 0 0 0;
&.collapsed {
border-radius: 2px 0 0 2px;
}
}
} }
.panel-default > .panel-heading + .panel-collapse > .panel-body { .panel-default > .panel-heading + .panel-collapse > .panel-body {
border-top-color: #e9e9e9; border-top-color: #ebebed;
} }
} }

View File

@@ -1,68 +1,69 @@
.alert { .alert {
border: none; border: none;
color: #555555; color: #555555;
font-size: 15px; font-size: 14px;
padding: 12px 15px; border-radius: 3px;
border-radius: 3px;
.close { .close {
top: 4px; right: 0;
right: 0; outline: 0;
outline: 0; opacity: 0.5;
opacity: 0.5; color: #626060;
color: #626060; text-shadow: none;
text-shadow: none; font-weight: normal;
font-weight: normal; transition: 150ms ease-in-out;
transition: 200ms ease-in-out;
&:hover { &:hover {
opacity: 0.9; opacity: 0.9;
} }
} }
.alert-text { .alert-text {
display: block; display: block;
margin: 6px 20px 0 45px; }
}
} }
.alert-icon { .alert-icon {
float: left; float: left;
width: 30px; width: 30px;
height: 30px; height: 30px;
display: table; display: table;
border-radius: 50%; border-radius: 50%;
text-align: center; text-align: center;
> i { > i {
font-size: 18px; font-size: 18px;
display: table-cell; display: table-cell;
vertical-align: middle; vertical-align: middle;
} }
} }
.alert-success { .alert-success {
background: #deedee; background: #deedee;
border-left: 3px solid #37bc9b; border-left: 3px solid #37bc9b;
.alert-icon { .alert-icon {
background: #c5e6e2; background: #c5e6e2;
> i { > i {
color: #37bc9b; color: #37bc9b;
} }
} }
} }
.alert-danger { .alert-danger {
background: #f2e8ee; background: #f2e8ee;
border-left: 3px solid #ff3366; border-left: 3px solid #ff3366;
.alert-icon { .alert-icon {
background: #f4ced5; background: #f4ced5;
> i { > i {
color: #ff3366; color: #ff3366;
} }
} }
}
.alert-dismissible {
padding-right: 20px;
} }

View File

@@ -1,17 +1,19 @@
/* buttons */ /* buttons */
.btn { .btn {
font-size: 15px; font-size: 14px;
font-weight: 500;
border: none; border: none;
border-radius: 3px; border-radius: 3px;
padding: 10px 20px; padding: 8px 16px;
transition: 200ms ease-in-out; transition: 150ms ease-in-out;
} }
.btn-default { .btn-default {
background: #f1f1f1; background: #f1f1f1;
outline: 0; outline: 0;
border-color: #dddddd; border-color: #dddddd;
&.focus, &.focus,
&:focus, &:focus,
&.active { &.active {
@@ -65,6 +67,10 @@
} }
} }
.btn-delete {
margin-left: 10px;
}
fieldset[disabled] .btn-default { fieldset[disabled] .btn-default {
&:focus, &:focus,
&.focus, &.focus,
@@ -310,9 +316,9 @@ fieldset[disabled] .btn-info {
right: 0; right: 0;
bottom: 0; bottom: 0;
margin: auto; margin: auto;
height: 16px; height: 14px;
width: 16px; width: 14px;
border: 2px solid #ffffff; border: 1px solid #ffffff;
border-radius: 100%; border-radius: 100%;
border-right-color: transparent; border-right-color: transparent;
border-top-color: transparent; border-top-color: transparent;
@@ -320,7 +326,7 @@ fieldset[disabled] .btn-info {
} }
&.btn-default:after { &.btn-default:after {
border: 2px solid #0068e1; border: 1px solid #0068e1;
border-right-color: transparent; border-right-color: transparent;
border-top-color: transparent; border-top-color: transparent;
} }
@@ -380,26 +386,35 @@ fieldset[disabled] .btn-info {
/* label */ /* label */
.label { .label {
font-weight: normal; font-weight: 500;
display: inline-block;
padding: 5px 10px; padding: 5px 10px;
} }
.label-default { .label-default {
background: #d2d6de; color: #4b5563;
background: #e5e7eb;
} }
.label-primary { .label-primary {
background: #0068e1; color: #3b82f6;
background: #dbeafe;
} }
.label-success { .label-success {
background: #37bc9b; color: #16a34a;
background: #bbf7d0;
} }
.label-danger { .label-danger {
background: #fc4b4b; color: #ef4444;
background: #fee2e2;
} }
.label-warning {
color: #c08304;
background: #fdeba3;
}
/* form error */ /* form error */
@@ -429,8 +444,8 @@ fieldset[disabled] .btn-info {
.input-group-addon { .input-group-addon {
color: #ff3366; color: #ff3366;
background-color: #f2dede; background: #f6f6f7;
border-color: #ff3366; border-color: #d9d9d9;
} }
.form-control-feedback { .form-control-feedback {
@@ -438,13 +453,15 @@ fieldset[disabled] .btn-info {
} }
} }
.help-block { .input-group-addon {
margin-bottom: 0; background: #f6f6f7;
border-color: #d9d9d9;
} }
.checkbox { .checkbox {
label { label {
font-size: 15px; font-size: 14px;
font-weight: 400 !important;
color: #333333; color: #333333;
margin-bottom: 0 !important; margin-bottom: 0 !important;
} }
@@ -458,7 +475,7 @@ fieldset[disabled] .btn-info {
&:checked + label, &:checked + label,
&:not(:checked) + label { &:not(:checked) + label {
font-family: "Roboto", sans-serif; font-family: "Inter", sans-serif;
position: relative; position: relative;
padding-left: 28px; padding-left: 28px;
cursor: pointer; cursor: pointer;
@@ -476,7 +493,7 @@ fieldset[disabled] .btn-info {
height: 17px; height: 17px;
border-radius: 3px; border-radius: 3px;
background: #e9e9e9; background: #e9e9e9;
transition: 200ms ease-in-out; transition: 150ms ease-in-out;
} }
&:checked + label:after, &:checked + label:after,
@@ -489,7 +506,7 @@ fieldset[disabled] .btn-info {
left: 2px; left: 2px;
color: #ffffff; color: #ffffff;
-webkit-text-stroke: 1px #0068e1; -webkit-text-stroke: 1px #0068e1;
transition: 200ms ease-in-out; transition: 150ms ease-in-out;
} }
&:checked + label:before { &:checked + label:before {
@@ -509,8 +526,6 @@ fieldset[disabled] .btn-info {
} }
} }
/* radio button */
.radio { .radio {
text-align: left; text-align: left;
@@ -527,7 +542,7 @@ fieldset[disabled] .btn-info {
&:checked + label, &:checked + label,
&:not(:checked) + label { &:not(:checked) + label {
font-family: "Roboto", sans-serif; font-family: "Inter", sans-serif;
position: relative; position: relative;
padding-left: 28px; padding-left: 28px;
cursor: pointer; cursor: pointer;
@@ -557,7 +572,7 @@ fieldset[disabled] .btn-info {
top: 4px; top: 4px;
left: 3px; left: 3px;
border-radius: 100%; border-radius: 100%;
transition: 200ms ease-in-out; transition: 150ms ease-in-out;
} }
&:not(:checked) + label:after { &:not(:checked) + label:after {
@@ -569,7 +584,7 @@ fieldset[disabled] .btn-info {
top: 3px; top: 3px;
left: 3px; left: 3px;
border-radius: 100%; border-radius: 100%;
transition: 200ms ease-in-out; transition: 150ms ease-in-out;
opacity: 0; opacity: 0;
transform: scale(0); transform: scale(0);
} }
@@ -589,11 +604,60 @@ fieldset[disabled] .btn-info {
margin-top: 0; margin-top: 0;
} }
.switch {
[type="checkbox"] {
display: none;
&:checked + label::before {
background: #0068e1;
}
&:checked + label::after {
left: 16px;
}
}
label {
font-weight: 400 !important;
position: relative;
min-height: 20px;
margin-bottom: 0 !important;
padding-left: 46px;
cursor: pointer;
&:before {
content: "";
position: absolute;
left: 0;
top: 4px;
height: 12px;
width: 30px;
background: #e9e9e9;
border-radius: 8px;
transition: 150ms ease-in-out;
}
&:after {
content: "";
position: absolute;
left: -4px;
top: 1px;
height: 18px;
width: 18px;
background: #ffffff;
border-radius: 16px;
box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.3);
transition: 150ms ease-in-out;
}
}
}
/* select option */ /* select option */
.custom-select-white { .custom-select-white {
appearance: none; appearance: none;
background: #f9f9f9 url('../images/arrow-white.png') no-repeat right 8px center; background: #f9f9f9 url("../images/arrow-white.png") no-repeat right 8px
center;
background-size: 10px; background-size: 10px;
line-height: normal !important; line-height: normal !important;
height: 40px; height: 40px;
@@ -604,9 +668,9 @@ fieldset[disabled] .btn-info {
.custom-select-black { .custom-select-black {
appearance: none; appearance: none;
background: #ffffff url('../images/arrow-black.png') no-repeat right 8px center; background: #ffffff url("../images/arrow-black.png") no-repeat right 8px
center;
background-size: 10px; background-size: 10px;
line-height: normal !important;
height: 40px; height: 40px;
padding: 0 30px 0 10px; padding: 0 30px 0 10px;
border-radius: 3px; border-radius: 3px;

View File

@@ -1,99 +1,125 @@
.grid { .content {
.single-grid { > .row {
position: relative; > div {
overflow: hidden; padding-right: 10px;
height: 130px; padding-left: 10px;
padding: 0 15px;
background: #ffffff;
border-radius: 3px;
box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.1);
h4 { &:first-child {
margin: 22px 0; padding-left: 15px;
}
&:last-child {
padding-right: 15px;
}
}
}
}
.grid {
> .row {
> div {
margin-bottom: 20px;
padding-left: 10px;
padding-right: 10px;
&:first-child {
padding-left: 15px;
}
&:last-child {
padding-right: 15px;
}
}
}
.single-grid {
position: relative;
display: flex;
flex-direction: column;
justify-content: space-between;
height: 130px;
padding: 25px;
border-radius: 3px;
box-shadow: 0 1px 5px rgba(0, 0, 0, 0.05);
.title {
font-weight: 600;
display: block;
color: #ffffff;
text-transform: uppercase;
} }
span { .count {
font-size: 26px; font-size: 26px;
margin-top: 3px; font-weight: 500;
color: #ffffff;
} }
i { i {
font-size: 36px; font-size: 96px;
position: absolute; position: absolute;
left: 15px; top: 10px;
bottom: 22px; right: -10px;
margin: 0; color: rgba(255, 255, 255, 0.25);
}
&.total-orders {
i {
color: rgba(0, 104, 225, 0.7);
}
} }
&.total-sales { &.total-sales {
i { background: #f24a78;
color: rgba(112, 124, 210, 0.7);
}
} }
&.total-customers { &.total-orders {
i { background: #2faedf;
color: rgba(255, 51, 102, 0.7);
}
} }
&.total-products { &.total-products {
i { background: #3e8ce8;
color: rgba(55, 188, 155, 0.7);
}
} }
}
}
.dashboard-panel { &.total-customers {
margin-top: 30px; background: #3abcbf;
padding: 0 15px; }
background: #ffffff;
overflow: hidden;
border-radius: 3px;
box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.1);
.table-responsive {
margin-bottom: 10px;
} }
} }
.grid-header { .grid-header {
overflow: auto; overflow: auto;
h4 { h5 {
margin: 15px 0; margin: 12px 0;
float: left; float: left;
}
i {
margin-right: 8px;
}
}
} }
.sales-analytics { .dashboard-panel {
background: #ffffff; margin-bottom: 20px;
margin-top: 30px; padding: 0 15px;
padding: 0 15px 15px; background: #ffffff;
border-radius: 3px; overflow: hidden;
box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.1); border-radius: 3px;
box-shadow: 0 1px 5px rgba(0, 0, 0, 0.05);
.chart { &:last-child {
height: 282px !important; margin-bottom: 0;
}
&.sales-analytics {
padding: 0 15px 15px;
.chart {
height: 282px !important;
}
}
.table-responsive {
margin-bottom: 10px;
} }
} }
.anchor-table { .anchor-table {
.table { .table {
> tbody { > tbody {
> tr { > tr {
> td { > td {
padding: 0; padding: 0;
a { a {
@@ -114,7 +140,7 @@
color: #0059bd; color: #0059bd;
} }
} }
} }
} }
} }
} }
@@ -132,21 +158,42 @@
} }
@media screen and (max-width: 1199px) { @media screen and (max-width: 1199px) {
.single-grid { .content {
&.total-customers { > .row {
margin-top: 15px; > div {
} padding-left: 15px;
padding-right: 15px;
&.total-products { &:first-child {
margin-top: 15px; margin-bottom: 20px;
}
}
}
}
.grid {
> .row {
> div {
&:nth-child(2) {
margin-bottom: 20px;
padding-right: 15px;
}
&:nth-child(3) {
padding-left: 15px;
}
}
} }
} }
} }
@media screen and (max-width: 767px) { @media screen and (max-width: 767px) {
.single-grid { .grid {
&.total-orders { > .row {
margin-top: 15px; > div {
padding-left: 15px;
padding-right: 15px;
}
} }
} }
} }

View File

@@ -1,14 +1,4 @@
.index-table { @import "datatables.net-bs/css/dataTables.bootstrap";
.label {
padding: 6px 10px;
}
> .loading-spinner {
display: table;
margin: 0 auto;
margin-bottom: 10px;
}
}
.dataTable.table { .dataTable.table {
border-bottom: 1px solid #e9e9e9; border-bottom: 1px solid #e9e9e9;
@@ -16,24 +6,20 @@
> { > {
thead > tr > th, thead > tr > th,
tfoot > tr > th { tfoot > tr > th {
font-family: 'Open Sans', sans-serif; font-family: "Inter", sans-serif;
color: #4a4a4a; color: #4a4a4a;
padding: 10px 15px; padding: 10px 15px;
border-color: #e9e9e9; border-color: #e9e9e9;
&:after {
padding: 5px;
}
} }
tbody > tr { tbody > tr {
transition: 200ms ease-in-out; transition: 150ms ease-in-out;
&:first-child > td { &:first-child > td {
border: none; border: none;
} }
&:nth-of-type(2n+1) { &:nth-of-type(2n + 1) {
background: #ffffff; background: #ffffff;
&:hover { &:hover {
@@ -41,6 +27,12 @@
} }
} }
&.clickable-row {
> td:not(:first-child) {
cursor: pointer;
}
}
> td { > td {
padding: 15px; padding: 15px;
vertical-align: middle; vertical-align: middle;
@@ -87,6 +79,32 @@
table.dataTable { table.dataTable {
margin: 5px 0 !important; margin: 5px 0 !important;
&.table-hover {
> tbody {
> tr:hover {
> * {
box-shadow: none;
}
}
}
}
&.table-striped {
> tbody {
> tr {
&.odd > * {
box-shadow: none;
}
&:hover {
> * {
box-shadow: none;
}
}
}
}
}
thead { thead {
.sorting, .sorting,
.sorting_asc, .sorting_asc,
@@ -121,23 +139,24 @@ div.dataTables_wrapper {
} }
div.dataTables_processing { div.dataTables_processing {
position: absolute;
top: 0;
left: 0;
margin: 0;
width: 100%; width: 100%;
height: 100%; height: 100%;
border: 0; margin: 0 !important;
text-align: center;
background: rgba(255, 255, 255, 0.7); background: rgba(255, 255, 255, 0.7);
border: 0;
z-index: 999; z-index: 999;
transform: translate(-50%, -50%);
.fa-spin { > div {
position: absolute; position: absolute;
top: 50%;
left: 50%; left: 50%;
color: #000; top: 50%;
font-size: 30px; margin: 0;
transform: translate(-50%, -50%);
> div {
background: #0068e1;
}
} }
} }
@@ -150,8 +169,10 @@ div.dataTables_wrapper {
} }
} }
.btn-delete { div.dataTables_processing {
margin-left: 10px; &.panel {
box-shadow: none;
}
} }
.dataTables_empty { .dataTables_empty {
@@ -191,30 +212,9 @@ div.dataTables_wrapper {
} }
div.dataTables_wrapper { div.dataTables_wrapper {
> .row > .col-sm-6 {
width: 100%;
}
div.dataTables_filter { div.dataTables_filter {
text-align: left; text-align: left;
margin-top: 15px; margin-top: 15px;
} }
} }
} }
@media screen and (max-width: 340px) {
div.dataTables_length {
display: block;
.btn-delete {
display: table;
margin: 15px 0 0 0;
}
}
div.dataTables_wrapper {
div.dataTables_length {
text-align: left;
}
}
}

View File

@@ -1,43 +1,33 @@
@import '~flatpickr/dist/flatpickr'; @import "flatpickr/dist/flatpickr";
.flatpickr-calendar { .flatpickr-calendar {
box-shadow: 0 1px 8px rgba(0, 0, 0, 0.15); box-shadow: 0 1px 8px rgba(0, 0, 0, 0.15);
}
.flatpickr-months {
.flatpickr-month {
height: 50px;
}
.flatpickr-prev-month,
.flatpickr-next-month {
top: 8px;
}
} }
.rtl { .rtl {
.flatpickr-calendar { .flatpickr-prev-month,
&:before, .flatpickr-next-month {
&:after { transform: rotateY(180deg);
right: 22px; }
left: auto;
}
}
} }
.flatpickr-month { .flatpickr-current-month {
height: 50px; height: auto;
}
.flatpickr-prev-month,
.flatpickr-next-month {
top: 10px;
}
.rtl {
.flatpickr-prev-month,
.flatpickr-next-month {
transform: rotateY(180deg);
}
}
span.flatpickr-current-month {
.cur-month {
font-family: "Open Sans", sans-serif !important;
}
} }
.flatpickr-weekdays { .flatpickr-weekdays {
span { span {
font-family: "Open Sans", sans-serif; font-family: "Inter", sans-serif;
} }
} }

File diff suppressed because it is too large Load Diff

View File

@@ -1,21 +1,20 @@
@import '~bootstrap/dist/css/bootstrap'; @import "bootstrap/dist/css/bootstrap";
@import '~font-awesome/css/font-awesome'; @import "font-awesome/css/font-awesome";
@import '~nprogress/nprogress'; @import "nprogress/nprogress";
@import '~datatables.net-bs/css/dataTables.bootstrap'; @import "flatpickr/dist/ie";
@import '~flatpickr/dist/ie'; @import "./datatables";
@import './datatables'; @import "./selectize";
@import './selectize'; @import "./toaster.scss";
@import './wysiwyg'; @import "./ohsnap";
@import './ohsnap'; @import "./flatpickr";
@import './flatpickr'; @import "./classes";
@import './classes'; @import "./tab";
@import './tab'; @import "./alert";
@import './alert'; @import "./modal";
@import './modal'; @import "./utilities";
@import './utilities'; @import "./accordion";
@import './accordion'; @import "./fleetcart";
@import './fleetcart'; @import "./panel";
@import './panel';
html { html {
direction: ltr; direction: ltr;
@@ -26,11 +25,11 @@ html {
> i { > i {
margin-right: 5px; margin-right: 5px;
} }
} }
.overflow-hidden { .overflow-hidden {
overflow: hidden; overflow: hidden;
} }
#nprogress { #nprogress {
@@ -49,13 +48,13 @@ html {
} }
.sortable-ghost { .sortable-ghost {
opacity: .2; opacity: 0.8;
} }
.btn-group.open .dropdown-toggle, .btn-group.open .dropdown-toggle,
.btn-group .dropdown-toggle:active { .btn-group .dropdown-toggle:active {
-webkit-box-shadow: none; -webkit-box-shadow: none;
box-shadow: none; box-shadow: none;
} }
.dot { .dot {
@@ -74,23 +73,33 @@ html {
} }
.options { .options {
&.table > {
thead > {
tr > {
th {
padding: 6px 8px;
}
}
}
}
tr td:first-child { tr td:first-child {
width: 34px; width: 35px;
min-width: 34px; min-width: 35px;
} }
tr td:last-child { tr td:last-child {
width: 60px; width: 60px;
} }
.drag-icon { .drag-handle {
font-size: 16px; font-size: 16px;
color: #737881; color: #737881;
cursor: move;
vertical-align: top; vertical-align: top;
margin-top: 12px; margin-top: 11px;
white-space: nowrap; white-space: nowrap;
display: inline-block; display: inline-block;
cursor: move;
i { i {
float: left; float: left;
@@ -103,7 +112,11 @@ html {
.choose-file, .choose-file,
.delete-row { .delete-row {
padding: 10px 15px; padding: 8px 14px;
}
.delete-row {
color: #4d4d4d;
} }
} }
@@ -115,7 +128,7 @@ html {
margin-left: -1px; margin-left: -1px;
padding: 0; padding: 0;
border: 1px solid #e9e9e9; border: 1px solid #e9e9e9;
transition: 200ms ease-in-out; transition: 150ms ease-in-out;
&:hover { &:hover {
cursor: pointer; cursor: pointer;
@@ -159,7 +172,8 @@ html {
background: transparent; background: transparent;
} }
&:hover, &:focus { &:hover,
&:focus {
color: #0068e1; color: #0068e1;
} }
} }
@@ -193,3 +207,7 @@ html {
.empty { .empty {
color: #626060; color: #626060;
} }
[v-cloak] {
display: none;
}

View File

@@ -23,7 +23,7 @@
outline: 0; outline: 0;
margin-top: 2px; margin-top: 2px;
-webkit-text-stroke: 1px #ffffff; -webkit-text-stroke: 1px #ffffff;
transition: 200ms ease-in-out; transition: 150ms ease-in-out;
} }
} }

View File

@@ -1,19 +1,21 @@
.panel-wrap { .panel-wrap {
.panel { .panel {
margin-bottom: 15px; margin-bottom: 15px;
box-shadow: none; box-shadow: none;
border: 1px solid #e9e9e9; border: 1px solid #e9e9e9;
border-radius: 3px; border-radius: 3px;
.panel-header { .panel-header {
font-size: 16px;
font-weight: 500;
padding: 15px; padding: 15px;
background: #f6f6f7; background: #f6f6f7;
border-bottom: 1px solid #e9e9e9; border-bottom: 1px solid #e9e9e9;
.drag-icon { .drag-handle {
font-size: 16px; font-size: 16px;
display: inline-block; display: inline-block;
margin: 2px 10px 0 0; margin: 3px 10px 0 0;
color: #737881; color: #737881;
cursor: move; cursor: move;
white-space: nowrap; white-space: nowrap;
@@ -21,19 +23,24 @@
> i { > i {
float: left; float: left;
&:last-child { &:last-child {
margin-left: 1px; margin-left: 1px;
} }
} }
} }
.btn { .btn {
color: #737881;
padding: 0; padding: 0;
background: transparent; background: transparent;
i { i {
-webkit-text-stroke: 1px #f6f6f7; -webkit-text-stroke: 1px #f6f6f7;
} }
&:hover {
color: #333333;
}
} }
} }
@@ -41,7 +48,7 @@
position: relative; position: relative;
padding: 15px; padding: 15px;
} }
.panel-image { .panel-image {
position: absolute; position: absolute;
left: 15px; left: 15px;
@@ -101,4 +108,4 @@
} }
} }
} }
} }

View File

@@ -1,4 +1,4 @@
@import '~selectize/dist/css/selectize'; @import "selectize/dist/css/selectize";
.selectize-control:not(.multi) { .selectize-control:not(.multi) {
.selectize-input .item, .selectize-input .item,
@@ -10,21 +10,34 @@
padding-right: 0 !important; padding-right: 0 !important;
} }
} }
.selectize-control { .selectize-control {
&.multi .selectize-input > div { &.multi {
padding: 4px 8px; .selectize-input {
&.has-items {
padding: 4px 4px 1px;
}
> div {
padding: 3px 8px;
}
}
} }
&.single .selectize-input { &.single .selectize-input {
cursor: text; cursor: text;
&.full {
padding: 8px 12px;
}
&:after { &:after {
content: none; content: none;
} }
> span { > span {
display: flex; display: flex;
padding: 2px 0 padding: 2px 0;
} }
input { input {
@@ -39,6 +52,7 @@
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
padding: 0;
border-left-color: #e9e9e9; border-left-color: #e9e9e9;
} }
@@ -51,22 +65,19 @@
} }
.selectize-input { .selectize-input {
padding: 4px 12px;
border-radius: 3px; border-radius: 3px;
border-color: #d9d9d9; border-color: #d9d9d9;
min-height: 40px; min-height: 36px;
vertical-align: bottom; vertical-align: bottom;
box-shadow: none !important; box-shadow: none !important;
transition: border-color ease-in-out .15s; transition: border-color ease-in-out 0.15s;
.dropdown-active { .dropdown-active {
-webkit-border-radius: 0; -webkit-border-radius: 0;
border-radius: 0; border-radius: 0;
} }
> input {
margin-top: 2px !important;
}
input { input {
font-size: 15px; font-size: 15px;
transition: 0ms !important; transition: 0ms !important;
@@ -87,7 +98,7 @@
&.focus { &.focus {
border-color: #6f8dfd; border-color: #6f8dfd;
box-shadow: 0 0 2px rgba(30, 140, 190, .8); box-shadow: 0 0 2px rgba(30, 140, 190, 0.8);
} }
.item { .item {
@@ -98,6 +109,14 @@
} }
} }
.ltr {
.selectize-input {
> input {
margin-top: 4px !important;
}
}
}
.selectize-dropdown { .selectize-dropdown {
[data-selectable] { [data-selectable] {
cursor: pointer; cursor: pointer;
@@ -108,7 +127,7 @@
} }
.selectize-dropdown-content .create strong { .selectize-dropdown-content .create strong {
font-family: "Open Sans", sans-serif; font-family: "Inter", sans-serif;
font-weight: 600; font-weight: 600;
} }
} }

View File

@@ -50,7 +50,7 @@
width: 0; width: 0;
background: #0068e1; background: #0068e1;
height: 1px; height: 1px;
transition: 200ms ease-in-out; transition: 150ms ease-in-out;
} }
} }

View File

@@ -0,0 +1,23 @@
@import "vue-toast-notification/dist/theme-sugar";
.v-toast {
padding: 20px 20px 15px;
}
.v-toast__item {
min-height: 3em;
.v-toast__icon {
display: none;
}
.v-toast__text {
padding: 0.5em 1em;
}
}
@media screen and (max-width: 991px) {
.v-toast {
padding: 15px 15px 10px !important;
}
}

View File

@@ -1,191 +1,219 @@
.p-tb-0 { .p-tb-0 {
padding-top: 0; padding-top: 0;
padding-bottom: 0; padding-bottom: 0;
} }
.p-tb-5 { .p-tb-5 {
padding-top: 5px; padding-top: 5px;
padding-bottom: 5px; padding-bottom: 5px;
} }
.p-tb-10 { .p-tb-10 {
padding-top: 10px; padding-top: 10px;
padding-bottom: 10px; padding-bottom: 10px;
} }
.p-tb-15 { .p-tb-15 {
padding-top: 15px; padding-top: 15px;
padding-bottom: 15px; padding-bottom: 15px;
} }
.p-t-0 { .p-t-0 {
padding-top: 0; padding-top: 0;
} }
.p-t-5 { .p-t-5 {
padding-top: 5px; padding-top: 5px;
} }
.p-t-10 { .p-t-10 {
padding-top: 10px; padding-top: 10px;
} }
.p-t-15 { .p-t-15 {
padding-top: 15px; padding-top: 15px;
} }
.p-b-0 { .p-b-0 {
padding-bottom: 0; padding-bottom: 0;
} }
.p-b-5 { .p-b-5 {
padding-bottom: 5px; padding-bottom: 5px;
} }
.p-b-10 { .p-b-10 {
padding-bottom: 10px; padding-bottom: 10px;
} }
.p-b-15 { .p-b-15 {
padding-bottom: 15px; padding-bottom: 15px;
} }
.p-l-0 { .p-l-0 {
padding-left: 0; padding-left: 0;
} }
.p-l-5 { .p-l-5 {
padding-left: 5px; padding-left: 5px;
} }
.p-l-10 { .p-l-10 {
padding-left: 10px; padding-left: 10px;
} }
.p-l-15 { .p-l-15 {
padding-left: 15px; padding-left: 15px;
} }
.p-r-0 { .p-r-0 {
padding-right: 0; padding-right: 0;
} }
.p-r-5 { .p-r-5 {
padding-right: 5px; padding-right: 5px;
} }
.p-r-10 { .p-r-10 {
padding-right: 10px; padding-right: 10px;
} }
.p-r-15 { .p-r-15 {
padding-right: 15px; padding-right: 15px;
} }
.m-tb-0 { .m-tb-0 {
margin-top: 0; margin-top: 0;
margin-bottom: 0; margin-bottom: 0;
} }
.m-tb-5 { .m-tb-5 {
margin-top: 5px; margin-top: 5px;
margin-bottom: 5px; margin-bottom: 5px;
} }
.m-tb-10 { .m-tb-10 {
margin-top: 10px; margin-top: 10px;
margin-bottom: 10px; margin-bottom: 10px;
} }
.m-tb-15 { .m-tb-15 {
margin-top: 15px; margin-top: 15px;
margin-bottom: 15px; margin-bottom: 15px;
} }
.m-t-0 { .m-t-0 {
margin-top: 0; margin-top: 0;
} }
.m-t-5 { .m-t-5 {
margin-top: 5px; margin-top: 5px;
} }
.m-t-10 { .m-t-10 {
margin-top: 10px; margin-top: 10px !important;
} }
.m-t-15 { .m-t-15 {
margin-top: 15px; margin-top: 15px;
} }
.m-b-0 { .m-b-0 {
margin-bottom: 0; margin-bottom: 0 !important;
} }
.m-b-5 { .m-b-5 {
margin-bottom: 5px; margin-bottom: 5px;
} }
.m-b-10 { .m-b-10 {
margin-bottom: 10px; margin-bottom: 10px !important;
} }
.m-b-15 { .m-b-15 {
margin-bottom: 15px; margin-bottom: 15px;
}
.m-b-20 {
margin-bottom: 20px !important;
} }
.m-l-0 { .m-l-0 {
margin-left: 0; margin-left: 0;
} }
.m-l-5 { .m-l-5 {
margin-left: 5px; margin-left: 5px;
} }
.m-l-10 { .m-l-10 {
margin-left: 10px; margin-left: 10px;
} }
.m-l-15 { .m-l-15 {
margin-left: 15px; margin-left: 15px;
} }
.m-r-0 { .m-r-0 {
margin-right: 0; margin-right: 0;
} }
.m-r-5 { .m-r-5 {
margin-right: 5px; margin-right: 5px;
} }
.m-r-10 { .m-r-10 {
margin-right: 10px; margin-right: 10px;
} }
.m-r-15 { .m-r-15 {
margin-right: 15px; margin-right: 15px;
} }
.no-border { .no-border {
border: 0 !important; border: 0 !important;
} }
.no-padding { .no-padding {
padding: 0 !important; padding: 0 !important;
} }
.no-margin { .no-margin {
margin: 0 !important; margin: 0 !important;
} }
.no-shadow { .no-shadow {
box-shadow: none !important; box-shadow: none !important;
} }
.cursor-auto { .cursor-auto {
cursor: auto !important; cursor: auto !important;
}
.cursor-pointer {
cursor: pointer;
}
.cursor-move {
cursor: move;
} }
.text-left { .text-left {
text-align: left; text-align: left;
}
.d-block {
display: block;
}
.d-flex {
display: flex;
}
.align-items-center {
align-items: center;
}
.justify-content-between {
justify-content: space-between;
} }

View File

@@ -13,3 +13,7 @@
} }
} }
} }
.tox-tinymce {
border-radius: 3px !important;
}

View File

@@ -1,40 +1,49 @@
<?php <?php
return [ return [
'admin' => 'Admin',
'visit_store' => 'Visit Store', 'visit_store' => 'Visit Store',
'form' => [ 'form' => [
'please_select' => 'Please Select', 'please_select' => 'Please Select',
], ],
'buttons' => [ 'buttons' => [
'save' => 'Save', 'save' => 'Save',
'delete' => 'Delete', 'delete' => 'Delete',
'cancel' => 'Cancel', 'cancel' => 'Cancel',
], ],
'table' => [ 'table' => [
'id' => 'ID', 'id' => 'ID',
'status' => 'Status', 'status' => 'Status',
'created' => 'Created', 'created' => 'Created',
'updated' => 'Updated',
'date' => 'Date', 'date' => 'Date',
], ],
'pagination' => [ 'pagination' => [
'previous' => 'Previous', 'previous' => 'Previous',
'next' => 'Next', 'next' => 'Next',
], ],
'delete' => [ 'delete' => [
'confirmation' => 'Confirmation', 'confirmation' => 'Confirmation',
'confirmation_message' => 'Are you sure you want to delete?', 'confirmation_message' => 'Are you sure you want to delete?',
], ],
'shortcuts' => [ 'shortcuts' => [
'available_shortcuts' => 'Available keyboard shortcuts on this page', 'available_shortcuts' => 'Available keyboard shortcuts on this page',
'this_menu' => 'This Menu', 'this_menu' => 'This Menu',
'back_to_index' => 'Back to :name Index', 'back_to_index' => 'Back to :name Index',
], ],
'errors' => [ 'errors' => [
'404' => '404', '404' => '404',
'404_title' => 'Oops! This page was not found', '404_title' => 'Oops! This page was not found',
'404_description' => 'The page you are looking for was not found.', '404_description' => 'The page you are looking for was not found',
'500' => '500', '500' => '500',
'500_title' => 'Oops! Something went wrong', '500_title' => 'Oops! Something went wrong',
'500_description' => 'An administrator was notified.', '500_description' => 'An administrator was notified',
], ],
]; ];

View File

@@ -1,7 +1,8 @@
<?php <?php
return [ return [
'resource_saved' => ':resource has been saved.', 'resource_created' => ':resource created',
'resource_deleted' => ':resource has been deleted.', 'resource_updated' => ':resource updated',
'permission_denied' => 'Permission Denied (required permission: ":permission").', 'resource_deleted' => ':resource deleted',
'permission_denied' => 'Permission Denied (required permission: ":permission")',
]; ];

View File

@@ -40,7 +40,7 @@
@endpush @endpush
@push('scripts') @push('scripts')
<script> <script type="module">
@if (isset($buttons) && in_array('create', $buttons)) @if (isset($buttons) && in_array('create', $buttons))
keypressAction([ keypressAction([
{ key: 'c', route: '{{ route("admin.{$resource}.create") }}'} { key: 'c', route: '{{ route("admin.{$resource}.create") }}'}
@@ -54,10 +54,10 @@
Mousetrap.bind('backspace', function () { Mousetrap.bind('backspace', function () {
$('.btn-delete').trigger('click'); $('.btn-delete').trigger('click');
}); });
@isset($resource) @isset($resource)
DataTable.setRoutes('#{{ $resource }}-table .table', { DataTable.setRoutes('#{{ $resource }}-table .table', {
index: '{{ "admin.{$resource}.index" }}', table: '{{ "admin.{$resource}.table" }}',
edit: '{{ "admin.{$resource}.edit" }}', edit: '{{ "admin.{$resource}.edit" }}',
destroy: '{{ "admin.{$resource}.destroy" }}', destroy: '{{ "admin.{$resource}.destroy" }}',
}); });

View File

@@ -1,8 +1,9 @@
<div class="col-lg-3 col-md-6 col-sm-6"> <div class="col-lg-3 col-md-6 col-sm-6">
<div class="single-grid total-customers"> <div class="single-grid total-customers">
<h4>{{ trans('admin::dashboard.total_customers') }}</h4> <i class="fa fa-users" aria-hidden="true"></i>
<i class="fa fa-users pull-left" aria-hidden="true"></i> <span class="title">{{ trans('admin::dashboard.total_customers') }}</span>
<span class="pull-right">{{ $totalCustomers }}</span>
<span class="count">{{ $totalCustomers }}</span>
</div> </div>
</div> </div>

View File

@@ -1,8 +1,9 @@
<div class="col-lg-3 col-md-6 col-sm-6"> <div class="col-lg-3 col-md-6 col-sm-6">
<div class="single-grid total-orders"> <div class="single-grid total-orders">
<h4>{{ trans('admin::dashboard.total_orders') }}</h4> <i class="fa fa-shopping-cart" aria-hidden="true"></i>
<i class="fa fa-shopping-cart pull-left" aria-hidden="true"></i> <span class="title">{{ trans('admin::dashboard.total_orders') }}</span>
<span class="pull-right">{{ $totalOrders }}</span>
<span class="count">{{ $totalOrders }}</span>
</div> </div>
</div> </div>

View File

@@ -1,8 +1,9 @@
<div class="col-lg-3 col-md-6 col-sm-6"> <div class="col-lg-3 col-md-6 col-sm-6">
<div class="single-grid total-products"> <div class="single-grid total-products">
<h4>{{ trans('admin::dashboard.total_products') }}</h4>
<i class="fa fa-cubes" aria-hidden="true"></i> <i class="fa fa-cubes" aria-hidden="true"></i>
<span class="pull-right">{{ $totalProducts }}</span>
<span class="title">{{ trans('admin::dashboard.total_products') }}</span>
<span class="count">{{ $totalProducts }}</span>
</div> </div>
</div> </div>

View File

@@ -1,8 +1,9 @@
<div class="col-lg-3 col-md-6 col-sm-6"> <div class="col-lg-3 col-md-6 col-sm-6">
<div class="single-grid total-sales"> <div class="single-grid total-sales">
<h4>{{ trans('admin::dashboard.total_sales') }}</h4> <i class="fa fa-money" aria-hidden="true"></i>
<i class="fa fa-money pull-left" aria-hidden="true"></i> <span class="title">{{ trans('admin::dashboard.total_sales') }}</span>
<span class="pull-right">{{ $totalSales->format() }}</span>
<span class="count">{{ $totalSales->format() }}</span>
</div> </div>
</div> </div>

View File

@@ -3,7 +3,7 @@
@section('title', trans('admin::dashboard.dashboard')) @section('title', trans('admin::dashboard.dashboard'))
@section('content_header') @section('content_header')
<h2 class="pull-left">{{ trans('admin::dashboard.dashboard') }}</h2> <h3 class="pull-left">{{ trans('admin::dashboard.dashboard') }}</h3>
@endsection @endsection
@section('content') @section('content')
@@ -44,3 +44,10 @@
</div> </div>
</div> </div>
@endsection @endsection
@push('globals')
@vite([
"Modules/Admin/Resources/assets/sass/dashboard.scss",
"Modules/Admin/Resources/assets/js/dashboard.js",
])
@endpush

View File

@@ -1,6 +1,6 @@
<div class="dashboard-panel"> <div class="dashboard-panel">
<div class="grid-header"> <div class="grid-header">
<h4><i class="fa fa-shopping-cart" aria-hidden="true"></i>{{ trans('admin::dashboard.latest_orders') }}</h4> <h5>{{ trans('admin::dashboard.latest_orders') }}</h5>
</div> </div>
<div class="clearfix"></div> <div class="clearfix"></div>

View File

@@ -1,6 +1,6 @@
<div class="dashboard-panel"> <div class="dashboard-panel">
<div class="grid-header"> <div class="grid-header">
<h4><i class="fa fa-comments-o" aria-hidden="true"></i>{{ trans('admin::dashboard.latest_reviews') }}</h4> <h5>{{ trans('admin::dashboard.latest_reviews') }}</h5>
</div> </div>
<div class="clearfix"></div> <div class="clearfix"></div>

View File

@@ -1,6 +1,6 @@
<div class="dashboard-panel"> <div class="dashboard-panel">
<div class="grid-header"> <div class="grid-header">
<h4><i class="fa fa-search" aria-hidden="true"></i>{{ trans('admin::dashboard.latest_search_terms') }}</h4> <h5>{{ trans('admin::dashboard.latest_search_terms') }}</h5>
</div> </div>
<div class="clearfix"></div> <div class="clearfix"></div>

View File

@@ -1,8 +1,6 @@
<div class="sales-analytics"> <div class="dashboard-panel sales-analytics">
<div class="grid-header clearfix"> <div class="grid-header clearfix">
<h4> <h5>{{ trans('admin::dashboard.sales_analytics_title') }}</h5>
<i class="fa fa-bar-chart" aria-hidden="true"></i>{{ trans('admin::dashboard.sales_analytics_title') }}
</h4>
</div> </div>
<div class="canvas"> <div class="canvas">

View File

@@ -1,60 +1,57 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="{{ locale() }}"> <html lang="{{ locale() }}">
<head>
<base href="{{ url('/') }}">
<meta charset="UTF-8">
<meta name="csrf-token" content="{{ csrf_token() }}">
<head> <title>
<base href="{{ url('/') }}"> @yield('title') - {{ setting('store_name') }} {{trans('admin::admin.admin')}}
<meta charset="UTF-8"> </title>
<meta name="csrf-token" content="{{ csrf_token() }}">
<title> <meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
@yield('title') - {{ setting('store_name') }} Admin
</title>
<meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport"> <link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css?family=Open+Sans:600|Roboto:400,500" rel="stylesheet"> @vite([
'Modules/Admin/Resources/assets/sass/main.scss',
'Modules/Admin/Resources/assets/js/main.js',
'Modules/Admin/Resources/assets/js/app.js'
])
@foreach ($assets->allCss() as $css) @stack('styles')
<link media="all" type="text/css" rel="stylesheet" href="{{ v($css) }}">
@endforeach
@stack('styles') @include('admin::partials.globals')
</head>
@include('admin::partials.globals') <body class="skin-blue sidebar-mini offcanvas clearfix {{ is_rtl() ? 'rtl' : 'ltr' }}" dir>
</head> <div class="left-side"></div>
<body class="skin-blue sidebar-mini offcanvas clearfix {{ is_rtl() ? 'rtl' : 'ltr' }}"> @include('admin::partials.sidebar')
<div class="left-side"></div>
@include('admin::partials.sidebar') <div class="wrapper">
<div class="content-wrapper">
@include('admin::partials.top_nav')
<div class="wrapper"> <section class="content-header clearfix">
<div class="content-wrapper"> @yield('content_header')
@include('admin::partials.top_nav') </section>
<section class="content-header clearfix"> <section class="content">
@yield('content_header') @include('admin::partials.notification')
</section>
<section class="content"> @yield('content')
@include('admin::partials.notification') </section>
@yield('content') <div id="notification-toast"></div>
</section> </div>
<div id="notification-toast"></div>
</div> </div>
</div>
@include('admin::partials.footer') @include('admin::partials.footer')
@include('admin::partials.confirmation_modal')
@include('admin::partials.confirmation_modal')
@foreach ($assets->allJs() as $js)
<script src="{{ v($js) }}"></script>
@endforeach
@stack('scripts')
</body>
@stack('scripts')
</body>
</html> </html>

View File

@@ -6,7 +6,7 @@
<i class="fa fa-times" aria-hidden="true"></i> <i class="fa fa-times" aria-hidden="true"></i>
</button> </button>
<h3 class="modal-title">{{ trans('admin::admin.delete.confirmation') }}</h3> <h4 class="modal-title">{{ trans('admin::admin.delete.confirmation') }}</h4>
</div> </div>
<div class="modal-body"> <div class="modal-body">

View File

@@ -4,7 +4,7 @@
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<a type="button" class="close" data-dismiss="modal" aria-label="Close">&times;</a> <a type="button" class="close" data-dismiss="modal" aria-label="Close">&times;</a>
<h4 class="modal-title">{{ trans('admin::admin.shortcuts.available_shortcuts') }}</h4> <h5 class="modal-title">{{ trans('admin::admin.shortcuts.available_shortcuts') }}</h5>
</div> </div>
<div class="modal-body"> <div class="modal-body">
@@ -21,7 +21,11 @@
<footer class="main-footer"> <footer class="main-footer">
<div class="pull-right hidden-xs"> <div class="pull-right hidden-xs">
<span>v{{ fleetcart_version() }}</span> <span>
<strong>
v{{ fleetcart_version() }}
</strong>
</span>
</div> </div>
<a href="#" data-toggle="modal" data-target="#keyboard-shortcuts-modal"> <a href="#" data-toggle="modal" data-target="#keyboard-shortcuts-modal">
@@ -30,6 +34,6 @@
<span> <span>
Copyright &copy; {{ date('Y') }} <a href="{{ route('home') }}" Copyright &copy; {{ date('Y') }} <a href="{{ route('home') }}"
target="_blank">{{ setting('store_name') }}</a> target="_blank"><strong>{{ setting('store_name') }}</strong></a>
</span> </span>
</footer> </footer>

View File

@@ -12,7 +12,6 @@
FleetCart.langs['admin::admin.buttons.delete'] = '{{ trans('admin::admin.buttons.delete') }}'; FleetCart.langs['admin::admin.buttons.delete'] = '{{ trans('admin::admin.buttons.delete') }}';
FleetCart.langs['media::media.file_manager.title'] = '{{ trans('media::media.file_manager.title') }}'; FleetCart.langs['media::media.file_manager.title'] = '{{ trans('media::media.file_manager.title') }}';
FleetCart.langs['media::messages.image_has_been_added'] = '{{ trans('media::messages.image_has_been_added') }}';
</script> </script>
@stack('globals') @stack('globals')

View File

@@ -1,23 +1,18 @@
@if (session()->has('success')) @if (session()->has('success'))
<div class="alert alert-success fade in alert-dismissable clearfix"> <div class="alert alert-success fade in alert-dismissible clearfix">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button> <button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>
<div class="alert-icon">
<i class="fa fa-check" aria-hidden="true"></i>
</div>
<span class="alert-text">{{ session('success') }}</span> <span class="alert-text">{{ session('success') }}</span>
</div> </div>
@endif @endif
@if (session()->has('error')) @if (session()->has('error'))
<div class="alert alert-danger fade in alert-dismissable clearfix"> <div class="alert alert-danger fade in alert-dismissible clearfix">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button> <button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>
<div class="alert-icon">
<i class="fa fa-exclamation" aria-hidden="true"></i>
</div>
<span class="alert-text">{{ session('error') }}</span> <span class="alert-text">{{ session('error') }}</span>
</div> </div>
@endif @endif
@stack('notifications')

View File

@@ -1,13 +1,17 @@
<nav class="navbar navbar-static-top clearfix"> <nav class="navbar navbar-static-top clearfix">
<ul class="nav navbar-nav clearfix"> <ul class="nav navbar-nav clearfix">
<li class="visit-store hidden-sm hidden-xs"> <li class="visit-store hidden-sm hidden-xs">
<a href="{{ route('home') }}"> <a href="{{ route('home') }}" target="_blank">
<i class="fa fa-desktop"></i> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<title>storefront-outline</title>
<path d="M5.06 3C4.63 3 4.22 3.14 3.84 3.42S3.24 4.06 3.14 4.5L2.11 8.91C1.86 10 2.06 10.95 2.72 11.77L3 12.05V19C3 19.5 3.2 20 3.61 20.39S4.5 21 5 21H19C19.5 21 20 20.8 20.39 20.39S21 19.5 21 19V12.05L21.28 11.77C21.94 10.95 22.14 10 21.89 8.91L20.86 4.5C20.73 4.06 20.5 3.7 20.13 3.42C19.77 3.14 19.38 3 18.94 3H5.06M18.89 4.97L19.97 9.38C20.06 9.81 19.97 10.2 19.69 10.55C19.44 10.86 19.13 11 18.75 11C18.44 11 18.17 10.9 17.95 10.66C17.73 10.43 17.61 10.16 17.58 9.84L16.97 5L18.89 4.97M5.06 5H7.03L6.42 9.84C6.3 10.63 5.91 11 5.25 11C4.84 11 4.53 10.86 4.31 10.55C4.03 10.2 3.94 9.81 4.03 9.38L5.06 5M9.05 5H11V9.7C11 10.05 10.89 10.35 10.64 10.62C10.39 10.88 10.08 11 9.7 11C9.36 11 9.07 10.88 8.84 10.59S8.5 10 8.5 9.66V9.5L9.05 5M13 5H14.95L15.5 9.5C15.58 9.92 15.5 10.27 15.21 10.57C14.95 10.87 14.61 11 14.2 11C13.89 11 13.61 10.88 13.36 10.62C13.11 10.35 13 10.05 13 9.7V5M7.45 12.05C8.08 12.67 8.86 13 9.8 13C10.64 13 11.38 12.67 12 12.05C12.69 12.67 13.45 13 14.3 13C15.17 13 15.92 12.67 16.55 12.05C17.11 12.67 17.86 13 18.8 13H19.03V19H5V13H5.25C6.16 13 6.89 12.67 7.45 12.05Z" />
</svg>
{{ trans('admin::admin.visit_store') }} {{ trans('admin::admin.visit_store') }}
</a> </a>
</li> </li>
<li class="dropdown top-nav-menu pull-right"> <li class="user dropdown top-nav-menu pull-right">
<a href="#" class="dropdown-toggle" data-toggle="dropdown"> <a href="#" class="dropdown-toggle" data-toggle="dropdown">
<i class="fa fa-user-circle-o"></i><span>{{ $currentUser->first_name }}</span> <i class="fa fa-user-circle-o"></i><span>{{ $currentUser->first_name }}</span>
</a> </a>

View File

@@ -0,0 +1,8 @@
<?php
namespace Modules\Admin\Services;
class CRUDService implements CRUDAactions
{
}

View File

@@ -5,21 +5,22 @@ namespace Modules\Admin\Sidebar;
use Maatwebsite\Sidebar\Menu; use Maatwebsite\Sidebar\Menu;
use Maatwebsite\Sidebar\Sidebar; use Maatwebsite\Sidebar\Sidebar;
use Nwidart\Modules\Facades\Module; use Nwidart\Modules\Facades\Module;
use Nwidart\Modules\Contracts\RepositoryInterface as Modules;
class AdminSidebar implements Sidebar class AdminSidebar implements Sidebar
{ {
/** /**
* The menu instance. * The menu instance.
* *
* @var \Maatwebsite\Sidebar\Menu * @var Menu
*/ */
protected $menu; protected $menu;
/** /**
* Create a new sidebar instance. * Create a new sidebar instance.
* *
* @param \Maatwebsite\Sidebar\Menu $menu * @param Menu $menu
*
* @return void * @return void
*/ */
public function __construct(Menu $menu) public function __construct(Menu $menu)
@@ -27,10 +28,11 @@ class AdminSidebar implements Sidebar
$this->menu = $menu; $this->menu = $menu;
} }
/** /**
* Get the built menu. * Get the built menu.
* *
* @return \Maatwebsite\Sidebar\Menu * @return Menu
*/ */
public function getMenu() public function getMenu()
{ {
@@ -39,6 +41,7 @@ class AdminSidebar implements Sidebar
return $this->menu; return $this->menu;
} }
/** /**
* Build the sidebar menu. * Build the sidebar menu.
* *
@@ -50,6 +53,7 @@ class AdminSidebar implements Sidebar
$this->addModuleExtenders(); $this->addModuleExtenders();
} }
/** /**
* Add active theme's sidebar extender. * Add active theme's sidebar extender.
* *
@@ -62,6 +66,24 @@ class AdminSidebar implements Sidebar
$this->add("Themes\\{$theme}\\Sidebar\\SidebarExtender"); $this->add("Themes\\{$theme}\\Sidebar\\SidebarExtender");
} }
/**
* Add sidebar extender to the menu.
*
* @param string $extender
*
* @return void
*/
private function add($extender)
{
if (class_exists($extender)) {
resolve($extender)->extend($this->menu);
}
$this->menu->add($this->menu);
}
/** /**
* Add all enabled modules sidebar extender. * Add all enabled modules sidebar extender.
* *
@@ -73,19 +95,4 @@ class AdminSidebar implements Sidebar
$this->add("Modules\\{$module->getName()}\\Sidebar\\SidebarExtender"); $this->add("Modules\\{$module->getName()}\\Sidebar\\SidebarExtender");
} }
} }
/**
* Add sidebar extender to the menu.
*
* @param string $extender
* @return void
*/
private function add($extender)
{
if (class_exists($extender)) {
resolve($extender)->extend($this->menu);
}
$this->menu->add($this->menu);
}
} }

View File

@@ -8,6 +8,7 @@ class BaseSidebarExtender
{ {
protected $auth; protected $auth;
public function __construct(Authentication $auth) public function __construct(Authentication $auth)
{ {
$this->auth = $auth; $this->auth = $auth;

View File

@@ -3,16 +3,21 @@
namespace Modules\Admin\Traits; namespace Modules\Admin\Traits;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Modules\Admin\Ui\AdminTable;
use Modules\Support\Eloquent\Model;
use Modules\Support\Search\Searchable; use Modules\Support\Search\Searchable;
use Modules\Admin\Ui\Facades\TabManager; use Modules\Admin\Ui\Facades\TabManager;
use Illuminate\Database\Eloquent\Model as EloquentModel;
trait HasCrudActions trait HasCrudActions
{ {
/** /**
* Display a listing of the resource. * Display a listing of the resource.
* *
* @param \Illuminate\Http\Request $request * @param Request $request
* @return \Illuminate\Http\Response *
* @return Response
*/ */
public function index(Request $request) public function index(Request $request)
{ {
@@ -24,32 +29,14 @@ trait HasCrudActions
->get(); ->get();
} }
if ($request->has('table')) {
return $this->getModel()->table($request);
}
return view("{$this->viewPath}.index"); return view("{$this->viewPath}.index");
} }
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
*/
public function create()
{
$data = array_merge([
'tabs' => TabManager::get($this->getModel()->getTable()),
$this->getResourceName() => $this->getModel(),
], $this->getFormData('create'));
return view("{$this->viewPath}.create", $data);
}
/** /**
* Store a newly created resource in storage. * Store a newly created resource in storage.
* *
* @return \Illuminate\Http\Response * @return Response
*/ */
public function store() public function store()
{ {
@@ -65,15 +52,42 @@ trait HasCrudActions
return $this->redirectTo($entity); return $this->redirectTo($entity);
} }
if (request()->wantsJson()) {
return response()->json(
[
'success' => true,
'message' => trans('admin::messages.resource_created', ['resource' => $this->getLabel()]),
], 200
);
}
return redirect()->route("{$this->getRoutePrefix()}.index") return redirect()->route("{$this->getRoutePrefix()}.index")
->withSuccess(trans('admin::messages.resource_saved', ['resource' => $this->getLabel()])); ->withSuccess(trans('admin::messages.resource_created', ['resource' => $this->getLabel()]));
} }
/**
* Show the form for creating a new resource.
*
* @return Response
*/
public function create()
{
$data = array_merge([
'tabs' => TabManager::get($this->getModel()->getTable()),
$this->getResourceName() => $this->getModel(),
], $this->getFormData('create'));
return view("{$this->viewPath}.create", $data);
}
/** /**
* Display the specified resource. * Display the specified resource.
* *
* @param int $id * @param int $id
* @return \Illuminate\Http\Response *
* @return Response
*/ */
public function show($id) public function show($id)
{ {
@@ -86,11 +100,13 @@ trait HasCrudActions
return view("{$this->viewPath}.show")->with($this->getResourceName(), $entity); return view("{$this->viewPath}.show")->with($this->getResourceName(), $entity);
} }
/** /**
* Show the form for editing the specified resource. * Show the form for editing the specified resource.
* *
* @param int $id * @param int $id
* @return \Illuminate\Http\Response *
* @return Response
*/ */
public function edit($id) public function edit($id)
{ {
@@ -99,14 +115,17 @@ trait HasCrudActions
$this->getResourceName() => $this->getEntity($id), $this->getResourceName() => $this->getEntity($id),
], $this->getFormData('edit', $id)); ], $this->getFormData('edit', $id));
return view("{$this->viewPath}.edit", $data); return view("{$this->viewPath}.edit", $data);
} }
/** /**
* Update the specified resource in storage. * Update the specified resource in storage.
* *
* @param int $id * @param int $id
* @return \Illuminate\Http\Response *
* @return Response
*/ */
public function update($id) public function update($id)
{ {
@@ -118,24 +137,39 @@ trait HasCrudActions
$this->getRequest('update')->all() $this->getRequest('update')->all()
); );
$entity->withoutEvents(function () use ($entity) {
$entity->touch();
});
$this->searchable($entity); $this->searchable($entity);
if (method_exists($this, 'redirectTo')) { if (method_exists($this, 'redirectTo')) {
return $this->redirectTo($entity) return $this->redirectTo($entity)
->withSuccess(trans('admin::messages.resource_saved', ['resource' => $this->getLabel()])); ->withSuccess(trans('admin::messages.resource_updated', ['resource' => $this->getLabel()]));
}
if (request()->wantsJson()) {
return response()->json(
[
'success' => true,
'message' => trans('admin::messages.resource_updated', ['resource' => $this->getLabel()]),
], 200
);
} }
return redirect()->route("{$this->getRoutePrefix()}.index") return redirect()->route("{$this->getRoutePrefix()}.index")
->withSuccess(trans('admin::messages.resource_saved', ['resource' => $this->getLabel()])); ->withSuccess(trans('admin::messages.resource_updated', ['resource' => $this->getLabel()]));
} }
/** /**
* Destroy resources by given ids. * Destroy resources by given ids.
* *
* @param string $ids * @param string $ids
*
* @return void * @return void
*/ */
public function destroy($ids) public function destroy(string $ids): void
{ {
$this->getModel() $this->getModel()
->withoutGlobalScope('active') ->withoutGlobalScope('active')
@@ -143,13 +177,153 @@ trait HasCrudActions
->delete(); ->delete();
} }
/**
* Prepare the table response for the resource.
*
* @param Request $request
*
* @return AdminTable
*/
public function table(Request $request): AdminTable
{
return $this->getModel()->table($request);
}
/**
* Get a new instance of the model.
*
* @return Model
*/
protected function getModel()
{
return new $this->model;
}
/**
* Disable search syncing for the entity.
*
* @return void
*/
protected function disableSearchSyncing(): void
{
if ($this->isSearchable()) {
$this->getModel()->disableSearchSyncing();
}
}
/**
* Determine if the entity is searchable.
*
* @return bool
*/
protected function isSearchable(): bool
{
return in_array(Searchable::class, class_uses_recursive($this->getModel()));
}
/**
* Get name of the resource.
*
* @return string
*/
protected function getResourceName(): string
{
return match (true) {
isset($this->resourceName) => $this->resourceName,
default => lcfirst(class_basename($this->model))
};
}
/**
* Get form data for the given action.
*
* @param string $action
* @param mixed ...$args
*
* @return array
*/
protected function getFormData(string $action, ...$args): array
{
return match (true) {
method_exists($this, 'formData') => $this->formData(...$args),
($action === 'create' && method_exists($this, 'createFormData')) => $this->createFormData(),
($action === 'edit' && method_exists($this, 'editFormData')) => $this->editFormData(...$args),
default => []
};
}
/**
* Get request object
*
* @param string $action
*
* @return Request
*/
protected function getRequest(string $action): Request
{
return match (true) {
!isset($this->validation) => request(),
isset($this->validation[$action]) => resolve($this->validation[$action]),
default => resolve($this->validation),
};
}
/**
* Make the given model instance searchable.
*
* @param $entity
*
* @return void
*/
protected function searchable($entity): void
{
if ($this->isSearchable()) {
$entity->searchable();
}
}
/**
* Get label of the resource.
*
* @return void
*/
protected function getLabel(): string
{
return trans($this->label);
}
/**
* Get route prefix of the resource.
*
* @return string
*/
protected function getRoutePrefix(): string
{
return match (true) {
isset($this->routePrefix) => $this->routePrefix,
default => "admin.{$this->getModel()->getTable()}"
};
}
/** /**
* Get an entity by the given id. * Get an entity by the given id.
* *
* @param int $id * @param int $id
* @return \Illuminate\Database\Eloquent\Model *
* @return EloquentModel
*/ */
protected function getEntity($id) protected function getEntity(int|string $id): EloquentModel
{ {
return $this->getModel() return $this->getModel()
->with($this->relations()) ->with($this->relations())
@@ -157,142 +331,20 @@ trait HasCrudActions
->findOrFail($id); ->findOrFail($id);
} }
/** /**
* Get the relations that should be eager loaded. * Get the relations that should be eager loaded.
* *
* @return array * @return array
*/ */
private function relations() private function relations(): array
{ {
return collect($this->with ?? [])->mapWithKeys(function ($relation) { return collect($this->with ?? [])->mapWithKeys(
return [$relation => function ($query) { function ($relation) {
return $query->withoutGlobalScope('active'); return [$relation => function ($query) {
}]; return $query->withoutGlobalScope('active');
})->all(); }];
} }
)->all();
/**
* Get form data for the given action.
*
* @param string $action
* @param mixed ...$args
* @return array
*/
protected function getFormData($action, ...$args)
{
if (method_exists($this, 'formData')) {
return $this->formData(...$args);
}
if ($action === 'create' && method_exists($this, 'createFormData')) {
return $this->createFormData();
}
if ($action === 'edit' && method_exists($this, 'editFormData')) {
return $this->editFormData(...$args);
}
return [];
}
/**
* Get name of the resource.
*
* @return string
*/
protected function getResourceName()
{
if (isset($this->resourceName)) {
return $this->resourceName;
}
return lcfirst(class_basename($this->model));
}
/**
* Get label of the resource.
*
* @return void
*/
protected function getLabel()
{
return trans($this->label);
}
/**
* Get route prefix of the resource.
*
* @return string
*/
protected function getRoutePrefix()
{
if (isset($this->routePrefix)) {
return $this->routePrefix;
}
return "admin.{$this->getModel()->getTable()}";
}
/**
* Get a new instance of the model.
*
* @return \Modules\Support\Eloquent\Model
*/
protected function getModel()
{
return new $this->model;
}
/**
* Get request object
*
* @param string $action
* @return \Illuminate\Http\Request
*/
protected function getRequest($action)
{
if (! isset($this->validation)) {
return request();
}
if (isset($this->validation[$action])) {
return resolve($this->validation[$action]);
}
return resolve($this->validation);
}
/**
* Disable search syncing for the entity.
*
* @return void
*/
protected function disableSearchSyncing()
{
if ($this->isSearchable()) {
$this->getModel()->disableSearchSyncing();
}
}
/**
* Determine if the entity is searchable.
*
* @return bool
*/
protected function isSearchable()
{
return in_array(Searchable::class, class_uses_recursive($this->getModel()));
}
/**
* Make the given model instance searchable.
*
* @return void
*/
protected function searchable($entity)
{
if ($this->isSearchable($entity)) {
$entity->searchable();
}
} }
} }

View File

@@ -2,6 +2,12 @@
namespace Modules\Admin\Ui; namespace Modules\Admin\Ui;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Yajra\DataTables\DataTables;
use Illuminate\Http\JsonResponse;
use Illuminate\Database\Eloquent\Builder;
use Yajra\DataTables\Exceptions\Exception;
use Illuminate\Contracts\Support\Responsable; use Illuminate\Contracts\Support\Responsable;
class AdminTable implements Responsable class AdminTable implements Responsable
@@ -11,28 +17,30 @@ class AdminTable implements Responsable
* *
* @var array * @var array
*/ */
protected $rawColumns = []; protected array $rawColumns = [];
/** /**
* Raw columns that will not be escaped. * Raw columns that will not be escaped.
* *
* @var array * @var array
*/ */
protected $defaultRawColumns = [ protected array $defaultRawColumns = [
'checkbox', 'thumbnail', 'status', 'created', 'checkbox', 'thumbnail', 'status', 'created', 'updated',
]; ];
/** /**
* Source of the table. * Source of the table.
* *
* @var \Illuminate\Database\Eloquent\Builder * @var Builder
*/ */
protected $source; protected $source;
/** /**
* Create a new table instance. * Create a new table instance.
* *
* @param \Illuminate\Database\Eloquent\Builder $source * @param Builder $source
*
* @return void * @return void
*/ */
public function __construct($source = null) public function __construct($source = null)
@@ -40,22 +48,38 @@ class AdminTable implements Responsable
$this->source = $source; $this->source = $source;
} }
/**
* Create an HTTP response that represents the object.
*
* @param Request $request
*
* @return Response
*/
public function toResponse($request)
{
return $this->make()->toJson();
}
/** /**
* Make table response for the resource. * Make table response for the resource.
* *
* @param mixed $source * @param mixed $source
* @return \Illuminate\Http\JsonResponse *
* @return JsonResponse
*/ */
public function make() public function make()
{ {
return $this->newTable(); return $this->newTable();
} }
/** /**
* Create a new datatable instance; * Create a new datatable instance;
* *
* @param mixed $source * @return DataTables
* @return \Yajra\DataTables\DataTables * @throws Exception
*/ */
public function newTable() public function newTable()
{ {
@@ -71,18 +95,10 @@ class AdminTable implements Responsable
->editColumn('created', function ($entity) { ->editColumn('created', function ($entity) {
return view('admin::partials.table.date')->with('date', $entity->created_at); return view('admin::partials.table.date')->with('date', $entity->created_at);
}) })
->editColumn('updated', function ($entity) {
return view('admin::partials.table.date')->with('date', $entity->updated_at);
})
->rawColumns(array_merge($this->defaultRawColumns, $this->rawColumns)) ->rawColumns(array_merge($this->defaultRawColumns, $this->rawColumns))
->removeColumn('translations'); ->removeColumn('translations');
} }
/**
* Create an HTTP response that represents the object.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function toResponse($request)
{
return $this->make()->toJson();
}
} }

View File

@@ -23,9 +23,10 @@ trait InputFields
{$attributes}" {$attributes}"
. ($disabled ? 'disabled' : '') . ($disabled ? 'disabled' : '')
. ($readonly ? 'readonly ' : '') . . ($readonly ? 'readonly ' : '') .
'>'; '>';
} }
protected function textareaField($name, $value, $class, $attributes, $options) protected function textareaField($name, $value, $class, $attributes, $options)
{ {
$readonly = array_pull($options, 'readonly', false); $readonly = array_pull($options, 'readonly', false);
@@ -38,21 +39,22 @@ trait InputFields
{$attributes}" {$attributes}"
. ($disabled ? 'disabled' : '') . ($disabled ? 'disabled' : '')
. ($readonly ? 'readonly ' : '') . . ($readonly ? 'readonly ' : '') .
">{$value}</textarea>"; ">{$value}</textarea>";
} }
protected function checkboxField($name, $value, $class, $attributes, $options, $label) protected function checkboxField($name, $value, $class, $attributes, $options, $label)
{ {
$checked = array_pull($options, 'checked', false); $checked = array_pull($options, 'checked', false);
$disabled = array_get($options, 'disabled', false); $disabled = array_get($options, 'disabled', false);
if (! is_null($value)) { if (!is_null($value)) {
$checked = $value; $checked = $value;
} }
$html = '<div class="checkbox">'; $html = '<div class="checkbox">';
if (! $disabled) { if (!$disabled) {
$html .= "<input type='hidden' value='0' name='{$name}'>"; $html .= "<input type='hidden' value='0' name='{$name}'>";
} }
@@ -63,9 +65,9 @@ trait InputFields
id='{$name}' id='{$name}'
{$attributes} {$attributes}
value='1'" value='1'"
. ($checked ? 'checked ' : '') . ($checked ? 'checked ' : '')
. ($disabled ? 'disabled' : '') . . ($disabled ? 'disabled' : '') .
'>'; '>';
$html .= "<label for='{$name}'>{$label}</label>"; $html .= "<label for='{$name}'>{$label}</label>";
$html .= '</div>'; $html .= '</div>';
@@ -73,6 +75,7 @@ trait InputFields
return $html; return $html;
} }
protected function selectField($name, $value, $class, $attributes, $options, $list) protected function selectField($name, $value, $class, $attributes, $options, $list)
{ {
$multiple = array_get($options, 'multiple', false); $multiple = array_get($options, 'multiple', false);
@@ -86,7 +89,7 @@ trait InputFields
{$attributes}" {$attributes}"
. ($disabled ? 'disabled' : '') . ($disabled ? 'disabled' : '')
. ($readonly ? 'readonly ' : '') . . ($readonly ? 'readonly ' : '') .
'>'; '>';
foreach ($list as $listValue => $listName) { foreach ($list as $listValue => $listName) {
$listValue = e($listValue); $listValue = e($listValue);
@@ -94,10 +97,10 @@ trait InputFields
if ($multiple && $value instanceof Collection) { if ($multiple && $value instanceof Collection) {
$selected = $value->where('id', $listValue)->isNotEmpty() ? 'selected' : ''; $selected = $value->where('id', $listValue)->isNotEmpty() ? 'selected' : '';
} elseif ($multiple && is_array($value)) { } else if ($multiple && is_array($value)) {
$selected = in_array($listValue, $value) ? 'selected' : ''; $selected = in_array($listValue, $value) ? 'selected' : '';
} else { } else {
$selected = (! is_null($value) && $value == $listValue) ? 'selected' : ''; $selected = (!is_null($value) && $value == $listValue) ? 'selected' : '';
} }
$html .= "<option value='{$listValue}' {$selected}>{$listName}</option>"; $html .= "<option value='{$listValue}' {$selected}>{$listName}</option>";
@@ -108,6 +111,7 @@ trait InputFields
return $html; return $html;
} }
protected function field($name, $title, $errors, $entity, $options, callable $fieldCallback, ...$args) protected function field($name, $title, $errors, $entity, $options, callable $fieldCallback, ...$args)
{ {
$value = $this->getValue($entity, $name); $value = $this->getValue($entity, $name);
@@ -139,7 +143,7 @@ trait InputFields
$html .= "<div class='col-md-{$fieldCol}'>"; $html .= "<div class='col-md-{$fieldCol}'>";
$html .= call_user_func_array($fieldCallback, $params); $html .= call_user_func_array($fieldCallback, $params);
if ($help && ! $errors->has($normalizedName)) { if ($help && !$errors->has($normalizedName)) {
$html .= "<span class='help-block'>{$help}</span>"; $html .= "<span class='help-block'>{$help}</span>";
} }
@@ -151,15 +155,31 @@ trait InputFields
return new HtmlString($html); return new HtmlString($html);
} }
private function normalizeTranslatableFieldName($name)
protected function generateHtmlAttributes($options = [])
{ {
if (starts_with($name, 'translatable[')) { $this->unsetUnnecessaryAttributes($options);
return 'translatable.' . str_between($name, 'translatable[', ']');
$attributes = '';
foreach ($options as $attr => $value) {
$attributes .= "{$attr}='{$value}' ";
} }
return $name; return $attributes;
} }
protected function unsetUnnecessaryAttributes(&$options = [])
{
foreach ($this->unnecessaryAttributes as $attribute) {
if (array_key_exists($attribute, $options)) {
unset($options[$attribute]);
}
}
}
protected function label($name, $title, $labelCol = 3, $required = false) protected function label($name, $title, $labelCol = 3, $required = false)
{ {
$html = "<label for='{$name}' class='col-md-{$labelCol} control-label text-left'>{$title}"; $html = "<label for='{$name}' class='col-md-{$labelCol} control-label text-left'>{$title}";
@@ -171,6 +191,7 @@ trait InputFields
return $html .= '</label>'; return $html .= '</label>';
} }
private function getValue($entity, $name) private function getValue($entity, $name)
{ {
if (is_object($entity) && method_exists($entity, 'translate') && $entity->isTranslationAttribute($name)) { if (is_object($entity) && method_exists($entity, 'translate') && $entity->isTranslationAttribute($name)) {
@@ -201,25 +222,13 @@ trait InputFields
return old($normalizedName, $value); return old($normalizedName, $value);
} }
protected function generateHtmlAttributes($options = [])
private function normalizeTranslatableFieldName($name)
{ {
$this->unsetUnnecessaryAttributes($options); if (starts_with($name, 'translatable[')) {
return 'translatable.' . str_between($name, 'translatable[', ']');
$attributes = '';
foreach ($options as $attr => $value) {
$attributes .= "{$attr}='{$value}' ";
} }
return $attributes; return $name;
}
protected function unsetUnnecessaryAttributes(&$options = [])
{
foreach ($this->unnecessaryAttributes as $attribute) {
if (array_key_exists($attribute, $options)) {
unset($options[$attribute]);
}
}
} }
} }

View File

@@ -10,48 +10,49 @@ class Form
protected $unnecessaryAttributes = ['disabled', 'readonly', 'checked']; protected $unnecessaryAttributes = ['disabled', 'readonly', 'checked'];
public function text($name, $title, $errors, $entity = null, $options = []) public function text($name, $title, $errors, $entity = null, $options = [])
{ {
return $this->input($name, $title, $errors, $entity, array_merge($options, ['type' => 'text'])); return $this->input($name, $title, $errors, $entity, array_merge($options, ['type' => 'text']));
} }
public function password($name, $title, $errors, $entity = null, $options = [])
{
return $this->input($name, $title, $errors, $entity, array_merge($options, ['type' => 'password']));
}
public function number($name, $title, $errors, $entity = null, $options = [])
{
return $this->input($name, $title, $errors, $entity, array_merge($options, ['type' => 'number']));
}
public function email($name, $title, $errors, $entity = null, $options = [])
{
return $this->input($name, $title, $errors, $entity, array_merge($options, ['type' => 'email']));
}
public function file($name, $title, $errors, $entity = null, $options = [])
{
return $this->input($name, $title, $errors, $entity, array_merge($options, ['type' => 'file']));
}
public function color($name, $title, $errors, $entity = null, $options = [])
{
return $this->input($name, $title, $errors, $entity, array_merge($options, ['type' => 'color']));
}
public function input($name, $title, $errors, $entity = null, $options = []) public function input($name, $title, $errors, $entity = null, $options = [])
{ {
return $this->field($name, $title, $errors, $entity, $options, [$this, 'inputField']); return $this->field($name, $title, $errors, $entity, $options, [$this, 'inputField']);
} }
public function textarea($name, $title, $errors, $entity = null, $options = [])
{
$options = array_merge(['rows' => 10, 'cols' => 10], $options);
return $this->field($name, $title, $errors, $entity, $options, [$this, 'textareaField']); public function password($name, $title, $errors, $entity = null, $options = [])
{
return $this->input($name, $title, $errors, $entity, array_merge($options, ['type' => 'password']));
} }
public function number($name, $title, $errors, $entity = null, $options = [])
{
return $this->input($name, $title, $errors, $entity, array_merge($options, ['type' => 'number']));
}
public function email($name, $title, $errors, $entity = null, $options = [])
{
return $this->input($name, $title, $errors, $entity, array_merge($options, ['type' => 'email']));
}
public function file($name, $title, $errors, $entity = null, $options = [])
{
return $this->input($name, $title, $errors, $entity, array_merge($options, ['type' => 'file']));
}
public function color($name, $title, $errors, $entity = null, $options = [])
{
return $this->input($name, $title, $errors, $entity, array_merge($options, ['type' => 'color']));
}
public function wysiwyg($name, $title, $errors, $entity = null, $options = []) public function wysiwyg($name, $title, $errors, $entity = null, $options = [])
{ {
$options['class'] = array_get($options, 'class', '') . ' wysiwyg'; $options['class'] = array_get($options, 'class', '') . ' wysiwyg';
@@ -59,11 +60,21 @@ class Form
return $this->textarea($name, $title, $errors, $entity, $options); return $this->textarea($name, $title, $errors, $entity, $options);
} }
public function textarea($name, $title, $errors, $entity = null, $options = [])
{
$options = array_merge(['rows' => 10, 'cols' => 10], $options);
return $this->field($name, $title, $errors, $entity, $options, [$this, 'textareaField']);
}
public function checkbox($name, $title, $label, $errors, $entity = null, $options = []) public function checkbox($name, $title, $label, $errors, $entity = null, $options = [])
{ {
return $this->field($name, $title, $errors, $entity, $options, [$this, 'checkboxField'], $label); return $this->field($name, $title, $errors, $entity, $options, [$this, 'checkboxField'], $label);
} }
public function select($name, $title, $errors, $list = [], $entity = null, $options = []) public function select($name, $title, $errors, $list = [], $entity = null, $options = [])
{ {
return $this->field($name, $title, $errors, $entity, $options, [$this, 'selectField'], $list); return $this->field($name, $title, $errors, $entity, $options, [$this, 'selectField'], $list);

View File

@@ -2,6 +2,7 @@
namespace Modules\Admin\Ui; namespace Modules\Admin\Ui;
use Closure;
use Illuminate\Support\ViewErrorBag; use Illuminate\Support\ViewErrorBag;
class Tab class Tab
@@ -11,21 +12,21 @@ class Tab
* *
* @var bool * @var bool
*/ */
private $active = false; public $active = false;
/** /**
* Name of the tab. * Name of the tab.
* *
* @var string * @var string
*/ */
private $name; public $name;
/** /**
* Label of the tab. * Label of the tab.
* *
* @var string * @var string
*/ */
private $label; public $label;
/** /**
* Weight of the tab. * Weight of the tab.
@@ -44,10 +45,25 @@ class Tab
/** /**
* View of the tab. * View of the tab.
* *
* @var string|\Closure * @var string|Closure
*/ */
private $view; private $view;
/**
* Prepend to the View of the tab.
*
* @var string|Closure
*/
private $prependView;
/**
* Append to the view of the tab.
*
* @var string|Closure
*/
private $appendView;
/** /**
* Data for the tab view. * Data for the tab view.
* *
@@ -58,15 +74,17 @@ class Tab
/** /**
* Error message bag. * Error message bag.
* *
* @var \Illuminate\Support\ViewErrorBag * @var ViewErrorBag
*/ */
private $errors; private $errors;
/** /**
* Create a new Tab instance. * Create a new Tab instance.
* *
* @param string $name * @param string $name
* @param string $label * @param string $label
*
* @return void * @return void
*/ */
public function __construct($name, $label) public function __construct($name, $label)
@@ -76,6 +94,7 @@ class Tab
$this->errors = request()->session()->get('errors') ?: new ViewErrorBag; $this->errors = request()->session()->get('errors') ?: new ViewErrorBag;
} }
/** /**
* Set tab as active. * Set tab as active.
* *
@@ -88,10 +107,12 @@ class Tab
return $this; return $this;
} }
/** /**
* Set weight of tab. * Set weight of tab.
* *
* @param int $weight * @param int $weight
*
* @return self * @return self
*/ */
public function weight($weight) public function weight($weight)
@@ -101,6 +122,7 @@ class Tab
return $this; return $this;
} }
/** /**
* Get weight of the tab. * Get weight of the tab.
* *
@@ -111,10 +133,12 @@ class Tab
return $this->weight; return $this->weight;
} }
/** /**
* Get nav of the tab. * Get nav of the tab.
* *
* @param array $data * @param array $data
*
* @return string * @return string
*/ */
public function getNav() public function getNav()
@@ -124,30 +148,12 @@ class Tab
</li>"; </li>";
} }
/**
* Return active class if tab is active.
*
* @return string
*/
private function activeClass()
{
return $this->active ? 'active' : '';
}
/**
* Return error class if tab fields has any error.
*
* @return string
*/
private function errorClass()
{
return $this->errors->hasAny($this->fields) ? 'has-error' : '';
}
/** /**
* Set fields of the tab. * Set fields of the tab.
* *
* @param array|string $fields * @param array|string $fields
*
* @return self * @return self
*/ */
public function fields($fields) public function fields($fields)
@@ -157,6 +163,7 @@ class Tab
return $this; return $this;
} }
/** /**
* Get fields of the tab. * Get fields of the tab.
* *
@@ -167,31 +174,61 @@ class Tab
return $this->fields; return $this->fields;
} }
/** /**
* Set view of the tab. * Set view of the tab.
* *
* @param \Closure|string $view * @param Closure|string $view
* @param array $data * @param array $data
*
* @return self * @return self
*/ */
public function view($view, $data = []) public function view($view, $data = [], $prependView = null, $appendView = null)
{ {
$this->view = $view; $this->view = $view;
$this->data = $data; $this->data = $data;
$this->prependView = $prependView;
$this->appendView = $appendView;
return $this; return $this;
} }
/** /**
* Get view of the tab. * Get view of the tab.
* *
* @param array $data * @param array $data
*
* @return string * @return string
*/ */
public function getView($data = []) public function getView($data = [])
{ {
$html = "<div class='tab-pane fade in {$this->activeClass()}' id='{$this->name}'>"; return $this->getPrependView($data) . $this->getMainView($data) . $this->getAppendView($data);
$html .= "<h3 class='tab-content-title'>{$this->label}</h3>"; }
public function getPrependView($data = [])
{
$html = '';
if (!is_null($this->prependView)) {
if (is_callable($this->prependView)) {
$html .= call_user_func($this->prependView, $this->name, $this->label, $this->activeClass());
} else {
$html .= $this->prependView;
}
} else {
$html .= "<div class='tab-pane fade in {$this->activeClass()}' id='{$this->name}'>";
$html .= "<h4 class='tab-content-title'>{$this->label}</h4>";
}
return $html;
}
public function getMainView($data = [])
{
$html = '';
if (is_callable($this->view)) { if (is_callable($this->view)) {
$html .= call_user_func($this->view, array_merge($this->data, $data)); $html .= call_user_func($this->view, array_merge($this->data, $data));
@@ -199,6 +236,46 @@ class Tab
$html .= view($this->view)->with(array_merge($this->data, $data))->render(); $html .= view($this->view)->with(array_merge($this->data, $data))->render();
} }
return $html .= '</div>'; return $html;
}
public function getAppendView($data = [])
{
$html = '';
if (!is_null($this->appendView)) {
if (is_callable($this->appendView)) {
$html .= call_user_func($this->appendView, $this->name, $this->label, $this->activeClass());
} else {
$html .= $this->appendView;
}
} else {
$html .= '</div>';
}
return $html;
}
/**
* Return active class if tab is active.
*
* @return string
*/
private function activeClass()
{
return $this->active ? 'active' : '';
}
/**
* Return error class if tab fields has any error.
*
* @return string
*/
private function errorClass()
{
return $this->errors->hasAny($this->fields) ? 'has-error' : '';
} }
} }

View File

@@ -18,11 +18,13 @@ class TabManager
*/ */
private $extends = []; private $extends = [];
/** /**
* Register a new Tabs. * Register a new Tabs.
* *
* @param string $name * @param string $name
* @param string $tabs * @param string $tabs
*
* @return void * @return void
*/ */
public function register($name, $tabs) public function register($name, $tabs)
@@ -30,28 +32,18 @@ class TabManager
$this->tabs[$name] = $tabs; $this->tabs[$name] = $tabs;
} }
/**
* Add a new Tabs extender.
*
* @param string $name
* @param string $extender
* @return void
*/
public function extend($name, $extender)
{
$this->extends[$name][] = $extender;
}
/** /**
* Get tabs for the given name. * Get tabs for the given name.
* *
* @param string $name * @param string $name
* @return \Modules\Admin\Ui\Tabs *
* @return Tabs
*/ */
public function get($name) public function get($name)
{ {
if (! array_key_exists($name, $this->tabs)) { if (!array_key_exists($name, $this->tabs)) {
return; return null;
} }
return tap(resolve($this->tabs[$name]), function (Tabs $tabs) use ($name) { return tap(resolve($this->tabs[$name]), function (Tabs $tabs) use ($name) {
@@ -61,11 +53,27 @@ class TabManager
}); });
} }
/**
* Add a new Tabs extender.
*
* @param string $name
* @param string $extender
*
* @return void
*/
public function extend($name, $extender)
{
$this->extends[$name][] = $extender;
}
/** /**
* Extend the given tabs using the given extenders. * Extend the given tabs using the given extenders.
* *
* @param \Modules\Admin\Ui\Tabs $tabs * @param Tabs $tabs
* @param array $extenders * @param array $extenders
*
* @return void * @return void
*/ */
private function extendTabs(Tabs $tabs, array $extenders) private function extendTabs(Tabs $tabs, array $extenders)

View File

@@ -36,6 +36,7 @@ abstract class Tabs
*/ */
protected $buttonOffset = true; protected $buttonOffset = true;
/** /**
* Make new tabs with groups. * Make new tabs with groups.
* *
@@ -43,23 +44,6 @@ abstract class Tabs
*/ */
abstract public function make(); abstract public function make();
/**
* Set group name.
*
* @param string $name
* @param string $title
* @return self
*/
public function group($name, $title = null)
{
$this->group = $name;
if (! is_null($title)) {
$this->groups[$name]['title'] = $title;
}
return $this;
}
/** /**
* Set current group as active group. * Set current group as active group.
@@ -73,21 +57,24 @@ abstract class Tabs
return $this; return $this;
} }
/** /**
* Add new tab. * Add new tab.
* *
* @param \Modules\Admin\Ui\Tab|null $tab * @param Tab|null $tab
*
* @return void * @return void
*/ */
public function add($tab) public function add($tab)
{ {
if (! is_null($tab)) { if (!is_null($tab)) {
$this->tabs[$this->group][] = $tab; $this->tabs[$this->group][] = $tab;
} }
return $this; return $this;
} }
/** /**
* Determine if tabs fields has any error. * Determine if tabs fields has any error.
* *
@@ -98,16 +85,70 @@ abstract class Tabs
return $this->getErrors()->hasAny($this->getTabFields()); return $this->getErrors()->hasAny($this->getTabFields());
} }
/**
* Generate navs for the tabs.
*
* @param array $data
*
* @return HtmlString
*/
public function navs($data = [])
{
return new HtmlString($this->getTabsData('nav', $data));
}
/**
* Render the tabs,
*
* @param array $data
*
* @return void
*/
public function render($data = [])
{
return view('admin::components.accordion', [
'tabs' => $this,
'name' => class_basename($this),
'groups' => $this->groups(),
'contents' => $this->contents($data),
'buttonOffset' => $this->buttonOffset,
]);
}
/**
* Set group name.
*
* @param string $name
* @param string $title
*
* @return self
*/
public function group($name, $title = null)
{
$this->group = $name;
if (!is_null($title)) {
$this->groups[$name]['title'] = $title;
}
return $this;
}
/** /**
* Get error message bag. * Get error message bag.
* *
* @return \Illuminate\Support\ViewErrorBag * @return ViewErrorBag
*/ */
protected function getErrors() protected function getErrors()
{ {
return request()->session()->get('errors') ?: new ViewErrorBag; return request()->session()->get('errors') ?: new ViewErrorBag;
} }
/** /**
* Get all tabs fields. * Get all tabs fields.
* *
@@ -120,34 +161,50 @@ abstract class Tabs
}, []); }, []);
} }
/**
* Generate navs for the tabs.
*
* @param array $data
* @return \Illuminate\Support\HtmlString
*/
public function navs($data = [])
{
return new HtmlString($this->getTabsData('nav', $data));
}
/** /**
* Render the tabs, * Get sorted tabs.
* *
* @param array $data * @return array
* @return void
*/ */
public function render($data = []) protected function getSortedTabs()
{ {
return view('admin::components.accordion', [ return collect($this->tabs[$this->group])->sortBy(function (Tab $tab) {
'tabs' => $this, return $tab->getWeight();
'name' => class_basename($this), })->all();
'groups' => $this->groups(),
'contents' => $this->contents($data),
'buttonOffset' => $this->buttonOffset,
]);
} }
/**
* Get tabs data for the given type.
*
* @param string $type
* @param array $data
*
* @return string
*
* @throws InvalidArgumentException
*/
protected function getTabsData($type, $data = [])
{
if (!array_key_exists($this->group, $this->tabs)) {
throw new InvalidArgumentException("Group [$this->group] is not registered.");
}
$html = '';
foreach ($this->getSortedTabs() as $tab) {
$method = 'get' . ucfirst($type);
if (method_exists($tab, $method)) {
$html .= call_user_func_array([$tab, $method], [$data]);
}
}
return $html;
}
/** /**
* Get all groups with it's options. * Get all groups with it's options.
* *
@@ -164,11 +221,13 @@ abstract class Tabs
return $groups; return $groups;
} }
/** /**
* Generate contents for the tabs. * Generate contents for the tabs.
* *
* @param array $data * @param array $data
* @return \Illuminate\Support\HtmlString *
* @return HtmlString
*/ */
protected function contents($data = []) protected function contents($data = [])
{ {
@@ -180,44 +239,4 @@ abstract class Tabs
return new HtmlString($contents); return new HtmlString($contents);
} }
/**
* Get tabs data for the given type.
*
* @param string $type
* @param array $data
* @return string
*
* @throws \InvalidArgumentException
*/
protected function getTabsData($type, $data = [])
{
if (! array_key_exists($this->group, $this->tabs)) {
throw new InvalidArgumentException("Group [$this->group] is not registered.");
}
$html = '';
foreach ($this->getSortedTabs() as $tab) {
$method = 'get' . ucfirst($type);
if (method_exists($tab, $method)) {
$html .= call_user_func_array([$tab, $method], [$data]);
}
}
return $html;
}
/**
* Get sorted tabs.
*
* @return array
*/
protected function getSortedTabs()
{
return collect($this->tabs[$this->group])->sortBy(function (Tab $tab) {
return $tab->getWeight();
})->all();
}
} }

View File

@@ -1,20 +1,20 @@
{ {
"name": "admin-module", "name": "admin-module",
"private": true, "private": true,
"devDependencies": { "dependencies": {
"bootstrap": "^3.3.7", "bootstrap": "^3.3.7",
"chart.js": "^2.7.3", "chart.js": "^4.4.0",
"datatables.net": "^1.10.15", "datatables.net": "^1.13.8",
"datatables.net-bs": "^1.10.15", "datatables.net-bs": "^1.13.8",
"flatpickr": "^3.0.6", "flatpickr": "^4.6.13",
"font-awesome": "^4.7.0", "font-awesome": "^4.7.0",
"jquery": "^3.2.1",
"jquery-slimscroll": "^1.3.8", "jquery-slimscroll": "^1.3.8",
"lodash": "^4.17.4", "lodash": "^4.17.21",
"mousetrap": "^1.6.1", "mousetrap": "^1.6.5",
"nprogress": "^0.2.0", "nprogress": "^0.2.0",
"selectize": "^0.12.6", "selectize": "^0.12.6",
"sortablejs": "https://github.com/RubaXa/Sortable", "sortablejs": "^1.15.0",
"tinymce": "^5.2.2" "tinymce": "5.10.7",
"vue-toast-notification": "0.6.3"
} }
} }

View File

@@ -14,6 +14,7 @@ class AttributeSetTabs extends Tabs
->add($this->general()); ->add($this->general());
} }
private function general() private function general()
{ {
return tap(new Tab('general', trans('attribute::attribute_sets.tabs.general')), function (Tab $tab) { return tap(new Tab('general', trans('attribute::attribute_sets.tabs.general')), function (Tab $tab) {

View File

@@ -3,13 +3,14 @@
namespace Modules\Attribute\Admin; namespace Modules\Attribute\Admin;
use Modules\Admin\Ui\AdminTable; use Modules\Admin\Ui\AdminTable;
use Illuminate\Http\JsonResponse;
class AttributeTable extends AdminTable class AttributeTable extends AdminTable
{ {
/** /**
* Make table response for the resource. * Make table response for the resource.
* *
* @return \Illuminate\Http\JsonResponse * @return JsonResponse
*/ */
public function make() public function make()
{ {

View File

@@ -17,6 +17,7 @@ class AttributeTabs extends Tabs
->add($this->values()); ->add($this->values());
} }
private function general() private function general()
{ {
return tap(new Tab('general', trans('attribute::admin.tabs.general')), function (Tab $tab) { return tap(new Tab('general', trans('attribute::admin.tabs.general')), function (Tab $tab) {
@@ -30,12 +31,14 @@ class AttributeTabs extends Tabs
}); });
} }
private function getAttributeSets() private function getAttributeSets()
{ {
return AttributeSet::all()->sortBy('name')->pluck('name', 'id') return AttributeSet::all()->sortBy('name')->pluck('name', 'id')
->prepend(trans('admin::admin.form.please_select'), ''); ->prepend(trans('admin::admin.form.please_select'), '');
} }
private function values() private function values()
{ {
return tap(new Tab('values', trans('attribute::admin.tabs.values')), function (Tab $tab) { return tap(new Tab('values', trans('attribute::admin.tabs.values')), function (Tab $tab) {

View File

@@ -19,6 +19,7 @@ class CreateAttributeSetsTable extends Migration
}); });
} }
/** /**
* Reverse the migrations. * Reverse the migrations.
* *

View File

@@ -24,6 +24,7 @@ class CreateAttributeSetTranslationsTable extends Migration
}); });
} }
/** /**
* Reverse the migrations. * Reverse the migrations.
* *

View File

@@ -23,6 +23,7 @@ class CreateAttributesTable extends Migration
}); });
} }
/** /**
* Reverse the migrations. * Reverse the migrations.
* *

View File

@@ -24,6 +24,7 @@ class CreateAttributeTranslationsTable extends Migration
}); });
} }
/** /**
* Reverse the migrations. * Reverse the migrations.
* *

View File

@@ -23,6 +23,7 @@ class CreateProductAttributesTable extends Migration
}); });
} }
/** /**
* Reverse the migrations. * Reverse the migrations.
* *

View File

@@ -23,6 +23,7 @@ class CreateAttributeValuesTable extends Migration
}); });
} }
/** /**
* Reverse the migrations. * Reverse the migrations.
* *

View File

@@ -24,6 +24,7 @@ class CreateAttributeValueTranslationsTable extends Migration
}); });
} }
/** /**
* Reverse the migrations. * Reverse the migrations.
* *

View File

@@ -23,6 +23,7 @@ class CreateProductAttributeValuesTable extends Migration
}); });
} }
/** /**
* Reverse the migrations. * Reverse the migrations.
* *

View File

@@ -23,6 +23,7 @@ class CreateAttributeCategoriesTable extends Migration
}); });
} }
/** /**
* Reverse the migrations. * Reverse the migrations.
* *

View File

@@ -18,6 +18,7 @@ class AddSlugColumnToAttributesTable extends Migration
}); });
} }
/** /**
* Reverse the migrations. * Reverse the migrations.
* *

View File

@@ -12,20 +12,24 @@ class Attribute extends Model
{ {
use Translatable, Sluggable; use Translatable, Sluggable;
/**
* The attributes that are translatable.
*
* @var array
*/
public $translatedAttributes = ['name'];
/** /**
* The relations to eager load on every query. * The relations to eager load on every query.
* *
* @var array * @var array
*/ */
protected $with = ['translations']; protected $with = ['translations'];
/** /**
* The attributes that are mass assignable. * The attributes that are mass assignable.
* *
* @var array * @var array
*/ */
protected $fillable = ['attribute_set_id', 'slug', 'is_filterable']; protected $fillable = ['attribute_set_id', 'slug', 'is_filterable'];
/** /**
* The attributes that should be cast to native types. * The attributes that should be cast to native types.
* *
@@ -34,14 +38,6 @@ class Attribute extends Model
protected $casts = [ protected $casts = [
'is_filterable' => 'boolean', 'is_filterable' => 'boolean',
]; ];
/**
* The attributes that are translatable.
*
* @var array
*/
public $translatedAttributes = ['name'];
/** /**
* The attribute that will be slugged. * The attribute that will be slugged.
* *
@@ -49,6 +45,7 @@ class Attribute extends Model
*/ */
protected $slugAttribute = 'name'; protected $slugAttribute = 'name';
/** /**
* Perform any actions required after the model boots. * Perform any actions required after the model boots.
* *
@@ -61,25 +58,6 @@ class Attribute extends Model
}); });
} }
public function attributeSet()
{
return $this->belongsTo(AttributeSet::class);
}
public function categories()
{
return $this->belongsToMany(Category::class, 'attribute_categories');
}
public function values()
{
return $this->hasMany(AttributeValue::class)->orderBy('position');
}
public function table()
{
return new AttributeTable($this->with('attributeSet'));
}
public function saveRelations(array $attributes) public function saveRelations(array $attributes)
{ {
@@ -87,6 +65,13 @@ class Attribute extends Model
$this->saveValues(array_get($attributes, 'values', [])); $this->saveValues(array_get($attributes, 'values', []));
} }
public function categories()
{
return $this->belongsToMany(Category::class, 'attribute_categories');
}
public function saveValues($values = []) public function saveValues($values = [])
{ {
$ids = $this->getDeleteCandidates($values); $ids = $this->getDeleteCandidates($values);
@@ -103,6 +88,25 @@ class Attribute extends Model
} }
} }
public function values()
{
return $this->hasMany(AttributeValue::class)->orderBy('position');
}
public function attributeSet()
{
return $this->belongsTo(AttributeSet::class);
}
public function table()
{
return new AttributeTable($this->with('attributeSet'));
}
private function getDeleteCandidates($values = []) private function getDeleteCandidates($values = [])
{ {
return $this->values() return $this->values()

View File

@@ -10,13 +10,18 @@ class AttributeSet extends Model
{ {
use Translatable; use Translatable;
/**
* The attributes that are translatable.
*
* @var array
*/
public $translatedAttributes = ['name'];
/** /**
* The relations to eager load on every query. * The relations to eager load on every query.
* *
* @var array * @var array
*/ */
protected $with = ['translations']; protected $with = ['translations'];
/** /**
* The attributes that aren't mass assignable. * The attributes that aren't mass assignable.
* *
@@ -24,18 +29,13 @@ class AttributeSet extends Model
*/ */
protected $guarded = []; protected $guarded = [];
/**
* The attributes that are translatable.
*
* @var array
*/
public $translatedAttributes = ['name'];
public function attributes() public function attributes()
{ {
return $this->hasMany(Attribute::class); return $this->hasMany(Attribute::class);
} }
public function table() public function table()
{ {
return new AdminTable($this->newQuery()); return new AdminTable($this->newQuery());

View File

@@ -9,24 +9,22 @@ class AttributeValue extends Model
{ {
use Translatable; use Translatable;
/**
* The attributes that are translatable.
*
* @var array
*/
public $translatedAttributes = ['value'];
/** /**
* The relations to eager load on every query. * The relations to eager load on every query.
* *
* @var array * @var array
*/ */
protected $with = ['translations']; protected $with = ['translations'];
/** /**
* The attributes that are mass assignable. * The attributes that are mass assignable.
* *
* @var array * @var array
*/ */
protected $fillable = ['position']; protected $fillable = ['position'];
/**
* The attributes that are translatable.
*
* @var array
*/
public $translatedAttributes = ['value'];
} }

View File

@@ -6,20 +6,24 @@ use Modules\Support\Eloquent\Model;
class ProductAttribute extends Model class ProductAttribute extends Model
{ {
/**
* Indicates if the model should be timestamped.
*
* @var bool
*/
public $timestamps = false;
/** /**
* The relations to eager load on every query. * The relations to eager load on every query.
* *
* @var array * @var array
*/ */
protected $with = ['attribute', 'values']; protected $with = ['attribute', 'values'];
/** /**
* The attributes that are mass assignable. * The attributes that are mass assignable.
* *
* @var array * @var array
*/ */
protected $fillable = ['attribute_id']; protected $fillable = ['attribute_id'];
/** /**
* The accessors to append to the model's array form. * The accessors to append to the model's array form.
* *
@@ -27,28 +31,25 @@ class ProductAttribute extends Model
*/ */
protected $appends = ['name']; protected $appends = ['name'];
/**
* Indicates if the model should be timestamped.
*
* @var bool
*/
public $timestamps = false;
public function attribute() public function attribute()
{ {
return $this->belongsTo(Attribute::class); return $this->belongsTo(Attribute::class);
} }
public function values() public function values()
{ {
return $this->hasMany(ProductAttributeValue::class, 'product_attribute_id'); return $this->hasMany(ProductAttributeValue::class, 'product_attribute_id');
} }
public function getNameAttribute() public function getNameAttribute()
{ {
return $this->attribute->name; return $this->attribute->name;
} }
public function getAttributeSetAttribute() public function getAttributeSetAttribute()
{ {
return $this->attribute->attributeSet->name; return $this->attribute->attributeSet->name;

View File

@@ -27,21 +27,25 @@ class ProductAttributeValue extends Model
*/ */
protected $appends = ['value']; protected $appends = ['value'];
public function exists() public function exists()
{ {
return ! is_null($this->attributeValue); return !is_null($this->attributeValue);
} }
public function attributeValue() public function attributeValue()
{ {
return $this->belongsTo(AttributeValue::class, 'attribute_value_id'); return $this->belongsTo(AttributeValue::class, 'attribute_value_id');
} }
public function getIdAttribute() public function getIdAttribute()
{ {
return $this->attributeValue->id; return $this->attributeValue->id;
} }
public function getValueAttribute() public function getValueAttribute()
{ {
return $this->attributeValue->value; return $this->attributeValue->value;

View File

@@ -15,6 +15,7 @@ class SaveAttributeRequest extends Request
*/ */
protected $availableAttributes = 'attribute::attributes.attributes'; protected $availableAttributes = 'attribute::attributes.attributes';
/** /**
* Get the validation rules that apply to the request. * Get the validation rules that apply to the request.
* *
@@ -30,18 +31,6 @@ class SaveAttributeRequest extends Request
]; ];
} }
private function getSlugRules()
{
$rules = $this->route()->getName() === 'admin.attributes.update'
? ['required']
: ['sometimes'];
$slug = Attribute::where('id', $this->id)->value('slug');
$rules[] = Rule::unique('attributes', 'slug')->ignore($slug, 'slug');
return $rules;
}
/** /**
* Get data to be validated from the request. * Get data to be validated from the request.
@@ -55,16 +44,32 @@ class SaveAttributeRequest extends Request
])->all(); ])->all();
} }
private function getSlugRules()
{
$rules = $this->route()->getName() === 'admin.attributes.update'
? ['required']
: ['sometimes'];
$slug = Attribute::where('id', $this->id)->value('slug');
$rules[] = Rule::unique('attributes', 'slug')->ignore($slug, 'slug');
return $rules;
}
/** /**
* Filter attribute values. * Filter attribute values.
* *
* @param array $values * @param array $values
*
* @return array * @return array
*/ */
private function filter($values = []) private function filter($values = [])
{ {
return array_filter($values, function ($value) { return array_filter($values, function ($value) {
return ! is_null($value['value']); return !is_null($value['value']);
}); });
} }
} }

View File

@@ -13,6 +13,7 @@ class SaveAttributeSetRequest extends Request
*/ */
protected $availableAttributes = 'attribute::attributes.attribute_sets'; protected $availableAttributes = 'attribute::attributes.attribute_sets';
/** /**
* Get the validation rules that apply to the request. * Get the validation rules that apply to the request.
* *

View File

@@ -14,6 +14,7 @@ class SaveProductAttributesRequest extends Request
*/ */
protected $availableAttributes = 'attribute::attributes.product_attributes'; protected $availableAttributes = 'attribute::attributes.product_attributes';
/** /**
* Get the validation rules that apply to the request. * Get the validation rules that apply to the request.
* *
@@ -27,6 +28,7 @@ class SaveProductAttributesRequest extends Request
]; ];
} }
/** /**
* Get data to be validated from the request. * Get data to be validated from the request.
* *
@@ -43,16 +45,18 @@ class SaveProductAttributesRequest extends Request
])->all(); ])->all();
} }
/** /**
* Filter product attributes. * Filter product attributes.
* *
* @param array $attributes * @param array $attributes
*
* @return array * @return array
*/ */
private function filter($attributes = []) private function filter($attributes = [])
{ {
return array_filter($attributes, function ($attribute) { return array_filter($attributes, function ($attribute) {
return ! is_null($attribute['attribute_id']); return !is_null($attribute['attribute_id']);
}); });
} }
} }

View File

@@ -10,7 +10,8 @@ class SaveProductAttributes
/** /**
* Handle the event. * Handle the event.
* *
* @param \Modules\Product\Entities\Product $product * @param Product $product
*
* @return void * @return void
*/ */
public function handle(Product $product) public function handle(Product $product)
@@ -19,10 +20,12 @@ class SaveProductAttributes
$this->createProductAttributes($product); $this->createProductAttributes($product);
} }
/** /**
* Delete all product attributes associated with the given product. * Delete all product attributes associated with the given product.
* *
* @param \Modules\Product\Entities\Product $product * @param Product $product
*
* @return void * @return void
*/ */
private function deleteProductAttributes(Product $product) private function deleteProductAttributes(Product $product)
@@ -30,10 +33,12 @@ class SaveProductAttributes
$product->attributes()->delete(); $product->attributes()->delete();
} }
/** /**
* Create product attributes for the given product. * Create product attributes for the given product.
* *
* @param \Modules\Product\Entities\Product $product * @param Product $product
*
* @return void * @return void
*/ */
private function createProductAttributes(Product $product) private function createProductAttributes(Product $product)
@@ -56,10 +61,12 @@ class SaveProductAttributes
$this->createProductAttributeValues($productAttributeValues); $this->createProductAttributeValues($productAttributeValues);
} }
/** /**
* Create the given product attribute values. * Create the given product attribute values.
* *
* @param array $productAttributeValues * @param array $productAttributeValues
*
* @return void * @return void
*/ */
private function createProductAttributeValues(array $productAttributeValues) private function createProductAttributeValues(array $productAttributeValues)

View File

@@ -2,17 +2,14 @@
namespace Modules\Attribute\Providers; namespace Modules\Attribute\Providers;
use Modules\Support\Traits\AddsAsset;
use Illuminate\Support\ServiceProvider; use Illuminate\Support\ServiceProvider;
use Modules\Admin\Ui\Facades\TabManager; use Modules\Admin\Ui\Facades\TabManager;
use Modules\Attribute\Admin\AttributeTabs; use Modules\Attribute\Admin\AttributeTabs;
use Modules\Attribute\Admin\AttributeSetTabs; use Modules\Attribute\Admin\AttributeSetTabs;
use Modules\Attribute\Admin\ProductTabsExtender;
class AttributeServiceProvider extends ServiceProvider class AttributeServiceProvider extends ServiceProvider
{ {
use AddsAsset;
/** /**
* Bootstrap the application services. * Bootstrap the application services.
* *
@@ -22,8 +19,5 @@ class AttributeServiceProvider extends ServiceProvider
{ {
TabManager::register('attributes', AttributeTabs::class); TabManager::register('attributes', AttributeTabs::class);
TabManager::register('attribute_sets', AttributeSetTabs::class); TabManager::register('attribute_sets', AttributeSetTabs::class);
TabManager::extend('products', ProductTabsExtender::class);
$this->addAdminAssets('admin.(attributes|products).(create|edit)', ['admin.attribute.css', 'admin.attribute.js']);
} }
} }

View File

@@ -4,7 +4,6 @@ namespace Modules\Attribute\Providers;
use Modules\Product\Entities\Product; use Modules\Product\Entities\Product;
use Modules\Attribute\Listeners\SaveProductAttributes; use Modules\Attribute\Listeners\SaveProductAttributes;
use Modules\Attribute\Http\Requests\SaveProductAttributesRequest;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider; use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
class EventServiceProvider extends ServiceProvider class EventServiceProvider extends ServiceProvider
@@ -18,10 +17,6 @@ class EventServiceProvider extends ServiceProvider
{ {
parent::boot(); parent::boot();
Product::saving(function () {
resolve(SaveProductAttributesRequest::class);
});
Product::saved(SaveProductAttributes::class); Product::saved(SaveProductAttributes::class);
} }
} }

Some files were not shown because too many files have changed in this diff Show More